Thomas Haller 2021-01-27 10:18:24 +01:00
commit 5217d0046e
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
12 changed files with 1202 additions and 829 deletions

View file

@ -1364,6 +1364,59 @@ nm_g_source_attach(GSource *source, GMainContext *context)
return source;
}
static inline GSource *
nm_g_idle_add_source(GSourceFunc func, gpointer user_data)
{
/* G convenience function to attach a new timeout source to the default GMainContext.
* In that sense it's very similar to g_idle_add() except that it returns a
* reference to the new source. */
return nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_DEFAULT, func, user_data, NULL),
NULL);
}
static inline GSource *
nm_g_timeout_add_source(guint timeout_msec, GSourceFunc func, gpointer user_data)
{
/* G convenience function to attach a new timeout source to the default GMainContext.
* In that sense it's very similar to g_timeout_add() except that it returns a
* reference to the new source. */
return nm_g_source_attach(
nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL),
NULL);
}
static inline GSource *
nm_g_timeout_add_source_seconds(guint timeout_sec, GSourceFunc func, gpointer user_data)
{
/* G convenience function to attach a new timeout source to the default GMainContext.
* In that sense it's very similar to g_timeout_add_seconds() except that it returns a
* reference to the new source. */
return nm_g_source_attach(
nm_g_timeout_source_new_seconds(timeout_sec, G_PRIORITY_DEFAULT, func, user_data, NULL),
NULL);
}
static inline GSource *
nm_g_timeout_add_source_approx(guint timeout_msec,
guint timeout_sec_threshold,
GSourceFunc func,
gpointer user_data)
{
GSource *source;
/* If timeout_msec is larger or equal than a threshold, then we use g_timeout_source_new_seconds()
* instead. */
if (timeout_msec / 1000u >= timeout_sec_threshold)
source = nm_g_timeout_source_new_seconds(timeout_msec / 1000u,
G_PRIORITY_DEFAULT,
func,
user_data,
NULL);
else
source = nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL);
return nm_g_source_attach(source, NULL);
}
NM_AUTO_DEFINE_FCN0(GMainContext *, _nm_auto_unref_gmaincontext, g_main_context_unref);
#define nm_auto_unref_gmaincontext nm_auto(_nm_auto_unref_gmaincontext)

View file

@ -1194,6 +1194,14 @@ nmtst_main_loop_run(GMainLoop *loop, guint timeout_msec)
return loopx != NULL;
}
#define nmtst_main_loop_run_assert(loop, timeout_msec) \
G_STMT_START \
{ \
if (!nmtst_main_loop_run((loop), (timeout_msec))) \
g_assert_not_reached(); \
} \
G_STMT_END
static inline void
_nmtst_main_loop_quit_on_notify(GObject *object, GParamSpec *pspec, gpointer user_data)
{

View file

@ -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

View file

@ -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
@ -222,6 +217,7 @@ nm_fake_ndisc_done(NMFakeNDisc *self)
static gboolean
send_rs(NMNDisc *ndisc, GError **error)
{
_LOGT("send_rs()");
g_signal_emit(ndisc, signals[RS_SENT], 0);
return TRUE;
}
@ -229,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;
@ -250,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;
}
@ -313,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) {
@ -378,7 +372,7 @@ nm_fake_ndisc_new(int ifindex, const char *ifname)
NM_NDISC_ROUTER_SOLICITATIONS,
NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT,
NM_NDISC_ROUTER_SOLICITATION_INTERVAL,
NM_NDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT,
NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL,
NM_NDISC_RA_TIMEOUT,
30u,
NULL);

View file

@ -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);

View file

@ -85,14 +85,26 @@ send_rs(NMNDisc *ndisc, GError **error)
static NMIcmpv6RouterPref
_route_preference_coerce(enum ndp_route_preference pref)
{
#define _ASSERT_ENUM(v1, v2) \
G_STMT_START \
{ \
G_STATIC_ASSERT((NMIcmpv6RouterPref)(v1) == (v2)); \
G_STATIC_ASSERT((enum ndp_route_preference)(v2) == (v1)); \
G_STATIC_ASSERT((gint64)(v1) == (v2)); \
G_STATIC_ASSERT((gint64)(v2) == (v1)); \
} \
G_STMT_END
switch (pref) {
case NDP_ROUTE_PREF_LOW:
return NM_ICMPV6_ROUTER_PREF_LOW;
case NDP_ROUTE_PREF_MEDIUM:
return NM_ICMPV6_ROUTER_PREF_MEDIUM;
case NDP_ROUTE_PREF_HIGH:
return NM_ICMPV6_ROUTER_PREF_HIGH;
_ASSERT_ENUM(NDP_ROUTE_PREF_LOW, NM_ICMPV6_ROUTER_PREF_LOW);
_ASSERT_ENUM(NDP_ROUTE_PREF_MEDIUM, NM_ICMPV6_ROUTER_PREF_MEDIUM);
_ASSERT_ENUM(NDP_ROUTE_PREF_HIGH, NM_ICMPV6_ROUTER_PREF_HIGH);
return (NMIcmpv6RouterPref) pref;
}
/* unexpected value must be treated as MEDIUM (RFC 4191). */
return NM_ICMPV6_ROUTER_PREF_MEDIUM;
}
@ -105,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;
@ -121,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))
@ -163,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;
}
@ -192,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;
}
}
@ -258,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;
}
}
@ -304,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;
}
@ -362,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;
@ -392,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... */
@ -416,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;
@ -691,13 +713,12 @@ nm_lndp_ndisc_get_sysctl(NMPlatform *platform,
NM_SET_OUT(out_router_solicitations, router_solicitations);
}
if (out_router_solicitation_interval || out_default_ra_timeout) {
router_solicitation_interval =
ipv6_sysctl_get(platform,
ifname,
"router_solicitation_interval",
1,
G_MAXINT32,
NM_NDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT);
router_solicitation_interval = ipv6_sysctl_get(platform,
ifname,
"router_solicitation_interval",
1,
G_MAXINT32,
NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL);
NM_SET_OUT(out_router_solicitation_interval, router_solicitation_interval);
}
if (out_default_ra_timeout) {

View file

@ -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);
/*****************************************************************************/

