diff options
Diffstat (limited to 'src/core/ndisc')
| -rw-r--r-- | src/core/ndisc/nm-lndp-ndisc.c | 35 | ||||
| -rw-r--r-- | src/core/ndisc/nm-ndisc-private.h | 2 | ||||
| -rw-r--r-- | src/core/ndisc/nm-ndisc.c | 152 | ||||
| -rw-r--r-- | src/core/ndisc/nm-ndisc.h | 27 |
4 files changed, 178 insertions, 38 deletions
diff --git a/src/core/ndisc/nm-lndp-ndisc.c b/src/core/ndisc/nm-lndp-ndisc.c index c19dcc91..69213da6 100644 --- a/src/core/ndisc/nm-lndp-ndisc.c +++ b/src/core/ndisc/nm-lndp-ndisc.c @@ -401,6 +401,30 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) } } +#if HAVE_CLAT + /* PREF64 */ + ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_PREF64) { + 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.plen, 96, 64, 56, 48, 40, 32)); + + if (nm_ndisc_add_pref64(ndisc, &pref64, now_msec)) { + changed |= NM_NDISC_CONFIG_PREF64; + } + } +#endif + nm_ndisc_ra_received(ndisc, now_msec, changed); return 0; } @@ -463,7 +487,8 @@ send_ra(NMNDisc *ndisc, GError **error) struct in6_addr *addr; struct ndp_msg *msg; guint i; - nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(0, FALSE); + nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(0, FALSE); + gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); errsv = ndp_msg_new(&msg, NDP_MSG_RA); if (errsv) { @@ -507,13 +532,9 @@ send_ra(NMNDisc *ndisc, GError **error) prefix->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; prefix->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; prefix->nd_opt_pi_valid_time = - htonl(_nm_ndisc_lifetime_from_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, - address->expiry_msec, - TRUE)); + htonl(_nm_ndisc_lifetime_from_expiry(now_msec, address->expiry_msec, TRUE)); prefix->nd_opt_pi_preferred_time = - htonl(_nm_ndisc_lifetime_from_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, - address->expiry_preferred_msec, - TRUE)); + htonl(_nm_ndisc_lifetime_from_expiry(now_msec, address->expiry_preferred_msec, TRUE)); prefix->nd_opt_pi_prefix.s6_addr32[0] = address->address.s6_addr32[0]; prefix->nd_opt_pi_prefix.s6_addr32[1] = address->address.s6_addr32[1]; prefix->nd_opt_pi_prefix.s6_addr32[2] = 0; diff --git a/src/core/ndisc/nm-ndisc-private.h b/src/core/ndisc/nm-ndisc-private.h index 1479e566..90ec032d 100644 --- a/src/core/ndisc/nm-ndisc-private.h +++ b/src/core/ndisc/nm-ndisc-private.h @@ -14,6 +14,7 @@ struct _NMNDiscDataInternal { NMNDiscData public; GArray *gateways; GArray *addresses; + GArray *pref64; GArray *routes; GArray *dns_servers; GArray *dns_domains; @@ -28,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); diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index 1a2bf480..2056b5f8 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -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 /*****************************************************************************/ @@ -109,7 +110,8 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx, int ifindex, const NMNDiscData *rdata, NMSettingIP6ConfigPrivacy ip6_privacy, - NMUtilsIPv6IfaceId *token) + NMUtilsIPv6IfaceId *token, + const char *network_id) { nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; guint32 ifa_flags; @@ -211,13 +213,21 @@ 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_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); + } + nm_l3_config_data_set_ndisc_hop_limit(l3cd, rdata->hop_limit); nm_l3_config_data_set_ndisc_reachable_time_msec(l3cd, rdata->reachable_time_ms); nm_l3_config_data_set_ndisc_retrans_timer_msec(l3cd, rdata->retrans_timer_ms); - nm_l3_config_data_set_ip6_mtu(l3cd, rdata->mtu); + nm_l3_config_data_set_ip6_mtu_ra(l3cd, rdata->mtu); if (token) nm_l3_config_data_set_ip6_token(l3cd, *token); + if (network_id) + nm_l3_config_data_set_network_id(l3cd, network_id); return g_steal_pointer(&l3cd); } @@ -416,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 @@ -437,7 +448,8 @@ nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed) nm_l3cfg_get_ifindex(priv->config.l3cfg), rdata, priv->config.ip6_privacy, - priv->iid_is_token ? &priv->iid : NULL); + priv->iid_is_token ? &priv->iid : NULL, + priv->config.network_id); l3cd = nm_l3_config_data_seal(l3cd); if (!nm_l3_config_data_equal(priv->l3cd, l3cd)) @@ -761,6 +773,59 @@ 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) +{ + 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) { NMNDiscPrivate *priv; @@ -1059,7 +1124,8 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) const NMPObject *obj; guint len; guint i; - gint32 fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000; + gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + gint32 now = now_msec / 1000; nm_assert(NM_IS_NDISC(ndisc)); nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER); @@ -1082,16 +1148,15 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) lifetime = nmp_utils_lifetime_get(addr->timestamp, addr->lifetime, addr->preferred, - &fake_now, + &now, &preferred); if (!lifetime) continue; a = (NMNDiscAddress) { - .address = addr->address, - .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, lifetime), - .expiry_preferred_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, preferred), + .address = addr->address, + .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, lifetime), + .expiry_preferred_msec = _nm_ndisc_lifetime_to_expiry(now_msec, preferred), }; if (nm_ndisc_add_address(ndisc, &a, 0, FALSE)) @@ -1111,8 +1176,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) n = (NMNDiscDNSServer) { .address = a.addr6, - .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, - NM_NDISC_ROUTER_LIFETIME), + .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, NM_NDISC_ROUTER_LIFETIME), }; if (nm_ndisc_add_dns_server(ndisc, &n, G_MININT64)) changed = TRUE; @@ -1127,8 +1191,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) n = (NMNDiscDNSDomain) { .domain = (char *) strvarr[i], - .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, - NM_NDISC_ROUTER_LIFETIME), + .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, NM_NDISC_ROUTER_LIFETIME), }; if (nm_ndisc_add_dns_domain(ndisc, &n, G_MININT64)) changed = TRUE; @@ -1400,6 +1463,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); @@ -1486,7 +1560,7 @@ clean_addresses(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint } if (i != j) { - *changed = NM_NDISC_CONFIG_ADDRESSES; + *changed |= NM_NDISC_CONFIG_ADDRESSES; g_array_set_size(rdata->addresses, j); } @@ -1520,11 +1594,50 @@ clean_routes(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 g_array_set_size(rdata->routes, j); } - if (_array_set_size_max(rdata->gateways, _SIZE_MAX_ROUTES)) + if (_array_set_size_max(rdata->routes, _SIZE_MAX_ROUTES)) *changed |= NM_NDISC_CONFIG_ROUTES; } 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->pref64->len == 0) + return; + + 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 clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) { NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; @@ -1550,7 +1663,7 @@ clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi g_array_set_size(rdata->dns_servers, j); } - if (_array_set_size_max(rdata->gateways, _SIZE_MAX_DNS_SERVERS)) + if (_array_set_size_max(rdata->dns_servers, _SIZE_MAX_DNS_SERVERS)) *changed |= NM_NDISC_CONFIG_DNS_SERVERS; } @@ -1580,12 +1693,12 @@ clean_dns_domains(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi j++; } - if (i != 0) { + if (i != j) { *changed |= NM_NDISC_CONFIG_DNS_DOMAINS; g_array_set_size(rdata->dns_domains, j); } - if (_array_set_size_max(rdata->gateways, _SIZE_MAX_DNS_DOMAINS)) + if (_array_set_size_max(rdata->dns_domains, _SIZE_MAX_DNS_DOMAINS)) *changed |= NM_NDISC_CONFIG_DNS_DOMAINS; } @@ -1600,6 +1713,7 @@ check_timestamps(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed) clean_gateways(ndisc, now_msec, &changed, &next_msec); clean_addresses(ndisc, now_msec, &changed, &next_msec); clean_routes(ndisc, now_msec, &changed, &next_msec); + clean_pref64(ndisc, now_msec, &changed, &next_msec); clean_dns_servers(ndisc, now_msec, &changed, &next_msec); clean_dns_domains(ndisc, now_msec, &changed, &next_msec); @@ -1919,6 +2033,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); @@ -1951,6 +2066,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); diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h index 8f1a12a2..cdd9a676 100644 --- a/src/core/ndisc/nm-ndisc.h +++ b/src/core/ndisc/nm-ndisc.h @@ -50,18 +50,6 @@ const char *nm_ndisc_dhcp_level_to_string(NMNDiscDHCPLevel level); * unit of it is milliseconds. But of course, infinity has not really a unit. */ #define NM_NDISC_EXPIRY_INFINITY G_MAXINT64 -/* in common cases, the expiry_msec tracks the timestamp in nm_utils_get_monotonic_timestamp_mses() - * timestamp when the item expires. - * - * When we configure an NMNDiscAddress to be announced via the router advertisement, - * then that address does not have a fixed expiry point in time, instead, the expiry - * really contains the lifetime from the moment when we send the router advertisement. - * In that case, the expiry_msec is more a "lifetime" that starts counting at timestamp - * zero. - * - * The unit is milliseconds (but of course, the timestamp is zero, so it doesn't really matter). */ -#define NM_NDISC_EXPIRY_BASE_TIMESTAMP ((gint64) 0) - static inline gint64 _nm_ndisc_lifetime_to_expiry(gint64 now_msec, guint32 lifetime) { @@ -119,6 +107,15 @@ typedef struct _NMNDiscRoute { bool duplicate : 1; } NMNDiscRoute; +typedef struct _NMNDiscPref64 { + struct in6_addr prefix; + struct in6_addr gateway; + gint64 expiry_msec; + gint64 gateway_expiry_msec; + NMIcmpv6RouterPref gateway_preference; + guint8 plen; +} NMNDiscPref64; + typedef struct { struct in6_addr address; gint64 expiry_msec; @@ -141,6 +138,7 @@ typedef enum { NM_NDISC_CONFIG_MTU = 1 << 7, NM_NDISC_CONFIG_REACHABLE_TIME = 1 << 8, NM_NDISC_CONFIG_RETRANS_TIMER = 1 << 9, + NM_NDISC_CONFIG_PREF64 = 1 << 10, } NMNDiscConfigMap; typedef enum { @@ -188,12 +186,14 @@ 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; } NMNDiscData; @@ -282,6 +282,7 @@ struct _NML3ConfigData *nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_id int ifindex, const NMNDiscData *rdata, NMSettingIP6ConfigPrivacy ip6_privacy, - NMUtilsIPv6IfaceId *token); + NMUtilsIPv6IfaceId *token, + const char *network_id); #endif /* __NETWORKMANAGER_NDISC_H__ */ |