diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2f4ad2ad24..574f8c6476 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -5148,26 +5148,26 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) static void ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - gint32 now; - GArray * addresses, *dns_servers, *dns_domains; - guint len, i; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gs_unref_array GArray *addresses = NULL; + gs_unref_array GArray *dns_servers = NULL; + gs_unref_array GArray * dns_domains = NULL; + guint len; + guint i; const NMDedupMultiHeadEntry *head_entry; NMDedupMultiIter ipconf_iter; if (nm_ndisc_get_node_type(ndisc) != NM_NDISC_NODE_TYPE_ROUTER) return; - now = nm_utils_get_monotonic_timestamp_sec(); - head_entry = nm_ip6_config_lookup_addresses(priv->ip_config_6); addresses = g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscAddress), head_entry ? head_entry->len : 0); nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) { const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS(ipconf_iter.current->obj); NMNDiscAddress * ndisc_addr; - guint32 lifetime, preferred; - gint32 base; + guint32 lifetime; + guint32 preferred; if (IN6_IS_ADDR_UNSPECIFIED(&addr->address) || IN6_IS_ADDR_LINKLOCAL(&addr->address)) continue; @@ -5178,31 +5178,21 @@ ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self) if (addr->plen != 64) continue; - /* resolve the timestamps relative to a new base. - * - * Note that for convenience, platform @addr might have timestamp and/or - * lifetime unset. We don't allow that flexibility for ndisc and require - * well defined timestamps. */ - if (addr->timestamp) { - nm_assert(addr->timestamp < G_MAXINT32); - base = addr->timestamp; - } else - base = now; - lifetime = nm_utils_lifetime_get(addr->timestamp, addr->lifetime, addr->preferred, - base, + NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000, &preferred); if (!lifetime) continue; g_array_set_size(addresses, addresses->len + 1); - ndisc_addr = &g_array_index(addresses, NMNDiscAddress, addresses->len - 1); - ndisc_addr->address = addr->address; - ndisc_addr->timestamp = base; - ndisc_addr->lifetime = lifetime; - ndisc_addr->preferred = preferred; + ndisc_addr = &g_array_index(addresses, NMNDiscAddress, addresses->len - 1); + ndisc_addr->address = addr->address; + ndisc_addr->expiry_msec = + _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, lifetime); + ndisc_addr->expiry_preferred_msec = + _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, preferred); } len = nm_ip6_config_get_num_nameservers(priv->ip_config_6); @@ -5212,10 +5202,10 @@ ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self) const struct in6_addr *nameserver = nm_ip6_config_get_nameserver(priv->ip_config_6, i); NMNDiscDNSServer * ndisc_nameserver; - ndisc_nameserver = &g_array_index(dns_servers, NMNDiscDNSServer, i); - ndisc_nameserver->address = *nameserver; - ndisc_nameserver->timestamp = now; - ndisc_nameserver->lifetime = NM_NDISC_ROUTER_LIFETIME; + ndisc_nameserver = &g_array_index(dns_servers, NMNDiscDNSServer, i); + ndisc_nameserver->address = *nameserver; + ndisc_nameserver->expiry_msec = + _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME); } len = nm_ip6_config_get_num_searches(priv->ip_config_6); @@ -5225,16 +5215,13 @@ ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self) const char * search = nm_ip6_config_get_search(priv->ip_config_6, i); NMNDiscDNSDomain *ndisc_search; - ndisc_search = &g_array_index(dns_domains, NMNDiscDNSDomain, i); - ndisc_search->domain = (char *) search; - ndisc_search->timestamp = now; - ndisc_search->lifetime = NM_NDISC_ROUTER_LIFETIME; + ndisc_search = &g_array_index(dns_domains, NMNDiscDNSDomain, i); + ndisc_search->domain = (char *) search; + ndisc_search->expiry_msec = + _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME); } nm_ndisc_set_config(ndisc, addresses, dns_servers, dns_domains); - g_array_unref(addresses); - g_array_unref(dns_servers); - g_array_unref(dns_domains); } static void diff --git a/src/ndisc/nm-fake-ndisc.c b/src/ndisc/nm-fake-ndisc.c index 634a48c2f3..e9b329385e 100644 --- a/src/ndisc/nm-fake-ndisc.c +++ b/src/ndisc/nm-fake-ndisc.c @@ -30,11 +30,10 @@ typedef struct { typedef struct { struct in6_addr network; - int plen; struct in6_addr gateway; - guint32 timestamp; - guint32 lifetime; - guint32 preferred; + gint64 expiry_msec; + gint64 expiry_preferred_msec; + int plen; NMIcmpv6RouterPref preference; } FakePrefix; @@ -128,8 +127,7 @@ void nm_fake_ndisc_add_gateway(NMFakeNDisc * self, guint ra_id, const char * addr, - guint32 timestamp, - guint32 lifetime, + gint64 expiry_msec, NMIcmpv6RouterPref preference) { NMFakeNDiscPrivate *priv = NM_FAKE_NDISC_GET_PRIVATE(self); @@ -137,12 +135,12 @@ nm_fake_ndisc_add_gateway(NMFakeNDisc * self, NMNDiscGateway * gw; g_assert(ra); - g_array_set_size(ra->gateways, ra->gateways->len + 1); - gw = &g_array_index(ra->gateways, NMNDiscGateway, ra->gateways->len - 1); - g_assert(inet_pton(AF_INET6, addr, &gw->address) == 1); - gw->timestamp = timestamp; - gw->lifetime = lifetime; - gw->preference = preference; + + gw = nm_g_array_append_new(ra->gateways, NMNDiscGateway); + if (inet_pton(AF_INET6, addr, &gw->address) != 1) + g_assert_not_reached(); + gw->expiry_msec = expiry_msec; + gw->preference = preference; } void @@ -151,9 +149,8 @@ nm_fake_ndisc_add_prefix(NMFakeNDisc * self, const char * network, guint plen, const char * gateway, - guint32 timestamp, - guint32 lifetime, - guint32 preferred, + gint64 expiry_msec, + gint64 expiry_preferred_msec, NMIcmpv6RouterPref preference) { NMFakeNDiscPrivate *priv = NM_FAKE_NDISC_GET_PRIVATE(self); @@ -161,54 +158,52 @@ nm_fake_ndisc_add_prefix(NMFakeNDisc * self, FakePrefix * prefix; g_assert(ra); - g_array_set_size(ra->prefixes, ra->prefixes->len + 1); - prefix = &g_array_index(ra->prefixes, FakePrefix, ra->prefixes->len - 1); - memset(prefix, 0, sizeof(*prefix)); - g_assert(inet_pton(AF_INET6, network, &prefix->network) == 1); - g_assert(inet_pton(AF_INET6, gateway, &prefix->gateway) == 1); - prefix->plen = plen; - prefix->timestamp = timestamp; - prefix->lifetime = lifetime; - prefix->preferred = preferred; - prefix->preference = preference; + + prefix = nm_g_array_append_new(ra->prefixes, FakePrefix); + *prefix = (FakePrefix){ + .plen = plen, + .expiry_msec = expiry_msec, + .expiry_preferred_msec = expiry_preferred_msec, + .preference = preference, + }; + if (inet_pton(AF_INET6, network, &prefix->network) != 1) + g_assert_not_reached(); + if (inet_pton(AF_INET6, gateway, &prefix->gateway) != 1) + g_assert_not_reached(); } void nm_fake_ndisc_add_dns_server(NMFakeNDisc *self, guint ra_id, const char * address, - guint32 timestamp, - guint32 lifetime) + gint64 expiry_msec) { NMFakeNDiscPrivate *priv = NM_FAKE_NDISC_GET_PRIVATE(self); FakeRa * ra = find_ra(priv->ras, ra_id); NMNDiscDNSServer * dns; g_assert(ra); - g_array_set_size(ra->dns_servers, ra->dns_servers->len + 1); - dns = &g_array_index(ra->dns_servers, NMNDiscDNSServer, ra->dns_servers->len - 1); - g_assert(inet_pton(AF_INET6, address, &dns->address) == 1); - dns->timestamp = timestamp; - dns->lifetime = lifetime; + + dns = nm_g_array_append_new(ra->dns_servers, NMNDiscDNSServer); + + dns->expiry_msec = expiry_msec; + if (inet_pton(AF_INET6, address, &dns->address) != 1) + g_assert_not_reached(); } void -nm_fake_ndisc_add_dns_domain(NMFakeNDisc *self, - guint ra_id, - const char * domain, - guint32 timestamp, - guint32 lifetime) +nm_fake_ndisc_add_dns_domain(NMFakeNDisc *self, guint ra_id, const char *domain, gint64 expiry_msec) { NMFakeNDiscPrivate *priv = NM_FAKE_NDISC_GET_PRIVATE(self); FakeRa * ra = find_ra(priv->ras, ra_id); NMNDiscDNSDomain * dns; g_assert(ra); - g_array_set_size(ra->dns_domains, ra->dns_domains->len + 1); - dns = &g_array_index(ra->dns_domains, NMNDiscDNSDomain, ra->dns_domains->len - 1); - dns->domain = g_strdup(domain); - dns->timestamp = timestamp; - dns->lifetime = lifetime; + + dns = nm_g_array_append_new(ra->dns_domains, NMNDiscDNSDomain); + + dns->domain = g_strdup(domain); + dns->expiry_msec = expiry_msec; } gboolean @@ -230,13 +225,13 @@ send_rs(NMNDisc *ndisc, GError **error) static gboolean receive_ra(gpointer user_data) { - NMFakeNDisc * self = user_data; - NMFakeNDiscPrivate * priv = NM_FAKE_NDISC_GET_PRIVATE(self); - NMNDisc * ndisc = NM_NDISC(self); - NMNDiscDataInternal *rdata = ndisc->rdata; - FakeRa * ra = priv->ras->data; - NMNDiscConfigMap changed = 0; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); + NMFakeNDisc * self = user_data; + NMFakeNDiscPrivate * priv = NM_FAKE_NDISC_GET_PRIVATE(self); + NMNDisc * ndisc = NM_NDISC(self); + NMNDiscDataInternal *rdata = ndisc->rdata; + FakeRa * ra = priv->ras->data; + NMNDiscConfigMap changed = 0; + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); guint i; NMNDiscDHCPLevel dhcp_level; @@ -251,53 +246,51 @@ receive_ra(gpointer user_data) } for (i = 0; i < ra->gateways->len; i++) { - NMNDiscGateway *item = &g_array_index(ra->gateways, NMNDiscGateway, i); + const NMNDiscGateway *item = &g_array_index(ra->gateways, NMNDiscGateway, i); - if (nm_ndisc_add_gateway(ndisc, item)) + if (nm_ndisc_add_gateway(ndisc, item, now_msec)) changed |= NM_NDISC_CONFIG_GATEWAYS; } for (i = 0; i < ra->prefixes->len; i++) { - FakePrefix * item = &g_array_index(ra->prefixes, FakePrefix, i); - NMNDiscRoute route = { - .network = item->network, - .plen = item->plen, - .gateway = item->gateway, - .timestamp = item->timestamp, - .lifetime = item->lifetime, - .preference = item->preference, + FakePrefix * item = &g_array_index(ra->prefixes, FakePrefix, i); + const NMNDiscRoute route = { + .network = item->network, + .plen = item->plen, + .gateway = item->gateway, + .expiry_msec = item->expiry_msec, + .preference = item->preference, }; g_assert(route.plen > 0 && route.plen <= 128); - if (nm_ndisc_add_route(ndisc, &route)) + if (nm_ndisc_add_route(ndisc, &route, now_msec)) changed |= NM_NDISC_CONFIG_ROUTES; if (item->plen == 64) { - NMNDiscAddress address = { - .address = item->network, - .timestamp = item->timestamp, - .lifetime = item->lifetime, - .preferred = item->preferred, - .dad_counter = 0, + const NMNDiscAddress address = { + .address = item->network, + .expiry_msec = item->expiry_msec, + .expiry_preferred_msec = item->expiry_preferred_msec, + .dad_counter = 0, }; - if (nm_ndisc_complete_and_add_address(ndisc, &address, now)) + if (nm_ndisc_complete_and_add_address(ndisc, &address, now_msec)) changed |= NM_NDISC_CONFIG_ADDRESSES; } } for (i = 0; i < ra->dns_servers->len; i++) { - NMNDiscDNSServer *item = &g_array_index(ra->dns_servers, NMNDiscDNSServer, i); + const NMNDiscDNSServer *item = &g_array_index(ra->dns_servers, NMNDiscDNSServer, i); - if (nm_ndisc_add_dns_server(ndisc, item)) + if (nm_ndisc_add_dns_server(ndisc, item, now_msec)) changed |= NM_NDISC_CONFIG_DNS_SERVERS; } for (i = 0; i < ra->dns_domains->len; i++) { - NMNDiscDNSDomain *item = &g_array_index(ra->dns_domains, NMNDiscDNSDomain, i); + const NMNDiscDNSDomain *item = &g_array_index(ra->dns_domains, NMNDiscDNSDomain, i); - if (nm_ndisc_add_dns_domain(ndisc, item)) + if (nm_ndisc_add_dns_domain(ndisc, item, now_msec)) changed |= NM_NDISC_CONFIG_DNS_DOMAINS; } @@ -314,7 +307,7 @@ receive_ra(gpointer user_data) priv->ras = g_slist_remove(priv->ras, priv->ras->data); fake_ra_free(ra); - nm_ndisc_ra_received(NM_NDISC(self), now, changed); + nm_ndisc_ra_received(NM_NDISC(self), now_msec, changed); /* Schedule next RA */ if (priv->ras) { diff --git a/src/ndisc/nm-fake-ndisc.h b/src/ndisc/nm-fake-ndisc.h index b393b593d3..677f15fcac 100644 --- a/src/ndisc/nm-fake-ndisc.h +++ b/src/ndisc/nm-fake-ndisc.h @@ -35,8 +35,7 @@ guint nm_fake_ndisc_add_ra(NMFakeNDisc * self, void nm_fake_ndisc_add_gateway(NMFakeNDisc * self, guint ra_id, const char * addr, - guint32 timestamp, - guint32 lifetime, + gint64 expiry_msec, NMIcmpv6RouterPref preference); void nm_fake_ndisc_add_prefix(NMFakeNDisc * self, @@ -44,22 +43,19 @@ void nm_fake_ndisc_add_prefix(NMFakeNDisc * self, const char * network, guint plen, const char * gateway, - guint32 timestamp, - guint32 lifetime, - guint32 preferred, + gint64 expiry_msec, + gint64 expiry_preferred_msec, NMIcmpv6RouterPref preference); void nm_fake_ndisc_add_dns_server(NMFakeNDisc *self, guint ra_id, const char * address, - guint32 timestamp, - guint32 lifetime); + gint64 expiry_msec); void nm_fake_ndisc_add_dns_domain(NMFakeNDisc *self, guint ra_id, const char * domain, - guint32 timestamp, - guint32 lifetime); + gint64 expiry_msec); void nm_fake_ndisc_emit_new_ras(NMFakeNDisc *self); diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index 6f9ac9dcab..0a1a5f2274 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -117,7 +117,7 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) NMNDiscConfigMap changed = 0; struct ndp_msgra * msgra = ndp_msgra(msg); struct in6_addr gateway_addr; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); int offset; int hop_limit; guint32 val; @@ -133,7 +133,9 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) * single time when the configuration is finished and updates can * come at any time. */ - _LOGD("received router advertisement at %d", (int) now); + _LOGD("received router advertisement at timestamp %" G_GINT64_FORMAT ".%03d seconds", + now_msec / 1000, + (int) (now_msec % 1000)); gateway_addr = *ndp_msg_addrto(msg); if (IN6_IS_ADDR_UNSPECIFIED(&gateway_addr)) @@ -175,13 +177,18 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) */ { const NMNDiscGateway gateway = { - .address = gateway_addr, - .timestamp = now, - .lifetime = ndp_msgra_router_lifetime(msgra), - .preference = _route_preference_coerce(ndp_msgra_route_preference(msgra)), + .address = gateway_addr, + .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msgra_router_lifetime(msgra)), + .preference = _route_preference_coerce(ndp_msgra_route_preference(msgra)), }; - if (nm_ndisc_add_gateway(ndisc, &gateway)) + /* https://tools.ietf.org/html/rfc2461#section-4.2 + * > A Lifetime of 0 indicates that the router is not a + * > default router and SHOULD NOT appear on the default + * > router list. + * We handle that by tracking a gateway that expires right now. */ + + if (nm_ndisc_add_gateway(ndisc, &gateway, now_msec)) changed |= NM_NDISC_CONFIG_GATEWAYS; } @@ -204,64 +211,71 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) if (ndp_msg_opt_prefix_flag_on_link(msg, offset)) { const NMNDiscRoute route = { - .network = r_network, - .plen = r_plen, - .timestamp = now, - .lifetime = ndp_msg_opt_prefix_valid_time(msg, offset), + .network = r_network, + .plen = r_plen, + .expiry_msec = + _nm_ndisc_lifetime_to_expiry(now_msec, + ndp_msg_opt_prefix_valid_time(msg, offset)), }; - if (nm_ndisc_add_route(ndisc, &route)) + if (nm_ndisc_add_route(ndisc, &route, now_msec)) changed |= NM_NDISC_CONFIG_ROUTES; } /* Address */ if (r_plen == 64 && ndp_msg_opt_prefix_flag_auto_addr_conf(msg, offset)) { - NMNDiscAddress address = { - .address = r_network, - .timestamp = now, - .lifetime = ndp_msg_opt_prefix_valid_time(msg, offset), - .preferred = ndp_msg_opt_prefix_preferred_time(msg, offset), + const guint32 valid_time = ndp_msg_opt_prefix_valid_time(msg, offset); + const guint32 preferred_time = + NM_MIN(ndp_msg_opt_prefix_preferred_time(msg, offset), valid_time); + const NMNDiscAddress address = { + .address = r_network, + .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, valid_time), + .expiry_preferred_msec = _nm_ndisc_lifetime_to_expiry(now_msec, preferred_time), }; - if (address.preferred <= address.lifetime) { - if (nm_ndisc_complete_and_add_address(ndisc, &address, now)) - changed |= NM_NDISC_CONFIG_ADDRESSES; - } + if (nm_ndisc_complete_and_add_address(ndisc, &address, now_msec)) + changed |= NM_NDISC_CONFIG_ADDRESSES; } } ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_ROUTE) { - NMNDiscRoute route = { - .gateway = gateway_addr, - .plen = ndp_msg_opt_route_prefix_len(msg, offset), - .timestamp = now, - .lifetime = ndp_msg_opt_route_lifetime(msg, offset), - .preference = _route_preference_coerce(ndp_msg_opt_route_preference(msg, offset)), - }; + guint8 plen = ndp_msg_opt_route_prefix_len(msg, offset); + struct in6_addr network; - if (route.plen == 0 || route.plen > 128) + if (plen == 0 || plen > 128) continue; - /* Routers through this particular gateway */ - nm_utils_ip6_address_clear_host_address(&route.network, + nm_utils_ip6_address_clear_host_address(&network, ndp_msg_opt_route_prefix(msg, offset), - route.plen); - if (nm_ndisc_add_route(ndisc, &route)) - changed |= NM_NDISC_CONFIG_ROUTES; + plen); + + { + const NMNDiscRoute route = { + .network = network, + .gateway = gateway_addr, + .plen = plen, + .expiry_msec = + _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_route_lifetime(msg, offset)), + .preference = _route_preference_coerce(ndp_msg_opt_route_preference(msg, offset)), + }; + + /* Routers through this particular gateway */ + if (nm_ndisc_add_route(ndisc, &route, now_msec)) + changed |= NM_NDISC_CONFIG_ROUTES; + } } - /* DNS information */ ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_RDNSS) { struct in6_addr *addr; int addr_index; ndp_msg_opt_rdnss_for_each_addr (addr, addr_index, msg, offset) { - NMNDiscDNSServer dns_server = { - .address = *addr, - .timestamp = now, - .lifetime = ndp_msg_opt_rdnss_lifetime(msg, offset), + const NMNDiscDNSServer dns_server = { + .address = *addr, + .expiry_msec = + _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_rdnss_lifetime(msg, offset)), }; - if (nm_ndisc_add_dns_server(ndisc, &dns_server)) + if (nm_ndisc_add_dns_server(ndisc, &dns_server, now_msec)) changed |= NM_NDISC_CONFIG_DNS_SERVERS; } } @@ -270,13 +284,13 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) int domain_index; ndp_msg_opt_dnssl_for_each_domain (domain, domain_index, msg, offset) { - NMNDiscDNSDomain dns_domain = { - .domain = domain, - .timestamp = now, - .lifetime = ndp_msg_opt_dnssl_lifetime(msg, offset), + const NMNDiscDNSDomain dns_domain = { + .domain = domain, + .expiry_msec = + _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_dnssl_lifetime(msg, offset)), }; - if (nm_ndisc_add_dns_domain(ndisc, &dns_domain)) + if (nm_ndisc_add_dns_domain(ndisc, &dns_domain, now_msec)) changed |= NM_NDISC_CONFIG_DNS_DOMAINS; } } @@ -316,7 +330,7 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) } } - nm_ndisc_ra_received(ndisc, now, changed); + nm_ndisc_ra_received(ndisc, now_msec, changed); return 0; } @@ -374,7 +388,6 @@ send_ra(NMNDisc *ndisc, GError **error) { NMLndpNDiscPrivate * priv = NM_LNDP_NDISC_GET_PRIVATE(ndisc); NMNDiscDataInternal * rdata = ndisc->rdata; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); int errsv; struct in6_addr * addr; struct ndp_msg * msg; @@ -404,18 +417,9 @@ send_ra(NMNDisc *ndisc, GError **error) /* The device let us know about all addresses that the device got * whose prefixes are suitable for delegating. Let's announce them. */ for (i = 0; i < rdata->addresses->len; i++) { - const NMNDiscAddress *address = &g_array_index(rdata->addresses, NMNDiscAddress, i); - guint32 age = NM_CLAMP((gint64) now - (gint64) address->timestamp, 0, G_MAXUINT32 - 1); - guint32 lifetime = address->lifetime; - guint32 preferred = address->preferred; + const NMNDiscAddress * address = &g_array_index(rdata->addresses, NMNDiscAddress, i); struct nd_opt_prefix_info *prefix; - /* Clamp the life times if they're not forever. */ - if (lifetime != NM_NDISC_INFINITY) - lifetime = lifetime > age ? lifetime - age : 0; - if (preferred != NM_NDISC_INFINITY) - preferred = preferred > age ? preferred - age : 0; - prefix = _ndp_msg_add_option(msg, sizeof(*prefix)); if (!prefix) { /* Maybe we could sent separate RAs, but why bother... */ @@ -428,8 +432,14 @@ send_ra(NMNDisc *ndisc, GError **error) prefix->nd_opt_pi_prefix_len = 64; 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(lifetime); - prefix->nd_opt_pi_preferred_time = htonl(preferred); + prefix->nd_opt_pi_valid_time = + htonl(_nm_ndisc_lifetime_from_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, + 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)); 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/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h index e06820f94e..047391243b 100644 --- a/src/ndisc/nm-ndisc-private.h +++ b/src/ndisc/nm-ndisc-private.h @@ -21,14 +21,15 @@ struct _NMNDiscDataInternal { typedef struct _NMNDiscDataInternal NMNDiscDataInternal; -void nm_ndisc_ra_received(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed); +void nm_ndisc_ra_received(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed); void nm_ndisc_rs_received(NMNDisc *ndisc); -gboolean nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new); -gboolean nm_ndisc_complete_and_add_address(NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s); -gboolean nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new); -gboolean nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new); -gboolean nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new); +gboolean nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gint64 now_msec); +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_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/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index 0ac10b8808..a86853c081 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -41,7 +41,9 @@ struct _NMNDiscPrivate { gint32 last_rs; gint32 last_ra; }; - guint timeout_id; /* prefix/dns/etc lifetime timeout */ + + GSource *timeout_expire_source; + NMUtilsIPv6IfaceId iid; /* immutable values: */ @@ -84,7 +86,8 @@ G_DEFINE_TYPE(NMNDisc, nm_ndisc, G_TYPE_OBJECT) /*****************************************************************************/ -static void _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed); +static void _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed); +static gboolean timeout_expire_cb(gpointer user_data); /*****************************************************************************/ @@ -102,6 +105,7 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, guint32 ifa_flags; guint8 plen; guint i; + const gint32 now_sec = nm_utils_get_monotonic_timestamp_sec(); l3cd = nm_l3_config_data_new(multi_idx, ifindex); @@ -129,12 +133,17 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, NMPlatformIP6Address a; a = (NMPlatformIP6Address){ - .ifindex = ifindex, - .address = ndisc_addr->address, - .plen = plen, - .timestamp = ndisc_addr->timestamp, - .lifetime = ndisc_addr->lifetime, - .preferred = MIN(ndisc_addr->lifetime, ndisc_addr->preferred), + .ifindex = ifindex, + .address = ndisc_addr->address, + .plen = plen, + .timestamp = now_sec, + .lifetime = _nm_ndisc_lifetime_from_expiry(((gint64) now_sec) * 1000, + ndisc_addr->expiry_msec, + TRUE), + .preferred = _nm_ndisc_lifetime_from_expiry( + ((gint64) now_sec) * 1000, + NM_MIN(ndisc_addr->expiry_msec, ndisc_addr->expiry_preferred_msec), + TRUE), .addr_source = NM_IP_CONFIG_SOURCE_NDISC, .n_ifa_flags = ifa_flags, }; @@ -220,68 +229,40 @@ _preference_to_priority(NMIcmpv6RouterPref pref) /*****************************************************************************/ -/* we rely on the fact, that _EXPIRY_INFINITY > any other valid gint64 timestamps. */ -#define _EXPIRY_INFINITY G_MAXINT64 - -static gint64 -get_expiry_time(guint32 timestamp, guint32 lifetime) -{ - nm_assert(timestamp > 0); - nm_assert(timestamp <= G_MAXINT32); - - if (lifetime == NM_NDISC_INFINITY) - return _EXPIRY_INFINITY; - return ((gint64) timestamp) + ((gint64) lifetime); -} - -#define get_expiry(item) \ - ({ \ - typeof(item) _item = (item); \ - nm_assert(_item); \ - get_expiry_time(_item->timestamp, _item->lifetime); \ - }) - -#define get_expiry_preferred(item) \ - ({ \ - typeof(item) _item = (item); \ - nm_assert(_item); \ - get_expiry_time(_item->timestamp, _item->preferred); \ - }) - static gboolean -expiry_next(gint32 now_s, gint64 expiry_timestamp, gint32 *nextevent) +expiry_next(gint64 now_msec, gint64 expiry_msec, gint64 *next_msec) { - gint32 e; - - if (expiry_timestamp == _EXPIRY_INFINITY) + if (expiry_msec == NM_NDISC_EXPIRY_INFINITY) return TRUE; - e = MIN(expiry_timestamp, ((gint64)(G_MAXINT32 - 1))); - if (now_s >= e) + + if (expiry_msec <= now_msec) { + /* expired. */ return FALSE; - if (nextevent) { - if (*nextevent > e) - *nextevent = e; } + + if (next_msec) { + if (*next_msec > expiry_msec) + *next_msec = expiry_msec; + } + + /* the timestamp is good (not yet expired) */ return TRUE; } static const char * -_get_exp(char *buf, gsize buf_size, gint64 now_ns, gint64 expiry_time) +_get_exp(char *buf, gsize buf_size, gint64 now_msec, gint64 expiry_time) { int l; - if (expiry_time == _EXPIRY_INFINITY) + if (expiry_time == NM_NDISC_EXPIRY_INFINITY) return "permanent"; - l = g_snprintf(buf, - buf_size, - "%.4f", - ((double) ((expiry_time * NM_UTILS_NSEC_PER_SEC) - now_ns)) - / ((double) NM_UTILS_NSEC_PER_SEC)); + l = g_snprintf(buf, buf_size, "%.3f", ((double) (expiry_time - now_msec)) / 1000); nm_assert(l < buf_size); return buf; } -#define get_exp(buf, now_ns, item) _get_exp((buf), G_N_ELEMENTS(buf), (now_ns), (get_expiry(item))) +#define get_exp(buf, now_msec, item) \ + _get_exp((buf), G_N_ELEMENTS(buf), (now_msec), (item)->expiry_msec) /*****************************************************************************/ @@ -352,17 +333,16 @@ _ASSERT_data_gateways(const NMNDiscDataInternal *data) const NMNDiscGateway *item = &g_array_index(data->gateways, NMNDiscGateway, i); nm_assert(!IN6_IS_ADDR_UNSPECIFIED(&item->address)); - nm_assert(item->timestamp > 0 && item->timestamp <= G_MAXINT32); for (j = 0; j < i; j++) { const NMNDiscGateway *item2 = &g_array_index(data->gateways, NMNDiscGateway, j); nm_assert(!IN6_ARE_ADDR_EQUAL(&item->address, &item2->address)); } - nm_assert(item->lifetime > 0); - if (i > 0) + if (i > 0) { nm_assert(_preference_to_priority(item_prev->preference) >= _preference_to_priority(item->preference)); + } item_prev = item; } @@ -408,7 +388,7 @@ nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed) /*****************************************************************************/ gboolean -nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new) +nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gint64 now_msec) { NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; guint i; @@ -417,41 +397,43 @@ nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new) for (i = 0; i < rdata->gateways->len;) { NMNDiscGateway *item = &g_array_index(rdata->gateways, NMNDiscGateway, i); - if (IN6_ARE_ADDR_EQUAL(&item->address, &new->address)) { - if (new->lifetime == 0) { + if (IN6_ARE_ADDR_EQUAL(&item->address, &new_item->address)) { + if (new_item->expiry_msec <= now_msec) { g_array_remove_index(rdata->gateways, i); _ASSERT_data_gateways(rdata); return TRUE; } - if (item->preference != new->preference) { + if (item->preference != new_item->preference) { g_array_remove_index(rdata->gateways, i); continue; } - if (get_expiry(item) == get_expiry(new)) + if (item->expiry_msec == new_item->expiry_msec) return FALSE; - *item = *new; + item->expiry_msec = new_item->expiry_msec; _ASSERT_data_gateways(rdata); return TRUE; } /* Put before less preferable gateways. */ - if (_preference_to_priority(item->preference) < _preference_to_priority(new->preference) + if (_preference_to_priority(item->preference) + < _preference_to_priority(new_item->preference) && insert_idx == G_MAXUINT) insert_idx = i; i++; } - if (new->lifetime) { - g_array_insert_val(rdata->gateways, - insert_idx == G_MAXUINT ? rdata->gateways->len : insert_idx, - *new); - } + if (new_item->expiry_msec <= now_msec) + return FALSE; + + g_array_insert_val(rdata->gateways, + insert_idx == G_MAXUINT ? rdata->gateways->len : insert_idx, + *new_item); _ASSERT_data_gateways(rdata); - return !!new->lifetime; + return TRUE; } /** @@ -504,25 +486,27 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr) return TRUE; } - _LOGW("complete-address: can't generate a new EUI-64 address"); + _LOGW("complete-address: can't generate a new_item EUI-64 address"); return FALSE; } static gboolean -nm_ndisc_add_address(NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s, gboolean from_ra) +nm_ndisc_add_address(NMNDisc * ndisc, + const NMNDiscAddress *new_item, + gint64 now_msec, + gboolean from_ra) { NMNDiscPrivate * priv = NM_NDISC_GET_PRIVATE(ndisc); NMNDiscDataInternal *rdata = &priv->rdata; - NMNDiscAddress new2; + NMNDiscAddress * new2; NMNDiscAddress * existing = NULL; guint i; - nm_assert(new); - nm_assert(new->timestamp > 0 && new->timestamp < G_MAXINT32); - nm_assert(!IN6_IS_ADDR_UNSPECIFIED(&new->address)); - nm_assert(!IN6_IS_ADDR_LINKLOCAL(&new->address)); - nm_assert(new->preferred <= new->lifetime); - nm_assert(!from_ra || now_s > 0); + nm_assert(new_item); + nm_assert(!IN6_IS_ADDR_UNSPECIFIED(&new_item->address)); + nm_assert(!IN6_IS_ADDR_LINKLOCAL(&new_item->address)); + nm_assert(new_item->expiry_preferred_msec <= new_item->expiry_msec); + nm_assert((!!from_ra) == (now_msec > 0)); for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *item = &g_array_index(rdata->addresses, NMNDiscAddress, i); @@ -530,12 +514,12 @@ nm_ndisc_add_address(NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s, gb if (from_ra) { /* RFC4862 5.5.3.d, we find an existing address with the same prefix. * (note that all prefixes at this point have implicitly length /64). */ - if (memcmp(&item->address, &new->address, 8) == 0) { + if (memcmp(&item->address, &new_item->address, 8) == 0) { existing = item; break; } } else { - if (IN6_ARE_ADDR_EQUAL(&item->address, &new->address)) { + if (IN6_ARE_ADDR_EQUAL(&item->address, &new_item->address)) { existing = item; break; } @@ -543,67 +527,60 @@ nm_ndisc_add_address(NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s, gb } if (existing) { + gint64 new_expiry_preferred_msec; + gint64 new_expiry_msec; + if (from_ra) { - const gint32 NM_NDISC_PREFIX_LFT_MIN = 7200; /* seconds, RFC4862 5.5.3.e */ - gint64 old_expiry_lifetime, old_expiry_preferred; - - old_expiry_lifetime = get_expiry(existing); - old_expiry_preferred = get_expiry_preferred(existing); - - if (new->lifetime == NM_NDISC_INFINITY) - existing->lifetime = NM_NDISC_INFINITY; + if (new_item->expiry_msec == NM_NDISC_EXPIRY_INFINITY) + new_expiry_msec = NM_NDISC_EXPIRY_INFINITY; else { - gint64 new_lifetime, remaining_lifetime; + const gint64 NDISC_PREFIX_LFT_MIN_MSEC = 7200 * 1000; /* RFC4862 5.5.3.e */ + gint64 new_lifetime; + gint64 existing_lifetime; + + new_lifetime = new_item->expiry_msec - now_msec; + if (existing->expiry_msec == NM_NDISC_EXPIRY_INFINITY) + existing_lifetime = G_MAXINT64; + else + existing_lifetime = existing->expiry_msec - now_msec; /* see RFC4862 5.5.3.e */ - if (existing->lifetime == NM_NDISC_INFINITY) - remaining_lifetime = G_MAXINT64; - else - remaining_lifetime = ((gint64) existing->timestamp) - + ((gint64) existing->lifetime) - ((gint64) now_s); - new_lifetime = - ((gint64) new->timestamp) + ((gint64) new->lifetime) - ((gint64) now_s); - - if (new_lifetime > (gint64) NM_NDISC_PREFIX_LFT_MIN - || new_lifetime > remaining_lifetime) { - existing->timestamp = now_s; - existing->lifetime = CLAMP(new_lifetime, (gint64) 0, (gint64)(G_MAXUINT32 - 1)); - } else if (remaining_lifetime <= (gint64) NM_NDISC_PREFIX_LFT_MIN) { + if (new_lifetime >= NDISC_PREFIX_LFT_MIN_MSEC + || new_lifetime >= existing_lifetime) { + /* either extend the lifetime of the new_item lifetime is longer than + * NDISC_PREFIX_LFT_MIN_MSEC. */ + new_expiry_msec = new_item->expiry_msec; + } else if (existing_lifetime <= NDISC_PREFIX_LFT_MIN_MSEC) { /* keep the current lifetime. */ + new_expiry_msec = existing->expiry_msec; } else { - existing->timestamp = now_s; - existing->lifetime = NM_NDISC_PREFIX_LFT_MIN; + /* trim the current lifetime to NDISC_PREFIX_LFT_MIN_MSEC. */ + new_expiry_msec = now_msec + NDISC_PREFIX_LFT_MIN_MSEC; } } - if (new->preferred == NM_NDISC_INFINITY) { - nm_assert(existing->lifetime == NM_NDISC_INFINITY); - existing->preferred = new->preferred; - } else { - existing->preferred = NM_CLAMP(((gint64) new->timestamp) + ((gint64) new->preferred) - - ((gint64) existing->timestamp), - 0, - G_MAXUINT32 - 1); - if (existing->lifetime != NM_NDISC_INFINITY) - existing->preferred = MIN(existing->preferred, existing->lifetime); + new_expiry_preferred_msec = + NM_MIN(new_item->expiry_preferred_msec, new_item->expiry_msec); + new_expiry_preferred_msec = NM_MIN(new_expiry_preferred_msec, new_expiry_msec); + } else { + if (new_item->expiry_msec <= now_msec) { + g_array_remove_index(rdata->addresses, i); + return TRUE; } - return old_expiry_lifetime != get_expiry(existing) - || old_expiry_preferred != get_expiry_preferred(existing); + new_expiry_msec = new_item->expiry_msec; + new_expiry_preferred_msec = + NM_MIN(new_item->expiry_preferred_msec, new_item->expiry_msec); } - if (new->lifetime == 0) { - g_array_remove_index(rdata->addresses, i); - return TRUE; - } - - if (get_expiry(existing) == get_expiry(new) - && get_expiry_preferred(existing) == get_expiry_preferred(new)) + /* the dad_counter does not get modified. */ + if (new_expiry_msec == existing->expiry_msec + && new_expiry_preferred_msec == existing->expiry_preferred_msec) { return FALSE; + } - existing->timestamp = new->timestamp; - existing->lifetime = new->lifetime; - existing->preferred = new->preferred; + existing->expiry_msec = new_expiry_msec; + existing->expiry_preferred_msec = new_expiry_preferred_msec; return TRUE; } @@ -614,36 +591,42 @@ nm_ndisc_add_address(NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s, gb if (priv->max_addresses && rdata->addresses->len >= priv->max_addresses) return FALSE; - if (new->lifetime == 0) + if (new_item->expiry_msec <= now_msec) return FALSE; + new2 = nm_g_array_append_new(rdata->addresses, NMNDiscAddress); + + *new2 = *new_item; + + new2->expiry_preferred_msec = NM_MIN(new2->expiry_preferred_msec, new2->expiry_msec); + if (from_ra) { - new2 = *new; - new2.dad_counter = 0; - if (!complete_address(ndisc, &new2)) + new2->dad_counter = 0; + if (!complete_address(ndisc, new2)) { + g_array_set_size(rdata->addresses, rdata->addresses->len - 1); return FALSE; - new = &new2; + } } - g_array_append_val(rdata->addresses, *new); return TRUE; } gboolean -nm_ndisc_complete_and_add_address(NMNDisc *ndisc, const NMNDiscAddress *new, gint32 now_s) +nm_ndisc_complete_and_add_address(NMNDisc *ndisc, const NMNDiscAddress *new_item, gint64 now_msec) { - return nm_ndisc_add_address(ndisc, new, now_s, TRUE); + return nm_ndisc_add_address(ndisc, new_item, now_msec, TRUE); } gboolean -nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new) +nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec) { NMNDiscPrivate * priv; NMNDiscDataInternal *rdata; guint i; guint insert_idx = G_MAXUINT; + gboolean changed = FALSE; - if (new->plen == 0 || new->plen > 128) { + if (new_item->plen == 0 || new_item->plen > 128) { /* Only expect non-default routes. The router has no idea what the * local configuration or user preferences are, so sending routes * with a prefix length of 0 must be ignored by NMNDisc. @@ -660,41 +643,48 @@ nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new) for (i = 0; i < rdata->routes->len;) { NMNDiscRoute *item = &g_array_index(rdata->routes, NMNDiscRoute, i); - if (IN6_ARE_ADDR_EQUAL(&item->network, &new->network) && item->plen == new->plen) { - if (new->lifetime == 0) { + if (IN6_ARE_ADDR_EQUAL(&item->network, &new_item->network) + && item->plen == new_item->plen) { + if (new_item->expiry_msec <= now_msec) { g_array_remove_index(rdata->routes, i); return TRUE; } - if (item->preference != new->preference) { + if (item->preference != new_item->preference) { g_array_remove_index(rdata->routes, i); + changed = TRUE; continue; } - if (get_expiry(item) == get_expiry(new) - && IN6_ARE_ADDR_EQUAL(&item->gateway, &new->gateway)) + if (item->expiry_msec == new_item->expiry_msec + && IN6_ARE_ADDR_EQUAL(&item->gateway, &new_item->gateway)) return FALSE; - *item = *new; + item->expiry_msec = new_item->expiry_msec; + item->gateway = new_item->gateway; return TRUE; } /* Put before less preferable routes. */ - if (_preference_to_priority(item->preference) < _preference_to_priority(new->preference) + if (_preference_to_priority(item->preference) + < _preference_to_priority(new_item->preference) && insert_idx == G_MAXUINT) insert_idx = i; i++; } - if (new->lifetime) { - g_array_insert_val(rdata->routes, insert_idx == G_MAXUINT ? 0u : insert_idx, *new); + if (new_item->expiry_msec <= now_msec) { + nm_assert(!changed); + return FALSE; } - return !!new->lifetime; + + g_array_insert_val(rdata->routes, insert_idx == G_MAXUINT ? 0u : insert_idx, *new_item); + return TRUE; } gboolean -nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new) +nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec) { NMNDiscPrivate * priv; NMNDiscDataInternal *rdata; @@ -706,28 +696,30 @@ nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new) for (i = 0; i < rdata->dns_servers->len; i++) { NMNDiscDNSServer *item = &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); - if (IN6_ARE_ADDR_EQUAL(&item->address, &new->address)) { - if (new->lifetime == 0) { + if (IN6_ARE_ADDR_EQUAL(&item->address, &new_item->address)) { + if (new_item->expiry_msec <= now_msec) { g_array_remove_index(rdata->dns_servers, i); return TRUE; } - if (get_expiry(item) == get_expiry(new)) + if (item->expiry_msec == new_item->expiry_msec) return FALSE; - *item = *new; + item->expiry_msec = new_item->expiry_msec; return TRUE; } } - if (new->lifetime) - g_array_append_val(rdata->dns_servers, *new); - return !!new->lifetime; + if (new_item->expiry_msec <= now_msec) + return FALSE; + + g_array_append_val(rdata->dns_servers, *new_item); + return TRUE; } -/* Copies new->domain if 'new' is added to the dns_domains list */ +/* Copies new_item->domain if 'new_item' is added to the dns_domains list */ gboolean -nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new) +nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new_item, gint64 now_msec) { NMNDiscPrivate * priv; NMNDiscDataInternal *rdata; @@ -740,43 +732,45 @@ nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new) for (i = 0; i < rdata->dns_domains->len; i++) { item = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); - if (!g_strcmp0(item->domain, new->domain)) { - if (new->lifetime == 0) { + if (nm_streq(item->domain, new_item->domain)) { + if (new_item->expiry_msec <= now_msec) { g_array_remove_index(rdata->dns_domains, i); return TRUE; } - if (get_expiry(item) == get_expiry(new)) + if (item->expiry_msec == new_item->expiry_msec) return FALSE; - item->timestamp = new->timestamp; - item->lifetime = new->lifetime; + item->expiry_msec = new_item->expiry_msec; return TRUE; } } - if (new->lifetime) { - g_array_append_val(rdata->dns_domains, *new); - item = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, rdata->dns_domains->len - 1); - item->domain = g_strdup(new->domain); - } - return !!new->lifetime; + if (new_item->expiry_msec <= now_msec) + return FALSE; + + item = nm_g_array_append_new(rdata->dns_domains, NMNDiscDNSDomain); + *item = (NMNDiscDNSDomain){ + .domain = g_strdup(new_item->domain), + .expiry_msec = new_item->expiry_msec, + }; + return TRUE; } /*****************************************************************************/ -#define _MAYBE_WARN(...) \ - G_STMT_START \ - { \ - gboolean _different_message; \ - \ - _different_message = g_strcmp0(priv->last_error, error->message) != 0; \ - _NMLOG(_different_message ? LOGL_WARN : LOGL_DEBUG, __VA_ARGS__); \ - if (_different_message) { \ - nm_clear_g_free(&priv->last_error); \ - priv->last_error = g_strdup(error->message); \ - } \ - } \ +#define _MAYBE_WARN(...) \ + G_STMT_START \ + { \ + gboolean _different_message; \ + \ + _different_message = !nm_streq0(priv->last_error, error->message); \ + _NMLOG(_different_message ? LOGL_WARN : LOGL_DEBUG, __VA_ARGS__); \ + if (_different_message) { \ + nm_clear_g_free(&priv->last_error); \ + priv->last_error = g_strdup(error->message); \ + } \ + } \ G_STMT_END static gboolean @@ -937,12 +931,16 @@ nm_ndisc_set_config(NMNDisc * ndisc, } for (i = 0; i < dns_servers->len; i++) { - if (nm_ndisc_add_dns_server(ndisc, &g_array_index(dns_servers, NMNDiscDNSServer, i))) + if (nm_ndisc_add_dns_server(ndisc, + &g_array_index(dns_servers, NMNDiscDNSServer, i), + G_MININT64)) changed = TRUE; } for (i = 0; i < dns_domains->len; i++) { - if (nm_ndisc_add_dns_domain(ndisc, &g_array_index(dns_domains, NMNDiscDNSDomain, i))) + if (nm_ndisc_add_dns_domain(ndisc, + &g_array_index(dns_domains, NMNDiscDNSDomain, i), + G_MININT64)) changed = TRUE; } @@ -1098,7 +1096,7 @@ nm_ndisc_stop(NMNDisc *ndisc) nm_clear_g_source(&priv->send_rs_id); nm_clear_g_source(&priv->send_ra_id); nm_clear_g_free(&priv->last_error); - nm_clear_g_source(&priv->timeout_id); + nm_clear_g_source_inst(&priv->timeout_expire_source); priv->solicitations_left = 0; priv->announcements_left = 0; @@ -1180,15 +1178,15 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed) NMNDiscDataInternal *rdata; guint i; char changedstr[CONFIG_MAP_MAX_STR]; - char addrstr[INET6_ADDRSTRLEN]; + char addrstr[NM_UTILS_INET_ADDRSTRLEN]; char str_pref[35]; char str_exp[100]; - gint64 now_ns; + gint64 now_msec; if (!_LOGD_ENABLED()) return; - now_ns = nm_utils_get_monotonic_timestamp_nsec(); + now_msec = nm_utils_get_monotonic_timestamp_msec(); priv = NM_NDISC_GET_PRIVATE(ndisc); rdata = &priv->rdata; @@ -1205,199 +1203,245 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed) _LOGD(" retrans timer : %u", (guint) rdata->public.retrans_timer_ms); for (i = 0; i < rdata->gateways->len; i++) { - NMNDiscGateway *gateway = &g_array_index(rdata->gateways, NMNDiscGateway, i); + const NMNDiscGateway *gateway = &g_array_index(rdata->gateways, NMNDiscGateway, i); - inet_ntop(AF_INET6, &gateway->address, addrstr, sizeof(addrstr)); _LOGD(" gateway %s pref %s exp %s", - addrstr, + _nm_utils_inet6_ntop(&gateway->address, addrstr), nm_icmpv6_router_pref_to_string(gateway->preference, str_pref, sizeof(str_pref)), - get_exp(str_exp, now_ns, gateway)); + get_exp(str_exp, now_msec, gateway)); } for (i = 0; i < rdata->addresses->len; i++) { const NMNDiscAddress *address = &g_array_index(rdata->addresses, NMNDiscAddress, i); - inet_ntop(AF_INET6, &address->address, addrstr, sizeof(addrstr)); - _LOGD(" address %s exp %s", addrstr, get_exp(str_exp, now_ns, address)); + _LOGD(" address %s exp %s", + _nm_utils_inet6_ntop(&address->address, addrstr), + get_exp(str_exp, now_msec, address)); } for (i = 0; i < rdata->routes->len; i++) { - NMNDiscRoute *route = &g_array_index(rdata->routes, NMNDiscRoute, i); - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + const NMNDiscRoute *route = &g_array_index(rdata->routes, NMNDiscRoute, i); + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - inet_ntop(AF_INET6, &route->network, addrstr, sizeof(addrstr)); _LOGD(" route %s/%u via %s pref %s exp %s", - addrstr, + _nm_utils_inet6_ntop(&route->network, addrstr), (guint) route->plen, _nm_utils_inet6_ntop(&route->gateway, sbuf), nm_icmpv6_router_pref_to_string(route->preference, str_pref, sizeof(str_pref)), - get_exp(str_exp, now_ns, route)); + get_exp(str_exp, now_msec, route)); } for (i = 0; i < rdata->dns_servers->len; i++) { - NMNDiscDNSServer *dns_server = &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); + const NMNDiscDNSServer *dns_server = + &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); - inet_ntop(AF_INET6, &dns_server->address, addrstr, sizeof(addrstr)); - _LOGD(" dns_server %s exp %s", addrstr, get_exp(str_exp, now_ns, dns_server)); + _LOGD(" dns_server %s exp %s", + _nm_utils_inet6_ntop(&dns_server->address, addrstr), + get_exp(str_exp, now_msec, dns_server)); } for (i = 0; i < rdata->dns_domains->len; i++) { - NMNDiscDNSDomain *dns_domain = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); + const NMNDiscDNSDomain *dns_domain = + &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); - _LOGD(" dns_domain %s exp %s", dns_domain->domain, get_exp(str_exp, now_ns, dns_domain)); + _LOGD(" dns_domain %s exp %s", dns_domain->domain, get_exp(str_exp, now_msec, dns_domain)); } } +/*****************************************************************************/ + static void -clean_gateways(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) +clean_gateways(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) { - NMNDiscDataInternal *rdata; + NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + NMNDiscGateway * arr; guint i; + guint j; - rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + if (rdata->gateways->len == 0) + return; - for (i = 0; i < rdata->gateways->len;) { - NMNDiscGateway *item = &g_array_index(rdata->gateways, NMNDiscGateway, i); + arr = &g_array_index(rdata->gateways, NMNDiscGateway, 0); - if (!expiry_next(now, get_expiry(item), nextevent)) { - g_array_remove_index(rdata->gateways, i); - *changed |= NM_NDISC_CONFIG_GATEWAYS; + for (i = 0, j = 0; i < rdata->gateways->len; i++) { + if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) continue; - } + if (i != j) + arr[j] = arr[i]; + j++; + } - i++; + if (i != j) { + *changed |= NM_NDISC_CONFIG_GATEWAYS; + g_array_set_size(rdata->gateways, j); } _ASSERT_data_gateways(rdata); } static void -clean_addresses(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) +clean_addresses(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) { - NMNDiscDataInternal *rdata; + NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + NMNDiscAddress * arr; guint i; + guint j; - rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + if (rdata->addresses->len == 0) + return; - for (i = 0; i < rdata->addresses->len;) { - const NMNDiscAddress *item = &g_array_index(rdata->addresses, NMNDiscAddress, i); + arr = &g_array_index(rdata->addresses, NMNDiscAddress, 0); - if (!expiry_next(now, get_expiry(item), nextevent)) { - g_array_remove_index(rdata->addresses, i); - *changed |= NM_NDISC_CONFIG_ADDRESSES; + for (i = 0, j = 0; i < rdata->addresses->len; i++) { + if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) continue; - } + if (i != j) + arr[j] = arr[i]; + j++; + } - i++; + if (i != j) { + *changed = NM_NDISC_CONFIG_ADDRESSES; + g_array_set_size(rdata->addresses, j); } } static void -clean_routes(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) +clean_routes(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) { - NMNDiscDataInternal *rdata; + NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + NMNDiscRoute * arr; guint i; + guint j; - rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + if (rdata->routes->len == 0) + return; - for (i = 0; i < rdata->routes->len;) { - NMNDiscRoute *item = &g_array_index(rdata->routes, NMNDiscRoute, i); + arr = &g_array_index(rdata->routes, NMNDiscRoute, 0); - if (!expiry_next(now, get_expiry(item), nextevent)) { - g_array_remove_index(rdata->routes, i); - *changed |= NM_NDISC_CONFIG_ROUTES; + for (i = 0, j = 0; i < rdata->routes->len; i++) { + if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) continue; - } + if (i != j) + arr[j] = arr[i]; + j++; + } - i++; + if (i != j) { + *changed |= NM_NDISC_CONFIG_ROUTES; + g_array_set_size(rdata->routes, j); } } static void -clean_dns_servers(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) +clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) { - NMNDiscDataInternal *rdata; + NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + NMNDiscDNSServer * arr; guint i; + guint j; - rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + if (rdata->dns_servers->len == 0) + return; - for (i = 0; i < rdata->dns_servers->len;) { - NMNDiscDNSServer *item = &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); + arr = &g_array_index(rdata->dns_servers, NMNDiscDNSServer, 0); - if (!expiry_next(now, get_expiry(item), nextevent)) { - g_array_remove_index(rdata->dns_servers, i); - *changed |= NM_NDISC_CONFIG_DNS_SERVERS; + for (i = 0, j = 0; i < rdata->dns_servers->len; i++) { + if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) continue; - } + if (i != j) + arr[j] = arr[i]; + j++; + } - i++; + if (i != j) { + *changed |= NM_NDISC_CONFIG_DNS_SERVERS; + g_array_set_size(rdata->dns_servers, j); } } static void -clean_dns_domains(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) +clean_dns_domains(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) { - NMNDiscDataInternal *rdata; + NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + NMNDiscDNSDomain * arr; guint i; + guint j; - rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; + if (rdata->dns_domains->len == 0) + return; - for (i = 0; i < rdata->dns_domains->len;) { - NMNDiscDNSDomain *item = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); + arr = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, 0); - if (!expiry_next(now, get_expiry(item), nextevent)) { - g_array_remove_index(rdata->dns_domains, i); - *changed |= NM_NDISC_CONFIG_DNS_DOMAINS; + for (i = 0, j = 0; i < rdata->dns_domains->len; i++) { + if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) continue; + + if (i != j) { + g_free(arr[j].domain); + arr[j] = arr[i]; + arr[i].domain = NULL; } - i++; + j++; + } + + if (i != 0) { + *changed |= NM_NDISC_CONFIG_DNS_DOMAINS; + g_array_set_size(rdata->dns_domains, j); } } -static gboolean timeout_cb(gpointer user_data); - static void -check_timestamps(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed) +check_timestamps(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed) { - NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(ndisc); - /* Use a magic date in the distant future (~68 years) */ - gint32 nextevent = G_MAXINT32; + NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(ndisc); + gint64 next_msec = G_MAXINT64; - nm_clear_g_source(&priv->timeout_id); + _LOGT("router-data: check for changed router advertisement data"); - clean_gateways(ndisc, now, &changed, &nextevent); - clean_addresses(ndisc, now, &changed, &nextevent); - clean_routes(ndisc, now, &changed, &nextevent); - clean_dns_servers(ndisc, now, &changed, &nextevent); - clean_dns_domains(ndisc, now, &changed, &nextevent); + 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_dns_servers(ndisc, now_msec, &changed, &next_msec); + clean_dns_domains(ndisc, now_msec, &changed, &next_msec); - if (nextevent != G_MAXINT32) { - if (nextevent <= now) - g_return_if_reached(); - _LOGD("scheduling next now/lifetime check: %d seconds", (int) (nextevent - now)); - priv->timeout_id = g_timeout_add_seconds(nextevent - now, timeout_cb, ndisc); + nm_assert(next_msec > now_msec); + + nm_clear_g_source_inst(&priv->timeout_expire_source); + + if (next_msec == NM_NDISC_EXPIRY_INFINITY) + _LOGD("router-data: next lifetime expiration will happen: never"); + else { + const gint64 timeout_msec = NM_MIN(next_msec - now_msec, ((gint64) G_MAXINT32)); + const guint TIMEOUT_APPROX_THRESHOLD_SEC = 10000; + + _LOGD("router-data: next lifetime expiration will happen: in %s%.3f seconds", + (timeout_msec / 1000) >= TIMEOUT_APPROX_THRESHOLD_SEC ? " about" : "", + ((double) timeout_msec) / 1000); + + priv->timeout_expire_source = nm_g_timeout_add_source_approx(timeout_msec, + TIMEOUT_APPROX_THRESHOLD_SEC, + timeout_expire_cb, + ndisc); } - if (changed) + if (changed != NM_NDISC_CONFIG_NONE) nm_ndisc_emit_config_change(ndisc, changed); } static gboolean -timeout_cb(gpointer user_data) +timeout_expire_cb(gpointer user_data) { - NMNDisc *self = user_data; - - NM_NDISC_GET_PRIVATE(self)->timeout_id = 0; - check_timestamps(self, nm_utils_get_monotonic_timestamp_sec(), 0); - return G_SOURCE_REMOVE; + check_timestamps(user_data, nm_utils_get_monotonic_timestamp_msec(), NM_NDISC_CONFIG_NONE); + return G_SOURCE_CONTINUE; } void -nm_ndisc_ra_received(NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed) +nm_ndisc_ra_received(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(ndisc); nm_clear_g_source_inst(&priv->ra_timeout_source); nm_clear_g_source(&priv->send_rs_id); nm_clear_g_free(&priv->last_error); - check_timestamps(ndisc, now, changed); + check_timestamps(ndisc, now_msec, changed); } void @@ -1525,7 +1569,7 @@ dispose(GObject *object) nm_clear_g_source(&priv->send_ra_id); nm_clear_g_free(&priv->last_error); - nm_clear_g_source(&priv->timeout_id); + nm_clear_g_source_inst(&priv->timeout_expire_source); G_OBJECT_CLASS(nm_ndisc_parent_class)->dispose(object); } diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index fd0fd98681..74b48b90b9 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -48,48 +48,91 @@ typedef enum { NM_NDISC_DHCP_LEVEL_MANAGED } NMNDiscDHCPLevel; -/* we rely on the fact that NM_NDISC_INFINITY is the largest possible - * time duration (G_MAXUINT32) and that the range of finite values - * goes from 0 to G_MAXUINT32-1. */ -#define NM_NDISC_INFINITY G_MAXUINT32 +#define NM_NDISC_INFINITY_U32 ((uint32_t) -1) -struct _NMNDiscGateway { +/* It's important that this is G_MAXINT64, so that we can meaningfully do + * MIN(e1, e2) to find the minimum expiry time (and properly handle if any + * of them is infinity). + * + * While usually you assign this to "expiry_msec", you might say the + * 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) +{ + if (lifetime == NM_NDISC_INFINITY_U32) + return NM_NDISC_EXPIRY_INFINITY; + return now_msec + (((gint64) lifetime) * 1000); +} + +static inline gint64 +_nm_ndisc_lifetime_from_expiry(gint64 now_msec, gint64 expiry_msec, gboolean ceil) +{ + gint64 diff; + + if (expiry_msec == NM_NDISC_EXPIRY_INFINITY) + return NM_NDISC_INFINITY_U32; + + /* we don't expect nor handle integer overflow. The time stamp and expiry + * should be reasonably small so that it cannot happen. */ + + diff = expiry_msec - now_msec; + + if (diff <= 0) + return 0; + + if (ceil) { + /* we ceil() towards the next full second (instead of floor()). */ + diff += 999; + } + + return NM_MIN(diff / 1000, (gint64)(G_MAXUINT32 - 1)); +} + +/*****************************************************************************/ + +typedef struct _NMNDiscGateway { struct in6_addr address; - guint32 timestamp; - guint32 lifetime; + gint64 expiry_msec; NMIcmpv6RouterPref preference; -}; -typedef struct _NMNDiscGateway NMNDiscGateway; +} NMNDiscGateway; -struct _NMNDiscAddress { +typedef struct _NMNDiscAddress { struct in6_addr address; + gint64 expiry_msec; + gint64 expiry_preferred_msec; guint8 dad_counter; - guint32 timestamp; - guint32 lifetime; - guint32 preferred; -}; -typedef struct _NMNDiscAddress NMNDiscAddress; +} NMNDiscAddress; -struct _NMNDiscRoute { +typedef struct _NMNDiscRoute { struct in6_addr network; - guint8 plen; struct in6_addr gateway; - guint32 timestamp; - guint32 lifetime; + gint64 expiry_msec; NMIcmpv6RouterPref preference; -}; -typedef struct _NMNDiscRoute NMNDiscRoute; + guint8 plen; +} NMNDiscRoute; typedef struct { struct in6_addr address; - guint32 timestamp; - guint32 lifetime; + gint64 expiry_msec; } NMNDiscDNSServer; typedef struct { - char * domain; - guint32 timestamp; - guint32 lifetime; + char * domain; + gint64 expiry_msec; } NMNDiscDNSDomain; typedef enum { @@ -113,6 +156,7 @@ typedef enum { } NMNDiscNodeType; #define NM_NDISC_MAX_ADDRESSES_DEFAULT 16 +#define NM_NDISC_MAX_RTR_SOLICITATION_DELAY 1 /* RFC4861 MAX_RTR_SOLICITATION_DELAY */ #define NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT 3 /* RFC4861 MAX_RTR_SOLICITATIONS */ #define NM_NDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT 4 /* RFC4861 RTR_SOLICITATION_INTERVAL */ #define NM_NDISC_ROUTER_ADVERTISEMENTS_DEFAULT 3 /* RFC4861 MAX_INITIAL_RTR_ADVERTISEMENTS */ diff --git a/src/ndisc/tests/test-ndisc-fake.c b/src/ndisc/tests/test-ndisc-fake.c index 7deec0ca34..408fc420e0 100644 --- a/src/ndisc/tests/test-ndisc-fake.c +++ b/src/ndisc/tests/test-ndisc-fake.c @@ -14,6 +14,8 @@ #include "nm-test-utils-core.h" +/*****************************************************************************/ + static NMFakeNDisc * ndisc_new(void) { @@ -26,19 +28,20 @@ ndisc_new(void) iid.id_u8[7] = 1; nm_ndisc_set_iid(ndisc, iid); g_assert(ndisc); + return NM_FAKE_NDISC(ndisc); } +/*****************************************************************************/ + static void match_gateway(const NMNDiscData *rdata, guint idx, const char * addr, - guint32 ts, - guint32 lt, + gint64 expiry_msec, NMIcmpv6RouterPref pref) { const NMNDiscGateway *gw; - char buf[INET6_ADDRSTRLEN]; g_assert(rdata); g_assert_cmpint(idx, <, rdata->gateways_n); @@ -46,63 +49,57 @@ match_gateway(const NMNDiscData *rdata, gw = &rdata->gateways[idx]; - g_assert_cmpstr(inet_ntop(AF_INET6, &gw->address, buf, sizeof(buf)), ==, addr); - g_assert_cmpint(gw->timestamp, ==, ts); - g_assert_cmpint(gw->lifetime, ==, lt); + nmtst_assert_ip6_address(&gw->address, addr); + g_assert_cmpint(gw->expiry_msec, ==, expiry_msec); g_assert_cmpint(gw->preference, ==, pref); } -#define match_address(rdata, idx, addr, ts, lt, pref) \ - G_STMT_START \ - { \ - const NMNDiscData * _rdata = (rdata); \ - guint _idx = (idx); \ - const NMNDiscAddress *_a; \ - guint _ts = (ts); \ - \ - g_assert(_rdata); \ - g_assert_cmpint(_idx, <, _rdata->addresses_n); \ - g_assert(_rdata->addresses); \ - \ - _a = &_rdata->addresses[_idx]; \ - \ - nmtst_assert_ip6_address(&_a->address, (addr)); \ - g_assert_cmpint(_a->timestamp, <=, _ts + 1); \ - g_assert_cmpint((int) _a->timestamp, >=, (int) _ts - 1); \ - g_assert_cmpint(_a->timestamp + _a->lifetime, ==, _ts + (lt)); \ - g_assert_cmpint(_a->timestamp + _a->preferred, ==, _ts + (pref)); \ - } \ +#define match_address(rdata, idx, addr, _expiry_msec, _expiry_preferred_msec) \ + G_STMT_START \ + { \ + const NMNDiscData * _rdata = (rdata); \ + guint _idx = (idx); \ + const NMNDiscAddress *_a; \ + \ + g_assert(_rdata); \ + g_assert_cmpint(_idx, <, _rdata->addresses_n); \ + g_assert(_rdata->addresses); \ + \ + _a = &_rdata->addresses[_idx]; \ + \ + nmtst_assert_ip6_address(&_a->address, (addr)); \ + g_assert_cmpint(_a->expiry_msec, ==, (_expiry_msec)); \ + g_assert_cmpint(_a->expiry_preferred_msec, ==, (_expiry_preferred_msec)); \ + } \ G_STMT_END -#define match_route(rdata, idx, nw, pl, gw, ts, lt, pref) \ - G_STMT_START \ - { \ - const NMNDiscData * _rdata = (rdata); \ - guint _idx = (idx); \ - const NMNDiscRoute *_r; \ - int _plen = (pl); \ - \ - g_assert(_rdata); \ - g_assert_cmpint(_idx, <, _rdata->routes_n); \ - g_assert(_rdata->routes); \ - g_assert(_plen > 0 && _plen <= 128); \ - \ - _r = &_rdata->routes[idx]; \ - \ - nmtst_assert_ip6_address(&_r->network, (nw)); \ - g_assert_cmpint((int) _r->plen, ==, _plen); \ - nmtst_assert_ip6_address(&_r->gateway, (gw)); \ - g_assert_cmpint(_r->timestamp, ==, (ts)); \ - g_assert_cmpint(_r->lifetime, ==, (lt)); \ - g_assert_cmpint(_r->preference, ==, (pref)); \ - } \ +#define match_route(rdata, idx, nw, pl, gw, _expiry_msec, pref) \ + G_STMT_START \ + { \ + const NMNDiscData * _rdata = (rdata); \ + guint _idx = (idx); \ + const NMNDiscRoute *_r; \ + int _plen = (pl); \ + \ + g_assert(_rdata); \ + g_assert_cmpint(_idx, <, _rdata->routes_n); \ + g_assert(_rdata->routes); \ + g_assert(_plen > 0 && _plen <= 128); \ + \ + _r = &_rdata->routes[idx]; \ + \ + nmtst_assert_ip6_address(&_r->network, (nw)); \ + g_assert_cmpint((int) _r->plen, ==, _plen); \ + nmtst_assert_ip6_address(&_r->gateway, (gw)); \ + g_assert_cmpint(_r->expiry_msec, ==, (_expiry_msec)); \ + g_assert_cmpint(_r->preference, ==, (pref)); \ + } \ G_STMT_END static void -match_dns_server(const NMNDiscData *rdata, guint idx, const char *addr, guint32 ts, guint32 lt) +match_dns_server(const NMNDiscData *rdata, guint idx, const char *addr, gint64 expiry_msec) { const NMNDiscDNSServer *dns; - char buf[INET6_ADDRSTRLEN]; g_assert(rdata); g_assert_cmpint(idx, <, rdata->dns_servers_n); @@ -110,13 +107,12 @@ match_dns_server(const NMNDiscData *rdata, guint idx, const char *addr, guint32 dns = &rdata->dns_servers[idx]; - g_assert_cmpstr(inet_ntop(AF_INET6, &dns->address, buf, sizeof(buf)), ==, addr); - g_assert_cmpint(dns->timestamp, ==, ts); - g_assert_cmpint(dns->lifetime, ==, lt); + nmtst_assert_ip6_address(&dns->address, addr); + g_assert_cmpint(dns->expiry_msec, ==, expiry_msec); } static void -match_dns_domain(const NMNDiscData *rdata, guint idx, const char *domain, guint32 ts, guint32 lt) +match_dns_domain(const NMNDiscData *rdata, guint idx, const char *domain, gint64 expiry_msec) { const NMNDiscDNSDomain *dns; @@ -127,67 +123,96 @@ match_dns_domain(const NMNDiscData *rdata, guint idx, const char *domain, guint3 dns = &rdata->dns_domains[idx]; g_assert_cmpstr(dns->domain, ==, domain); - g_assert_cmpint(dns->timestamp, ==, ts); - g_assert_cmpint(dns->lifetime, ==, lt); + g_assert_cmpint(dns->expiry_msec, ==, expiry_msec); } +/*****************************************************************************/ + typedef struct { GMainLoop *loop; + gint64 timestamp_msec_1; guint counter; guint rs_counter; - guint32 timestamp1; - guint32 first_solicit; + gint64 first_solicit_msec; guint32 timeout_id; } TestData; +/*****************************************************************************/ + static void test_simple_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, TestData *data) { NMNDiscConfigMap changed = changed_int; - g_assert_cmpint(changed, - ==, - NM_NDISC_CONFIG_DHCP_LEVEL | NM_NDISC_CONFIG_GATEWAYS - | NM_NDISC_CONFIG_ADDRESSES | NM_NDISC_CONFIG_ROUTES - | NM_NDISC_CONFIG_DNS_SERVERS | NM_NDISC_CONFIG_DNS_DOMAINS - | NM_NDISC_CONFIG_HOP_LIMIT | NM_NDISC_CONFIG_MTU); - g_assert_cmpint(rdata->dhcp_level, ==, NM_NDISC_DHCP_LEVEL_OTHERCONF); - match_gateway(rdata, 0, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - match_address(rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); - match_route(rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10); - match_dns_server(rdata, 0, "2001:db8:c:c::1", data->timestamp1, 10); - match_dns_domain(rdata, 0, "foobar.com", data->timestamp1, 10); + switch (data->counter++) { + case 0: + g_assert_cmpint(changed, + ==, + NM_NDISC_CONFIG_DHCP_LEVEL | NM_NDISC_CONFIG_GATEWAYS + | NM_NDISC_CONFIG_ADDRESSES | NM_NDISC_CONFIG_ROUTES + | NM_NDISC_CONFIG_DNS_SERVERS | NM_NDISC_CONFIG_DNS_DOMAINS + | NM_NDISC_CONFIG_HOP_LIMIT | NM_NDISC_CONFIG_MTU); + g_assert_cmpint(rdata->dhcp_level, ==, NM_NDISC_DHCP_LEVEL_OTHERCONF); + match_gateway(rdata, + 0, + "fe80::1", + data->timestamp_msec_1 + 10000, + NM_ICMPV6_ROUTER_PREF_MEDIUM); + match_address(rdata, + 0, + "2001:db8:a:a::1", + data->timestamp_msec_1 + 10000, + data->timestamp_msec_1 + 10000); + match_route(rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp_msec_1 + 10000, 10); + match_dns_server(rdata, 0, "2001:db8:c:c::1", data->timestamp_msec_1 + 10000); + match_dns_domain(rdata, 0, "foobar.com", data->timestamp_msec_1 + 3500); - g_assert(nm_fake_ndisc_done(NM_FAKE_NDISC(ndisc))); - data->counter++; - g_main_loop_quit(data->loop); + g_assert(nm_fake_ndisc_done(NM_FAKE_NDISC(ndisc))); + break; + case 1: + g_main_loop_quit(data->loop); + break; + default: + g_assert_not_reached(); + } } static void test_simple(void) { - NMFakeNDisc *ndisc = ndisc_new(); - guint32 now = nm_utils_get_monotonic_timestamp_sec(); - TestData data = {g_main_loop_new(NULL, FALSE), 0, 0, now}; - guint id; + nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new(NULL, FALSE); + gs_unref_object NMFakeNDisc *ndisc = ndisc_new(); + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + TestData data = { + .loop = loop, + .timestamp_msec_1 = now_msec, + }; + guint id; id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_OTHERCONF, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 10); - nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now, 10); - nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar.com", now, 10); + + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec + 10000, NM_ICMPV6_ROUTER_PREF_MEDIUM); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:a::", + 64, + "fe80::1", + now_msec + 10000, + now_msec + 10000, + 10); + nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now_msec + 10000); + nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar.com", now_msec + 3500); g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, G_CALLBACK(test_simple_changed), &data); nm_ndisc_start(NM_NDISC(ndisc)); nmtst_main_loop_run_assert(data.loop, 15000); - g_assert_cmpint(data.counter, ==, 1); - - g_object_unref(ndisc); - g_main_loop_unref(data.loop); + g_assert_cmpint(data.counter, ==, 2); } +/*****************************************************************************/ + static void test_everything_rs_sent(NMNDisc *ndisc, TestData *data) { @@ -208,11 +233,19 @@ test_everything_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_ | NM_NDISC_CONFIG_ADDRESSES | NM_NDISC_CONFIG_ROUTES | NM_NDISC_CONFIG_DNS_SERVERS | NM_NDISC_CONFIG_DNS_DOMAINS | NM_NDISC_CONFIG_HOP_LIMIT | NM_NDISC_CONFIG_MTU); - match_gateway(rdata, 0, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - match_address(rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); - match_route(rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10); - match_dns_server(rdata, 0, "2001:db8:c:c::1", data->timestamp1, 10); - match_dns_domain(rdata, 0, "foobar.com", data->timestamp1, 10); + match_gateway(rdata, + 0, + "fe80::1", + data->timestamp_msec_1 + 10000, + NM_ICMPV6_ROUTER_PREF_MEDIUM); + match_address(rdata, + 0, + "2001:db8:a:a::1", + data->timestamp_msec_1 + 10000, + data->timestamp_msec_1 + 10000); + match_route(rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp_msec_1 + 10000, 10); + match_dns_server(rdata, 0, "2001:db8:c:c::1", data->timestamp_msec_1 + 10000); + match_dns_domain(rdata, 0, "foobar.com", data->timestamp_msec_1 + 10000); } else if (data->counter == 1) { g_assert_cmpint(changed, ==, @@ -221,16 +254,28 @@ test_everything_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_ | NM_NDISC_CONFIG_DNS_DOMAINS); g_assert_cmpint(rdata->gateways_n, ==, 1); - match_gateway(rdata, 0, "fe80::2", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); + match_gateway(rdata, + 0, + "fe80::2", + data->timestamp_msec_1 + 10000, + NM_ICMPV6_ROUTER_PREF_MEDIUM); g_assert_cmpint(rdata->addresses_n, ==, 2); - match_address(rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 0); - match_address(rdata, 1, "2001:db8:a:b::1", data->timestamp1, 10, 10); + match_address(rdata, + 0, + "2001:db8:a:a::1", + data->timestamp_msec_1 + 10000, + data->timestamp_msec_1); + match_address(rdata, + 1, + "2001:db8:a:b::1", + data->timestamp_msec_1 + 10000, + data->timestamp_msec_1 + 10000); g_assert_cmpint(rdata->routes_n, ==, 1); - match_route(rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1, 10, 10); + match_route(rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp_msec_1 + 10000, 10); g_assert_cmpint(rdata->dns_servers_n, ==, 1); - match_dns_server(rdata, 0, "2001:db8:c:c::2", data->timestamp1, 10); + match_dns_server(rdata, 0, "2001:db8:c:c::2", data->timestamp_msec_1 + 10000); g_assert_cmpint(rdata->dns_domains_n, ==, 1); - match_dns_domain(rdata, 0, "foobar2.com", data->timestamp1, 10); + match_dns_domain(rdata, 0, "foobar2.com", data->timestamp_msec_1 + 10000); g_assert(nm_fake_ndisc_done(NM_FAKE_NDISC(ndisc))); g_main_loop_quit(data->loop); @@ -243,31 +288,49 @@ test_everything_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_ static void test_everything(void) { - NMFakeNDisc *ndisc = ndisc_new(); - guint32 now = nm_utils_get_monotonic_timestamp_sec(); - TestData data = {g_main_loop_new(NULL, FALSE), 0, 0, now}; - guint id; + nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new(NULL, FALSE); + gs_unref_object NMFakeNDisc *ndisc = ndisc_new(); + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + TestData data = { + .loop = loop, + .timestamp_msec_1 = now_msec, + }; + guint id; id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 10); - nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now, 10); - nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar.com", now, 10); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec + 10000, NM_ICMPV6_ROUTER_PREF_MEDIUM); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:a::", + 64, + "fe80::1", + now_msec + 10000, + now_msec + 10000, + 10); + nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now_msec + 10000); + nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar.com", now_msec + 10000); /* expire everything from the first RA in the second */ id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 0, NM_ICMPV6_ROUTER_PREF_MEDIUM); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 0, 0, 0); - nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now, 0); - nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar.com", now, 0); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec, NM_ICMPV6_ROUTER_PREF_MEDIUM); + nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now_msec, now_msec, 0); + nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now_msec); + nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar.com", now_msec); /* and add some new stuff */ - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::2", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10); - nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::2", now, 10); - nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar2.com", now, 10); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::2", now_msec + 10000, NM_ICMPV6_ROUTER_PREF_MEDIUM); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:b::", + 64, + "fe80::2", + now_msec + 10000, + now_msec + 10000, + 10); + nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::2", now_msec + 10000); + nm_fake_ndisc_add_dns_domain(ndisc, id, "foobar2.com", now_msec + 10000); g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, G_CALLBACK(test_everything_changed), &data); g_signal_connect(ndisc, NM_FAKE_NDISC_RS_SENT, G_CALLBACK(test_everything_rs_sent), &data); @@ -276,9 +339,6 @@ test_everything(void) nmtst_main_loop_run_assert(data.loop, 15000); g_assert_cmpint(data.counter, ==, 2); g_assert_cmpint(data.rs_counter, ==, 1); - - g_object_unref(ndisc); - g_main_loop_unref(data.loop); } static void @@ -296,14 +356,30 @@ test_preference_order_cb(NMNDisc * ndisc, | NM_NDISC_CONFIG_ROUTES); g_assert_cmpint(rdata->gateways_n, ==, 2); - match_gateway(rdata, 0, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_HIGH); - match_gateway(rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_LOW); + match_gateway(rdata, + 0, + "fe80::1", + data->timestamp_msec_1 + 10000, + NM_ICMPV6_ROUTER_PREF_HIGH); + match_gateway(rdata, + 1, + "fe80::2", + data->timestamp_msec_1 + 11000, + NM_ICMPV6_ROUTER_PREF_LOW); g_assert_cmpint(rdata->addresses_n, ==, 2); - match_address(rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); - match_address(rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10); + match_address(rdata, + 0, + "2001:db8:a:a::1", + data->timestamp_msec_1 + 10000, + data->timestamp_msec_1 + 10000); + match_address(rdata, + 1, + "2001:db8:a:b::1", + data->timestamp_msec_1 + 11000, + data->timestamp_msec_1 + 10000); g_assert_cmpint(rdata->routes_n, ==, 2); - match_route(rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10); - match_route(rdata, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 5); + match_route(rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp_msec_1 + 11000, 10); + match_route(rdata, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp_msec_1 + 10000, 5); g_assert(nm_fake_ndisc_done(NM_FAKE_NDISC(ndisc))); g_main_loop_quit(data->loop); @@ -315,31 +391,46 @@ test_preference_order_cb(NMNDisc * ndisc, static void test_preference_order(void) { - NMFakeNDisc *ndisc = ndisc_new(); - guint32 now = nm_utils_get_monotonic_timestamp_sec(); - TestData data = {g_main_loop_new(NULL, FALSE), 0, 0, now}; - guint id; + nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new(NULL, FALSE); + gs_unref_object NMFakeNDisc *ndisc = ndisc_new(); + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + TestData data = { + .loop = loop, + .timestamp_msec_1 = now_msec, + }; + guint id; /* Test insertion order of gateways */ id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_HIGH); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 5); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec + 10000, NM_ICMPV6_ROUTER_PREF_HIGH); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:a::", + 64, + "fe80::1", + now_msec + 10000, + now_msec + 10000, + 5); id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::2", ++now, 10, NM_ICMPV6_ROUTER_PREF_LOW); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::2", now_msec + 11000, NM_ICMPV6_ROUTER_PREF_LOW); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:b::", + 64, + "fe80::2", + now_msec + 11000, + now_msec + 10000, + 10); g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, G_CALLBACK(test_preference_order_cb), &data); nm_ndisc_start(NM_NDISC(ndisc)); nmtst_main_loop_run_assert(data.loop, 15000); g_assert_cmpint(data.counter, ==, 2); - - g_object_unref(ndisc); - g_main_loop_unref(data.loop); } static void @@ -356,14 +447,30 @@ test_preference_changed_cb(NMNDisc * ndisc, NM_NDISC_CONFIG_GATEWAYS | NM_NDISC_CONFIG_ADDRESSES | NM_NDISC_CONFIG_ROUTES); g_assert_cmpint(rdata->gateways_n, ==, 2); - match_gateway(rdata, 0, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - match_gateway(rdata, 1, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_LOW); + match_gateway(rdata, + 0, + "fe80::2", + data->timestamp_msec_1 + 11000, + NM_ICMPV6_ROUTER_PREF_MEDIUM); + match_gateway(rdata, + 1, + "fe80::1", + data->timestamp_msec_1 + 10000, + NM_ICMPV6_ROUTER_PREF_LOW); g_assert_cmpint(rdata->addresses_n, ==, 2); - match_address(rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); - match_address(rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10); + match_address(rdata, + 0, + "2001:db8:a:a::1", + data->timestamp_msec_1 + 10000, + data->timestamp_msec_1 + 10000); + match_address(rdata, + 1, + "2001:db8:a:b::1", + data->timestamp_msec_1 + 11000, + data->timestamp_msec_1 + 11000); g_assert_cmpint(rdata->routes_n, ==, 2); - match_route(rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10); - match_route(rdata, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 5); + match_route(rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp_msec_1 + 11000, 10); + match_route(rdata, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp_msec_1 + 10000, 5); } else if (data->counter == 2) { g_assert_cmpint(changed, ==, @@ -371,14 +478,30 @@ test_preference_changed_cb(NMNDisc * ndisc, | NM_NDISC_CONFIG_ROUTES); g_assert_cmpint(rdata->gateways_n, ==, 2); - match_gateway(rdata, 0, "fe80::1", data->timestamp1 + 2, 10, NM_ICMPV6_ROUTER_PREF_HIGH); - match_gateway(rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); + match_gateway(rdata, + 0, + "fe80::1", + data->timestamp_msec_1 + 12000, + NM_ICMPV6_ROUTER_PREF_HIGH); + match_gateway(rdata, + 1, + "fe80::2", + data->timestamp_msec_1 + 11000, + NM_ICMPV6_ROUTER_PREF_MEDIUM); g_assert_cmpint(rdata->addresses_n, ==, 2); - match_address(rdata, 0, "2001:db8:a:a::1", data->timestamp1 + 3, 9, 9); - match_address(rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10); + match_address(rdata, + 0, + "2001:db8:a:a::1", + data->timestamp_msec_1 + 12000, + data->timestamp_msec_1 + 12000); + match_address(rdata, + 1, + "2001:db8:a:b::1", + data->timestamp_msec_1 + 11000, + data->timestamp_msec_1 + 11000); g_assert_cmpint(rdata->routes_n, ==, 2); - match_route(rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1 + 2, 10, 15); - match_route(rdata, 1, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10); + match_route(rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp_msec_1 + 12000, 15); + match_route(rdata, 1, "2001:db8:a:b::", 64, "fe80::2", data->timestamp_msec_1 + 11000, 10); g_assert(nm_fake_ndisc_done(NM_FAKE_NDISC(ndisc))); g_main_loop_quit(data->loop); @@ -390,10 +513,14 @@ test_preference_changed_cb(NMNDisc * ndisc, static void test_preference_changed(void) { - NMFakeNDisc *ndisc = ndisc_new(); - guint32 now = nm_utils_get_monotonic_timestamp_sec(); - TestData data = {g_main_loop_new(NULL, FALSE), 0, 0, now}; - guint id; + nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new(NULL, FALSE); + gs_unref_object NMFakeNDisc *ndisc = ndisc_new(); + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + TestData data = { + .loop = loop, + .timestamp_msec_1 = now_msec, + }; + guint id; /* Test that when a low-preference and medium gateway send advertisements, * that if the low-preference gateway switches to high-preference, we do @@ -402,18 +529,39 @@ test_preference_changed(void) id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_LOW); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 5); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec + 10000, NM_ICMPV6_ROUTER_PREF_LOW); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:a::", + 64, + "fe80::1", + now_msec + 10000, + now_msec + 10000, + 5); id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::2", ++now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::2", now_msec + 11000, NM_ICMPV6_ROUTER_PREF_MEDIUM); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:b::", + 64, + "fe80::2", + now_msec + 11000, + now_msec + 11000, + 10); id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", ++now, 10, NM_ICMPV6_ROUTER_PREF_HIGH); - nm_fake_ndisc_add_prefix(ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 15); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec + 12000, NM_ICMPV6_ROUTER_PREF_HIGH); + nm_fake_ndisc_add_prefix(ndisc, + id, + "2001:db8:a:a::", + 64, + "fe80::1", + now_msec + 12000, + now_msec + 12000, + 15); g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, @@ -423,16 +571,15 @@ test_preference_changed(void) nm_ndisc_start(NM_NDISC(ndisc)); nmtst_main_loop_run_assert(data.loop, 15000); g_assert_cmpint(data.counter, ==, 3); - - g_object_unref(ndisc); - g_main_loop_unref(data.loop); } +/*****************************************************************************/ + static void -test_dns_solicit_loop_changed(NMNDisc * ndisc, - const NMNDiscData *rdata, - guint changed_int, - TestData * data) +_test_dns_solicit_loop_changed(NMNDisc * ndisc, + const NMNDiscData *rdata, + guint changed_int, + TestData * data) { data->counter++; } @@ -446,14 +593,14 @@ success_timeout(TestData *data) } static void -test_dns_solicit_loop_rs_sent(NMFakeNDisc *ndisc, TestData *data) +_test_dns_solicit_loop_rs_sent(NMFakeNDisc *ndisc, TestData *data) { - guint32 now = nm_utils_get_monotonic_timestamp_sec(); - guint id; + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + guint id; if (data->rs_counter > 0 && data->rs_counter < 6) { if (data->rs_counter == 1) { - data->first_solicit = now; + data->first_solicit_msec = now_msec; /* Kill the test after 10 seconds if it hasn't failed yet */ data->timeout_id = g_timeout_add_seconds(10, (GSourceFunc) success_timeout, data); } @@ -464,12 +611,16 @@ test_dns_solicit_loop_rs_sent(NMFakeNDisc *ndisc, TestData *data) */ id = nm_fake_ndisc_add_ra(ndisc, 0, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); + nm_fake_ndisc_add_gateway(ndisc, + id, + "fe80::1", + now_msec + 10000, + NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_emit_new_ras(ndisc); } else if (data->rs_counter >= 6) { /* Fail if we've sent too many solicitations in the past 4 seconds */ - g_assert_cmpint(now - data->first_solicit, >, 4); + g_assert_cmpint(now_msec - data->first_solicit_msec, >, 4000); g_source_remove(data->timeout_id); g_main_loop_quit(data->loop); } @@ -479,10 +630,14 @@ test_dns_solicit_loop_rs_sent(NMFakeNDisc *ndisc, TestData *data) static void test_dns_solicit_loop(void) { - NMFakeNDisc *ndisc = ndisc_new(); - guint32 now = nm_utils_get_monotonic_timestamp_sec(); - TestData data = {g_main_loop_new(NULL, FALSE), 0, 0, now, 0}; - guint id; + nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new(NULL, FALSE); + gs_unref_object NMFakeNDisc *ndisc = ndisc_new(); + const gint64 now_msec = nm_utils_get_monotonic_timestamp_msec(); + TestData data = { + .loop = loop, + .timestamp_msec_1 = now_msec, + }; + guint id; g_test_skip("The solicitation behavior is wrong and need fixing. This test is not working too"); return; @@ -496,26 +651,25 @@ test_dns_solicit_loop(void) id = nm_fake_ndisc_add_ra(ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert(id); - nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_LOW); - nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now, 6); + nm_fake_ndisc_add_gateway(ndisc, id, "fe80::1", now_msec + 10000, NM_ICMPV6_ROUTER_PREF_LOW); + nm_fake_ndisc_add_dns_server(ndisc, id, "2001:db8:c:c::1", now_msec + 6000); g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, - G_CALLBACK(test_dns_solicit_loop_changed), + G_CALLBACK(_test_dns_solicit_loop_changed), &data); g_signal_connect(ndisc, NM_FAKE_NDISC_RS_SENT, - G_CALLBACK(test_dns_solicit_loop_rs_sent), + G_CALLBACK(_test_dns_solicit_loop_rs_sent), &data); nm_ndisc_start(NM_NDISC(ndisc)); nmtst_main_loop_run_assert(data.loop, 20000); g_assert_cmpint(data.counter, ==, 3); - - g_object_unref(ndisc); - g_main_loop_unref(data.loop); } +/*****************************************************************************/ + NMTST_DEFINE(); int diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index eeb69946a4..d48c6e30c8 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1497,6 +1497,7 @@ nm_ip6_config_reset_addresses_ndisc(NMIP6Config * self, NMIP6ConfigPrivate *priv; guint i; gboolean changed = FALSE; + gint32 base_time_sec; g_return_if_fail(NM_IS_IP6_CONFIG(self)); @@ -1504,6 +1505,16 @@ nm_ip6_config_reset_addresses_ndisc(NMIP6Config * self, g_return_if_fail(priv->ifindex > 0); + /* the base-timestamp doesn't matter it's only an anchor for the + * expiry. However, try to re-use the same base-time for a while + * by rounding it to 10000 seconds. + * + * That is because we deduplicate and NMPlatformIP6Address instances + * so using the same timestamps is preferable. */ + base_time_sec = nm_utils_get_monotonic_timestamp_sec(); + base_time_sec = (base_time_sec / 10000) * 10000; + base_time_sec = NM_MAX(1, base_time_sec); + nm_dedup_multi_index_dirty_set_idx(priv->multi_idx, &priv->idx_ip6_addresses); for (i = 0; i < addresses_n; i++) { @@ -1516,9 +1527,13 @@ nm_ip6_config_reset_addresses_ndisc(NMIP6Config * self, a->ifindex = priv->ifindex; a->address = ndisc_addr->address; a->plen = plen; - a->timestamp = ndisc_addr->timestamp; - a->lifetime = ndisc_addr->lifetime; - a->preferred = MIN(ndisc_addr->lifetime, ndisc_addr->preferred); + a->timestamp = base_time_sec, + a->lifetime = _nm_ndisc_lifetime_from_expiry(((gint64) base_time_sec) * 1000, + ndisc_addr->expiry_msec, + TRUE), + a->preferred = _nm_ndisc_lifetime_from_expiry(((gint64) base_time_sec) * 1000, + ndisc_addr->expiry_preferred_msec, + TRUE), a->addr_source = NM_IP_CONFIG_SOURCE_NDISC; a->n_ifa_flags = ifa_flags;