File diff suppressed because it is too large Load diff

View file

@ -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 {
@ -112,15 +155,17 @@ typedef enum {
NM_NDISC_NODE_TYPE_ROUTER,
} NMNDiscNodeType;
#define NM_NDISC_MAX_ADDRESSES_DEFAULT 16
#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 */
#define NM_NDISC_ROUTER_ADVERT_DELAY 3 /* RFC4861 MIN_DELAY_BETWEEN_RAS */
#define NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL 16 /* RFC4861 MAX_INITIAL_RTR_ADVERT_INTERVAL */
#define NM_NDISC_ROUTER_ADVERT_DELAY_MS 500 /* RFC4861 MAX_RA_DELAY_TIME */
#define NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL 600 /* RFC4861 MaxRtrAdvInterval default */
#define NM_NDISC_ROUTER_LIFETIME 900 /* 1.5 * NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL */
#define NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL 4 /* seconds */
#define NM_NDISC_RFC4861_MAX_RTR_SOLICITATION_DELAY 1 /* seconds */
#define NM_NDISC_MAX_ADDRESSES_DEFAULT 16
#define NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT 3 /* RFC4861, MAX_RTR_SOLICITATIONS */
#define NM_NDISC_ROUTER_ADVERTISEMENTS_DEFAULT 3 /* RFC4861, MAX_INITIAL_RTR_ADVERTISEMENTS */
#define NM_NDISC_ROUTER_ADVERT_DELAY 3 /* RFC4861, MIN_DELAY_BETWEEN_RAS */
#define NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL 16 /* RFC4861, MAX_INITIAL_RTR_ADVERT_INTERVAL */
#define NM_NDISC_ROUTER_ADVERT_DELAY_MS 500 /* RFC4861, MAX_RA_DELAY_TIME */
#define NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL 600 /* RFC4861, MaxRtrAdvInterval default */
#define NM_NDISC_ROUTER_LIFETIME 900 /* 1.5 * NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL */
struct _NMNDiscPrivate;
struct _NMNDiscDataInternal;

View file

@ -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));
g_main_loop_run(data.loop);
g_assert_cmpint(data.counter, ==, 1);
g_object_unref(ndisc);
g_main_loop_unref(data.loop);
nmtst_main_loop_run_assert(data.loop, 15000);
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,42 +288,57 @@ 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);
nm_ndisc_start(NM_NDISC(ndisc));
g_main_loop_run(data.loop);
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));
g_main_loop_run(data.loop);
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,
@ -421,68 +569,38 @@ test_preference_changed(void)
&data);
nm_ndisc_start(NM_NDISC(ndisc));
g_main_loop_run(data.loop);
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++;
}
static gboolean
success_timeout(TestData *data)
{
data->timeout_id = 0;
g_main_loop_quit(data->loop);
return G_SOURCE_REMOVE;
}
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;
if (data->rs_counter > 0 && data->rs_counter < 6) {
if (data->rs_counter == 1) {
data->first_solicit = now;
/* Kill the test after 10 seconds if it hasn't failed yet */
data->timeout_id = g_timeout_add_seconds(10, (GSourceFunc) success_timeout, data);
}
/* On all but the first solicitation, which should be triggered by the
* DNS servers reaching 1/2 lifetime, emit a new RA without the DNS
* servers again.
*/
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_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_source_remove(data->timeout_id);
g_main_loop_quit(data->loop);
}
data->rs_counter++;
}
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;
/* Ensure that no solicitation loop happens when DNS servers or domains
* stop being sent in advertisements. This can happen if two routers
@ -493,26 +611,27 @@ 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));
g_main_loop_run(data.loop);
if (nmtst_main_loop_run(data.loop, 10000))
g_error("we expect to run the loop until timeout. What is wrong?");
g_assert_cmpint(data.counter, ==, 3);
g_object_unref(ndisc);
g_main_loop_unref(data.loop);
g_assert_cmpint(data.rs_counter, ==, 1);
}
/*****************************************************************************/
NMTST_DEFINE();
int

View file

@ -559,7 +559,7 @@ char * nm_utils_format_con_diff_for_audit(GHashTable *diff);
/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from <linux/icmpv6.h>,
* the values for netlink attribute RTA_PREF) and "enum ndp_route_preference"
* from <ndp.h>. */
typedef enum {
typedef enum _nm_packed {
NM_ICMPV6_ROUTER_PREF_MEDIUM = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */
NM_ICMPV6_ROUTER_PREF_LOW = 0x3, /* ICMPV6_ROUTER_PREF_LOW */
NM_ICMPV6_ROUTER_PREF_HIGH = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */

View file

@ -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;