From 2af4e12ad1dc66a8343a0649bbfe2491b9932071 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 19 Dec 2013 18:07:36 -0600 Subject: [PATCH] core: fix IPv6 router solicitation loop (rh #1044757) --- NetworkManager.spec | 7 +- rh1044757-ipv6-solicit-infinity.patch | 431 ++++++++++++++++++++++++++ 2 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 rh1044757-ipv6-solicit-infinity.patch diff --git a/NetworkManager.spec b/NetworkManager.spec index 001c6e9..c4c1d8c 100644 --- a/NetworkManager.spec +++ b/NetworkManager.spec @@ -19,7 +19,7 @@ Name: NetworkManager Summary: Network connection manager and user applications Epoch: 1 Version: 0.9.9.0 -Release: 21%{snapshot}%{?dist} +Release: 22%{snapshot}%{?dist} Group: System Environment/Base License: GPLv2+ URL: http://www.gnome.org/projects/NetworkManager/ @@ -48,6 +48,7 @@ Patch18: rh1018317-openvpn-ptp.patch Patch19: rh1034921-startup-link-wait.patch Patch20: rh1029213-ignore-RA-default-routes.patch Patch21: rh1032819-set-broadcast-address.patch +Patch22: rh1044757-ipv6-solicit-infinity.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -192,6 +193,7 @@ deployments. %patch19 -p1 -b .startup-linkwait %patch20 -p1 -b .ignore-RA-default-routes %patch21 -p1 -b .broadcast-addr +%patch22 -p1 -b .ipv6-solicit-infinity %build @@ -390,6 +392,9 @@ fi %config %{_sysconfdir}/%{name}/conf.d/00-server.conf %changelog +* Thu Dec 19 2013 Dan Williams - 0.9.9.0-22.git20131003 +- core: fix IPv6 router solicitation loop (rh #1044757) + * Thu Dec 12 2013 Dan Williams - 0.9.9.0-21.git20131003 - core: wait for link before declaring startup complete (rh #1034921) - core: ignore RA-provided IPv6 default routes (rh #1029213) diff --git a/rh1044757-ipv6-solicit-infinity.patch b/rh1044757-ipv6-solicit-infinity.patch new file mode 100644 index 0000000..818a714 --- /dev/null +++ b/rh1044757-ipv6-solicit-infinity.patch @@ -0,0 +1,431 @@ +From 8d3618a07baccc8abd9cfe7cf5b000b5d8c3340b Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 23 Oct 2013 18:37:02 +0200 +Subject: [PATCH] rdisc: emit config_change signal for update of address + lifetime + +Signed-off-by: Thomas Haller +--- + src/rdisc/nm-lndp-rdisc.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c +index 3299b32..f94d82a 100644 +--- a/src/rdisc/nm-lndp-rdisc.c ++++ b/src/rdisc/nm-lndp-rdisc.c +@@ -101,16 +101,19 @@ add_address (NMRDisc *rdisc, const NMRDiscAddress *new) + { + int i; + + for (i = 0; i < rdisc->addresses->len; i++) { + NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i); + + if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { +- memcpy (item, new, sizeof (*new)); +- return FALSE; ++ gboolean changed = item->timestamp + item->lifetime != new->timestamp + new->lifetime || ++ item->timestamp + item->preferred != new->timestamp + new->preferred; ++ ++ *item = *new; ++ return changed; + } + } + + g_array_insert_val (rdisc->addresses, i, *new); + return TRUE; + } + +-- +1.8.3.1 + +From 4f3f789fa5dad459a2aecabd77ef4a595dec5013 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 19 Dec 2013 10:58:46 -0600 +Subject: [PATCH] rdisc: ensure RDNSS and DNSSL lifetimes are updated (rh + #1044757) (bgo #720760) + +The DNS server and domain timestamps and lifetimes weren't updated +when a new RA was received. When half the lifetime for either of +them had passed, clean_dns_servers() and clean_domains() request a +Router Solicitation to ensure the DNS information does not expire. + +This obviously results in a new Router Advertisement, but since the +timestamps don't get updated, clean_dns_servers() and clean_domains() +simply request another solicitation because 'now' is still greater +than half the old lifetime. This casues another solicit request, +which causes another RA, which... etc. + +https://bugzilla.redhat.com/show_bug.cgi?id=1044757 +https://bugzilla.gnome.org/show_bug.cgi?id=720760 +--- + src/devices/nm-device.c | 9 +++-- + src/rdisc/nm-lndp-rdisc.c | 97 +++++++++++++++++++++++++++++------------------ + 2 files changed, 67 insertions(+), 39 deletions(-) + +diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c +index 74d443d..6f2383b 100644 +--- a/src/devices/nm-device.c ++++ b/src/devices/nm-device.c +@@ -3315,14 +3315,17 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device + NMRDiscDNSServer *discovered_server = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); + + nm_ip6_config_add_nameserver (priv->ac_ip6_config, &discovered_server->address); + } + } + + if (changed & NM_RDISC_CONFIG_DNS_DOMAINS) { ++ /* Rebuild domain list from router discovery cache. */ ++ nm_ip6_config_reset_domains (priv->ac_ip6_config); ++ + for (i = 0; i < rdisc->dns_domains->len; i++) { + NMRDiscDNSDomain *discovered_domain = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); + + nm_ip6_config_add_domain (priv->ac_ip6_config, discovered_domain->domain); + } + } + +@@ -3357,28 +3360,29 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device + + static gboolean + addrconf6_start (NMDevice *self) + { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; ++ const char *ip_iface = nm_device_get_ip_iface (self); + + connection = nm_device_get_connection (self); + g_assert (connection); + + g_warn_if_fail (priv->ac_ip6_config == NULL); + if (priv->ac_ip6_config) { + g_object_unref (priv->ac_ip6_config); + priv->ac_ip6_config = NULL; + } + +- priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self)); ++ priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface); + nm_platform_sysctl_set (priv->ip6_accept_ra_path, "0"); + + if (!priv->rdisc) { +- nm_log_err (LOGD_IP6, "Failed to start router discovery."); ++ nm_log_err (LOGD_IP6, "(%s): failed to start router discovery.", ip_iface); + return FALSE; + } + + priv->rdisc_config_changed_sigid = g_signal_connect ( + priv->rdisc, NM_RDISC_CONFIG_CHANGED, G_CALLBACK (rdisc_config_changed), self); + + /* FIXME: what if interface has no lladdr, like PPP? */ +diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c +index f94d82a..2e22fd9 100644 +--- a/src/rdisc/nm-lndp-rdisc.c ++++ b/src/rdisc/nm-lndp-rdisc.c +@@ -140,44 +140,68 @@ add_route (NMRDisc *rdisc, const NMRDiscRoute *new) + } + + g_array_insert_val (rdisc->routes, i, *new); + return TRUE; + } + + static gboolean +-add_server (NMRDisc *rdisc, const NMRDiscDNSServer *new) ++add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new) + { + int i; + + for (i = 0; i < rdisc->dns_servers->len; i++) { + NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); + +- if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) +- return FALSE; ++ if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { ++ gboolean changed = item->timestamp != new->timestamp || ++ item->lifetime != new->lifetime; ++ if (changed) { ++ item->timestamp = new->timestamp; ++ item->lifetime = new->lifetime; ++ } ++ return changed; ++ } + } + ++ /* DNS server should no longer be used */ ++ if (new->lifetime == 0) ++ return FALSE; ++ + g_array_insert_val (rdisc->dns_servers, i, *new); +- + return TRUE; + } + ++/* Always copies new->domain */ + static gboolean + add_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new) + { ++ NMRDiscDNSDomain *item; + int i; + + for (i = 0; i < rdisc->dns_domains->len; i++) { +- NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); ++ item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); + +- if (!g_strcmp0 (item->domain, new->domain)) +- return FALSE; ++ if (!g_strcmp0 (item->domain, new->domain)) { ++ gboolean changed = item->timestamp != new->timestamp || ++ item->lifetime != new->lifetime; ++ if (changed) { ++ item->timestamp = new->timestamp; ++ item->lifetime = new->lifetime; ++ } ++ return changed; ++ } + } + ++ /* Domain should no longer be used */ ++ if (new->lifetime == 0) ++ return FALSE; ++ + g_array_insert_val (rdisc->dns_domains, i, *new); +- ++ item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); ++ item->domain = g_strdup (new->domain); + return TRUE; + } + + #define RETRY 10 + + static gboolean + send_rs (NMRDisc *rdisc) +@@ -186,15 +210,15 @@ send_rs (NMRDisc *rdisc) + struct ndp_msg *msg; + int error; + + error = ndp_msg_new (&msg, NDP_MSG_RS); + g_assert (!error); + ndp_msg_ifindex_set (msg, rdisc->ifindex); + +- debug ("(%s): sending router solicitation: %d", rdisc->ifname, rdisc->ifindex); ++ debug ("(%s): sending router solicitation", rdisc->ifname); + + error = ndp_msg_send (priv->ndp, msg); + if (error) + error ("(%s): cannot send router solicitation: %d.", rdisc->ifname, error); + + ndp_msg_destroy (msg); + +@@ -218,139 +242,140 @@ solicit (NMRDisc *rdisc) + static void + clean_gateways (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent) + { + int i; + + for (i = 0; i < rdisc->gateways->len; i++) { + NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i); +- guint32 expiry = item->timestamp + item->lifetime; ++ guint64 expiry = item->timestamp + item->lifetime; + +- if (item->lifetime == UINT_MAX) ++ if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->gateways, i--); + *changed |= NM_RDISC_CONFIG_GATEWAYS; + } else if (*nextevent > expiry) +- *nextevent = expiry; ++ *nextevent = (guint32) expiry; + } + } + + static void + clean_addresses (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent) + { + int i; + + for (i = 0; i < rdisc->addresses->len; i++) { + NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i); +- guint32 expiry = item->timestamp + item->lifetime; ++ guint64 expiry = item->timestamp + item->lifetime; + +- if (item->lifetime == UINT_MAX) ++ if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->addresses, i--); + *changed |= NM_RDISC_CONFIG_ADDRESSES; + } else if (*nextevent > expiry) +- *nextevent = expiry; ++ *nextevent = (guint32) expiry; + } + } + + static void + clean_routes (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent) + { + int i; + + for (i = 0; i < rdisc->routes->len; i++) { + NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i); +- guint32 expiry = item->timestamp + item->lifetime; ++ guint64 expiry = item->timestamp + item->lifetime; + +- if (item->lifetime == UINT_MAX) ++ if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->routes, i--); + *changed |= NM_RDISC_CONFIG_ROUTES; + } else if (*nextevent > expiry) +- *nextevent = expiry; ++ *nextevent = (guint32) expiry; + } + } + + static void +-clean_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent) ++clean_dns_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent) + { + int i; + + for (i = 0; i < rdisc->dns_servers->len; i++) { + NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); +- guint32 expiry = item->timestamp + item->lifetime; +- guint32 refresh = item->timestamp + item->lifetime / 2; ++ guint64 expiry = item->timestamp + item->lifetime; ++ guint64 refresh = item->timestamp + item->lifetime / 2; + +- if (item->lifetime == UINT_MAX) ++ if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->dns_servers, i--); +- *changed |= NM_RDISC_CONFIG_ROUTES; ++ *changed |= NM_RDISC_CONFIG_DNS_SERVERS; + } else if (now >= refresh) + solicit (rdisc); + else if (*nextevent > refresh) +- *nextevent = refresh; ++ *nextevent = (guint32) refresh; + } + } + + static void + clean_domains (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent) + { + int i; + + for (i = 0; i < rdisc->dns_domains->len; i++) { + NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); +- guint32 expiry = item->timestamp + item->lifetime; +- guint32 refresh = item->timestamp + item->lifetime / 2; ++ guint64 expiry = item->timestamp + item->lifetime; ++ guint64 refresh = item->timestamp + item->lifetime / 2; + +- if (item->lifetime == UINT_MAX) ++ if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { ++ g_free (item->domain); + g_array_remove_index (rdisc->dns_domains, i--); +- *changed |= NM_RDISC_CONFIG_ROUTES; ++ *changed |= NM_RDISC_CONFIG_DNS_DOMAINS; + } else if (now >= refresh) + solicit (rdisc); + else if (*nextevent >=refresh) +- *nextevent = refresh; ++ *nextevent = (guint32) refresh; + } + } + + static gboolean timeout_cb (gpointer user_data); + + static void + check_timestamps (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed) + { + NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); +- /* Use a magic date in distant enough future as there's no guint32 max macro. */ +- guint32 never = G_MAXINT32; ++ /* Use a magic date in the distant future (~68 years) */ ++ guint32 never = G_MAXINT32; + guint32 nextevent = never; + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + clean_gateways (rdisc, now, &changed, &nextevent); + clean_addresses (rdisc, now, &changed, &nextevent); + clean_routes (rdisc, now, &changed, &nextevent); +- clean_servers (rdisc, now, &changed, &nextevent); ++ clean_dns_servers (rdisc, now, &changed, &nextevent); + clean_domains (rdisc, now, &changed, &nextevent); + + if (changed) + g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, changed); + + if (nextevent != never) { +- debug ("Scheduling next now/lifetime check: %d seconds", (int) nextevent); ++ debug ("(%s): scheduling next now/lifetime check: %u seconds", rdisc->ifname, nextevent); + priv->timeout_id = g_timeout_add_seconds (nextevent, timeout_cb, rdisc); + } + } + + static guint32 + get_time (void) + { +@@ -450,15 +475,15 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) + * + * The biggest difference from good old DHCP is that all configuration + * items have their own lifetimes and they are merged from various + * sources. Router discovery is *not* contract-based, so there is *no* + * single time when the configuration is finished and updates can + * come at any time. + */ +- debug ("Recieved router advertisement: %d at %d", rdisc->ifindex, (int) now); ++ debug ("(%s): received router advertisement at %u", rdisc->ifname, now); + + if (priv->send_rs_id) { + g_source_remove (priv->send_rs_id); + priv->send_rs_id = 0; + } + + /* DHCP level: +@@ -559,27 +584,27 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) + /* Pad the lifetime somewhat to give a bit of slack in cases + * where one RA gets lost or something (which can happen on unreliable + * links like WiFi where certain types of frames are not retransmitted). + * Note that 0 has special meaning and is therefore not adjusted. + */ + if (dns_server.lifetime && dns_server.lifetime < 7200) + dns_server.lifetime = 7200; +- if (add_server (rdisc, &dns_server)) ++ if (add_dns_server (rdisc, &dns_server)) + changed |= NM_RDISC_CONFIG_DNS_SERVERS; + } + } + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_DNSSL) { + char *domain; + int domain_index; + + ndp_msg_opt_dnssl_for_each_domain (domain, domain_index, msg, offset) { + NMRDiscDNSDomain dns_domain; + + memset (&dns_domain, 0, sizeof (dns_domain)); +- dns_domain.domain = g_strdup (domain); ++ dns_domain.domain = domain; + dns_domain.timestamp = now; + dns_domain.lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset); + /* Pad the lifetime somewhat to give a bit of slack in cases + * where one RA gets lost or something (which can happen on unreliable + * links like WiFi where certain types of frames are not retransmitted). + * Note that 0 has special meaning and is therefore not adjusted. + */ +-- +1.8.3.1 +