[NetworkManager/f20] backport patches for IPv6 privacy

thaller thaller at fedoraproject.org
Sun Feb 16 14:39:22 UTC 2014


commit c84b54dbfe0c56753f497dc13fa0ee40a87d0d91
Author: Thomas Haller <thaller at redhat.com>
Date:   Sat Feb 15 12:16:46 2014 +0100

    backport patches for IPv6 privacy

 0044-ipv6-privacy.patch | 1739 +++++++++++++++++++++++++++++++++++++++++++++++
 NetworkManager.spec     |    5 +
 2 files changed, 1744 insertions(+), 0 deletions(-)
---
diff --git a/0044-ipv6-privacy.patch b/0044-ipv6-privacy.patch
new file mode 100644
index 0000000..afbc2ef
--- /dev/null
+++ b/0044-ipv6-privacy.patch
@@ -0,0 +1,1739 @@
+From 9989350adbe25dccb716f8df41ac915c1e262304 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Sat, 15 Feb 2014 13:20:40 +0100
+Subject: [PATCH 01/14] fix several compile errors
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c                   | 2 +-
+ src/settings/nm-system-config-interface.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index c3b3f21..92de1db 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -6925,7 +6925,7 @@ nm_device_add_pending_action (NMDevice *device, const char *action)
+ 			             nm_device_get_iface (device),
+ 			             g_slist_length (priv->pending_actions),
+ 			             action);
+-			g_return_val_if_reached (FALSE);
++			g_return_if_reached ();
+ 		}
+ 	}
+ 
+diff --git a/src/settings/nm-system-config-interface.c b/src/settings/nm-system-config-interface.c
+index 18457ea..fd36fc4 100644
+--- a/src/settings/nm-system-config-interface.c
++++ b/src/settings/nm-system-config-interface.c
+@@ -141,7 +141,7 @@ gboolean
+ nm_system_config_interface_load_connection (NMSystemConfigInterface *config,
+                                             const char *filename)
+ {
+-	g_return_val_if_fail (config != NULL, NULL);
++	g_return_val_if_fail (config != NULL, FALSE);
+ 
+ 	if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->load_connection)
+ 		return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->load_connection (config, filename);
+-- 
+1.8.5.3
+
+
+From 7ffd758bf4e0d57a240ec4031bf987b397950463 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Tue, 21 Jan 2014 13:07:06 +0100
+Subject: [PATCH 02/14] core: add nm_utils_ascii_str_to_int64() function
+
+(cherry picked from commit 63075d98a59270ffcb89bb556b6ee5efc14eef0f)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/NetworkManagerUtils.c | 60 +++++++++++++++++++++++++++++++++++++
+ src/NetworkManagerUtils.h |  2 ++
+ src/tests/Makefile.am     | 10 +++++++
+ src/tests/test-general.c  | 76 +++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 148 insertions(+)
+ create mode 100644 src/tests/test-general.c
+
+diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
+index bc3d1b2..d0937fb 100644
+--- a/src/NetworkManagerUtils.c
++++ b/src/NetworkManagerUtils.c
+@@ -644,3 +644,63 @@ nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id)
+ 	return g_strdup_printf ("%s.%d", parent_iface, vlan_id);
+ }
+ 
++/* nm_utils_ascii_str_to_int64:
++ *
++ * A wrapper for g_ascii_strtoll, that checks whether the whole string
++ * can be successfully converted to a number and is within a given
++ * range. On any error, @fallback will be returned and @errno will be set
++ * to a non-zero value. Check @errno for errors. Any trailing or leading
++ * (ascii) white space is ignored and the functions is locale independent.
++ *
++ * The function is guaranteed to return a value between @min and @max
++ * (included) or @fallback. Also, the parsing is rather strict, it does
++ * not allow for any unrecognized characters, except leading and trailing
++ * white space.
++ **/
++gint64
++nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback)
++{
++	gint64 v;
++	char *end;
++	char *str_free = NULL;
++
++	if (str) {
++		while (str[0] && g_ascii_isspace (str[0]))
++			str++;
++	}
++	if (!str || !str[0]) {
++		errno = EINVAL;
++		return fallback;
++	}
++
++	if (g_ascii_isspace (str[strlen (str) - 1])) {
++		str_free = g_strdup (str);
++		g_strstrip (str_free);
++		str = str_free;
++	}
++
++	errno = 0;
++	v = g_ascii_strtoll (str, &end, base);
++
++	if (errno != 0) {
++		g_free (str_free);
++		return fallback;
++	}
++
++	if (end[0] != 0) {
++		g_free (str_free);
++		errno = EINVAL;
++		return fallback;
++	}
++
++	g_free (str_free);
++	if (v > max || v < min) {
++		errno = ERANGE;
++		return fallback;
++	}
++
++	return v;
++}
++
++
++
+diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
+index 819cb07..0904c51 100644
+--- a/src/NetworkManagerUtils.h
++++ b/src/NetworkManagerUtils.h
+@@ -91,4 +91,6 @@ void nm_utils_complete_generic (NMConnection *connection,
+ 
+ char *nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id);
+ 
++gint64 nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback);
++
+ #endif /* NETWORK_MANAGER_UTILS_H */
+diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
+index 3e485fd..2b42d2c 100644
+--- a/src/tests/Makefile.am
++++ b/src/tests/Makefile.am
+@@ -12,6 +12,7 @@ AM_CPPFLAGS = \
+ 
+ noinst_PROGRAMS = \
+ 	test-dhcp-options \
++	test-general \
+ 	test-policy-hosts \
+ 	test-wifi-ap-utils \
+ 	test-ip4-config \
+@@ -62,6 +63,14 @@ test_ip6_config_SOURCES = \
+ test_ip6_config_LDADD = \
+ 	$(top_builddir)/src/libNetworkManager.la
+ 
++####### general test #######
++
++test_general_SOURCES = \
++	test-general.c
++
++test_general_LDADD = \
++	$(top_builddir)/src/libNetworkManager.la
++
+ ####### secret agent interface test #######
+ 
+ EXTRA_DIST = test-secret-agent.py
+@@ -74,4 +83,5 @@ check-local: test-dhcp-options test-policy-hosts test-wifi-ap-utils test-ip4-con
+ 	$(abs_builddir)/test-wifi-ap-utils
+ 	$(abs_builddir)/test-ip4-config
+ 	$(abs_builddir)/test-ip6-config
++	$(abs_builddir)/test-general
+ 
+diff --git a/src/tests/test-general.c b/src/tests/test-general.c
+new file mode 100644
+index 0000000..649653c
+--- /dev/null
++++ b/src/tests/test-general.c
+@@ -0,0 +1,76 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Copyright (C) 2014 Red Hat, Inc.
++ *
++ */
++
++#include <glib.h>
++#include <string.h>
++#include <errno.h>
++
++#include "NetworkManagerUtils.h"
++
++
++static void
++test_nm_utils_ascii_str_to_int64_do (const char *str, guint base, gint64 min,
++                                     gint64 max, gint64 fallback, int exp_errno,
++                                     gint64 exp_val)
++{
++	gint64 v;
++
++	errno = 0;
++	v = nm_utils_ascii_str_to_int64 (str, base, min, max, fallback);
++	g_assert_cmpint (errno, ==, exp_errno);
++	g_assert_cmpint (v, ==, exp_val);
++}
++
++static void
++test_nm_utils_ascii_str_to_int64 (void)
++{
++	test_nm_utils_ascii_str_to_int64_do ("4711", 10, 0, 10000, -1, 0, 4711);
++	test_nm_utils_ascii_str_to_int64_do ("", 10, 0, 10000, -1, EINVAL, -1);
++	test_nm_utils_ascii_str_to_int64_do (NULL, 10, 0, 10000, -1, EINVAL, -1);
++	test_nm_utils_ascii_str_to_int64_do (" 1x ", 10, 0, 10000, -1, EINVAL, -1);
++	test_nm_utils_ascii_str_to_int64_do (" 10000 ", 10, 0, 10000, -1, 0, 10000);
++	test_nm_utils_ascii_str_to_int64_do (" 10001 ", 10, 0, 10000, -1, ERANGE, -1);
++	test_nm_utils_ascii_str_to_int64_do (" 0xFF ", 16, 0, 10000, -1, 0, 255);
++	test_nm_utils_ascii_str_to_int64_do (" FF ", 16, 0, 10000, -1, 0, 255);
++	test_nm_utils_ascii_str_to_int64_do (" FF ", 10, 0, 10000, -2, EINVAL, -2);
++	test_nm_utils_ascii_str_to_int64_do (" 9223372036854775807 ", 10, 0, G_MAXINT64, -2, 0, G_MAXINT64);
++	test_nm_utils_ascii_str_to_int64_do (" 0x7FFFFFFFFFFFFFFF ", 16, 0, G_MAXINT64, -2, 0, G_MAXINT64);
++	test_nm_utils_ascii_str_to_int64_do (" 7FFFFFFFFFFFFFFF ", 16, 0, G_MAXINT64, -2, 0, G_MAXINT64);
++	test_nm_utils_ascii_str_to_int64_do (" 9223372036854775808 ", 10, 0, G_MAXINT64, -2, ERANGE, -2);
++	test_nm_utils_ascii_str_to_int64_do (" -9223372036854775808 ", 10, G_MININT64, 0, -2, 0, G_MININT64);
++	test_nm_utils_ascii_str_to_int64_do (" -9223372036854775808 ", 10, G_MININT64+1, 0, -2, ERANGE, -2);
++	test_nm_utils_ascii_str_to_int64_do (" -9223372036854775809 ", 10, G_MININT64, 0, -2, ERANGE, -2);
++	test_nm_utils_ascii_str_to_int64_do ("\r\n\t10000\t\n\t\n", 10, 0, 10000, -1, 0, 10000);
++}
++
++/*******************************************/
++
++int
++main (int argc, char **argv)
++{
++	g_test_init (&argc, &argv, NULL);
++
++	g_type_init ();
++
++	g_test_add_func ("/general/nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
++
++	return g_test_run ();
++}
++
+-- 
+1.8.5.3
+
+
+From 04320290b6c1dfb7f6ac0b8c7f9b71cffbc5730b Mon Sep 17 00:00:00 2001
+From: Dan Winship <danw at gnome.org>
+Date: Mon, 14 Oct 2013 10:38:56 -0400
+Subject: [PATCH 03/14] core: add function nm_utils_get_ip_config_method()
+
+Add nm_utils_get_ip_config_method(), which returns the correct
+IP config method for a connection, whether the connection has IP4 and
+IP6 settings objects or not, and use that to keep some more of the
+simplifications from the earlier patch.
+
+This is a partial backport of an upstream commit.
+
+(cherry picked from commit f03635e5ac829d4fc896573f2f3c6969b9d5a2e2)
+---
+ src/NetworkManagerUtils.c | 43 +++++++++++++++++++++++++++++++++++++++++++
+ src/NetworkManagerUtils.h |  3 +++
+ 2 files changed, 46 insertions(+)
+
+diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
+index d0937fb..bac5322 100644
+--- a/src/NetworkManagerUtils.c
++++ b/src/NetworkManagerUtils.c
+@@ -576,6 +576,49 @@ get_new_connection_name (const GSList *existing,
+ 	return cname;
+ }
+ 
++const char *
++nm_utils_get_ip_config_method (NMConnection *connection,
++                               GType         ip_setting_type)
++{
++	NMSettingConnection *s_con;
++	NMSettingIP4Config *s_ip4;
++	NMSettingIP6Config *s_ip6;
++	const char *method;
++
++	s_con = nm_connection_get_setting_connection (connection);
++
++	if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) {
++		g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
++
++		if (nm_setting_connection_get_master (s_con))
++			return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
++		else {
++			s_ip4 = nm_connection_get_setting_ip4_config (connection);
++			g_return_val_if_fail (s_ip4 != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
++			method = nm_setting_ip4_config_get_method (s_ip4);
++			g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
++
++			return method;
++		}
++
++	} else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) {
++		g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
++
++		if (nm_setting_connection_get_master (s_con))
++			return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
++		else {
++			s_ip6 = nm_connection_get_setting_ip6_config (connection);
++			g_return_val_if_fail (s_ip6 != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
++			method = nm_setting_ip6_config_get_method (s_ip6);
++			g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
++
++			return method;
++		}
++
++	} else
++		g_assert_not_reached ();
++}
++
+ void
+ nm_utils_complete_generic (NMConnection *connection,
+                            const char *ctype,
+diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
+index 0904c51..a15f74f 100644
+--- a/src/NetworkManagerUtils.h
++++ b/src/NetworkManagerUtils.h
+@@ -82,6 +82,9 @@ gboolean nm_utils_get_proc_sys_net_value_with_bounds (const char *path,
+                                                       gint32 valid_min,
+                                                       gint32 valid_max);
+ 
++const char *nm_utils_get_ip_config_method (NMConnection *connection,
++                                           GType         ip_setting_type);
++
+ void nm_utils_complete_generic (NMConnection *connection,
+                                 const char *ctype,
+                                 const GSList *existing,
+-- 
+1.8.5.3
+
+
+From 32602714f0e06301d68be71daf38c42215b46944 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Mon, 6 Jan 2014 19:59:17 +0100
+Subject: [PATCH 04/14] core: add function nm_platform_sysctl_get_int32()
+
+This is a partial backport of an upstream commit.
+
+(cherry picked from commit 2b87dbb2a990fa6aee9ca1e431a2d1ee2af453e9)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/platform/nm-platform.c | 32 ++++++++++++++++++++++++++++++++
+ src/platform/nm-platform.h |  1 +
+ 2 files changed, 33 insertions(+)
+
+diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
+index 06cdec0..cf21e5c 100644
+--- a/src/platform/nm-platform.c
++++ b/src/platform/nm-platform.c
+@@ -27,6 +27,7 @@
+ #include <netlink/route/addr.h>
+ 
+ #include "nm-platform.h"
++#include "NetworkManagerUtils.h"
+ #include "nm-logging.h"
+ #include "nm-enum-types.h"
+ 
+@@ -243,6 +244,37 @@ nm_platform_sysctl_get (const char *path)
+ 	return klass->sysctl_get (platform, path);
+ }
+ 
++/**
++ * nm_platform_sysctl_get_int32:
++ * @path: Absolute path to sysctl
++ * @fallback: default value, if the content of path could not be read
++ * as decimal integer.
++ *
++ * Returns: contents of the sysctl file parsed as s32 integer, or
++ * @fallback on error. Also, on error, @errno will be set to a non-zero
++ * value.
++ */
++gint32
++nm_platform_sysctl_get_int32 (const char *path, gint32 fallback)
++{
++	char *value = NULL;
++	gint32 ret;
++
++	g_return_val_if_fail (path, fallback);
++
++	if (path)
++		value = nm_platform_sysctl_get (path);
++
++	if (!value) {
++		errno = EINVAL;
++		return fallback;
++	}
++
++	ret = nm_utils_ascii_str_to_int64 (value, 10, G_MININT32, G_MAXINT32, fallback);
++	g_free (value);
++	return ret;
++}
++
+ /******************************************************************/
+ 
+ /**
+diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
+index 58f51ce..d4864d0 100644
+--- a/src/platform/nm-platform.h
++++ b/src/platform/nm-platform.h
+@@ -349,6 +349,7 @@ void nm_platform_query_devices (void);
+ 
+ gboolean nm_platform_sysctl_set (const char *path, const char *value);
+ char *nm_platform_sysctl_get (const char *path);
++gint32 nm_platform_sysctl_get_int32 (const char *path, gint32 fallback);
+ 
+ GArray *nm_platform_link_get_all (void);
+ gboolean nm_platform_dummy_add (const char *name);
+-- 
+1.8.5.3
+
+
+From e72164b28a31355fb71b798b6197113d64f94335 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Fri, 13 Dec 2013 20:12:57 +0100
+Subject: [PATCH 05/14] core: fix NMDevice.ip6_use_tempaddr to avoid buffer
+ overrun for zero char in config file
+
+(cherry picked from commit bb9deec9ef98ede632e69f8e5a6e017070f714a8)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 92de1db..e32050f 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -3272,7 +3272,6 @@ static int
+ ip6_use_tempaddr (void)
+ {
+ 	char *contents = NULL;
+-	gsize len = 0;
+ 	const char *group_name = "[forged_group]\n";
+ 	char *sysctl_data = NULL;
+ 	GKeyFile *keyfile;
+@@ -3280,15 +3279,15 @@ ip6_use_tempaddr (void)
+ 	int tmp, ret = -1;
+ 
+ 	/* Read file contents to a string. */
+-	if (!g_file_get_contents ("/etc/sysctl.conf", &contents, &len, NULL))
+-		if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, &len, NULL))
++	if (!g_file_get_contents ("/etc/sysctl.conf", &contents, NULL, NULL))
++		if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, NULL, NULL))
+ 			return -1;
+ 
+ 	/* Prepend a group so that we can use GKeyFile parser. */
+ 	sysctl_data = g_strdup_printf ("%s%s", group_name, contents);
+ 
+ 	keyfile = g_key_file_new ();
+-	if (!g_key_file_load_from_data (keyfile, sysctl_data, len + strlen (group_name), G_KEY_FILE_NONE, NULL))
++	if (!g_key_file_load_from_data (keyfile, sysctl_data, -1, G_KEY_FILE_NONE, NULL))
+ 		goto done;
+ 
+ 	tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error);
+-- 
+1.8.5.3
+
+
+From 3edb66027825c327ba0b24339505d99c0432623d Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Thu, 7 Nov 2013 20:38:08 +0100
+Subject: [PATCH 06/14] core: IPv6 mode link-local must not behave like auto
+ (bgo#706618, bgo#707155)
+
+In act_stage3_ip6_config_start, for IPv6 mode link-local, we check
+if there is already an IPv6 address configured. If yes, we are
+already done.
+
+For now, as current workaround, if the LL does not exist, we
+NM_ACT_STAGE_RETURN_STOP.
+
+Later, we will POSTPONE and wait a timeout until we see a LL address
+that is no longer TENTATIVE. The same should be done for method auto,
+so that the device is usable to send router solitations (bgo#707155).
+
+https://bugzilla.gnome.org/show_bug.cgi?id=707155
+https://bugzilla.gnome.org/show_bug.cgi?id=706618
+
+(cherry picked from commit 10bd060076befc4b711125a19831d323f9ea8595)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c | 30 ++++++++++++++++++++++++++++--
+ 1 file changed, 28 insertions(+), 2 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index e32050f..152814c 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -3265,6 +3265,31 @@ addrconf6_cleanup (NMDevice *self)
+ 
+ /******************************************/
+ 
++static int
++linklocal6_start (NMDevice *self)
++{
++	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
++	int i;
++
++	if (priv->ip6_config) {
++		for (i = 0; i < nm_ip6_config_get_num_addresses (priv->ip6_config); i++) {
++			const NMPlatformIP6Address *addr = nm_ip6_config_get_address (priv->ip6_config, i);
++
++			if (addr->plen == 128 && IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
++				/* FIXME: only accept the address if it is no longer TENTATIVE */
++				return NM_ACT_STAGE_RETURN_SUCCESS;
++			}
++		}
++	}
++
++	/* FIXME: we should NM_ACT_STAGE_RETURN_POSTPONE and wait until with timeout
++	 * we get a link local address that is no longer TENTATIVE. */
++	nm_log_warn (LOGD_DEVICE, "[%s] starting IPv6 with mode 'link-local', but the device has no link-local addresses configured.",
++	             nm_device_get_iface (self));
++
++	return NM_ACT_STAGE_RETURN_STOP;
++}
++
+ /* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or
+  * /lib/sysctl.d/sysctl.conf
+  */
+@@ -3387,13 +3412,14 @@ act_stage3_ip6_config_start (NMDevice *self,
+ 
+ 	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
+ 
+-	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
+-	    || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
++	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
+ 		if (!addrconf6_start (self)) {
+ 			/* IPv6 might be disabled; allow IPv4 to proceed */
+ 			ret = NM_ACT_STAGE_RETURN_STOP;
+ 		} else
+ 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
++	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
++		ret = linklocal6_start (self);
+ 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
+ 		/* Router advertisements shouldn't be used in pure DHCP mode */
+ 		if (priv->ip6_accept_ra_path)
+-- 
+1.8.5.3
+
+
+From e1d059d1a2d359504900d14e9d8acf0307187e6c Mon Sep 17 00:00:00 2001
+From: Dan Williams <dcbw at redhat.com>
+Date: Thu, 7 Nov 2013 21:30:25 -0600
+Subject: [PATCH 07/14] core: fix hanlding of IPv6LL address if interface
+ already has one
+
+act_stage3_ip6_config_start() expects a non-NULL NMIP6Config if the
+sub-method returns NM_ACT_STAGE_RETURN_SUCCESS.
+
+(cherry picked from commit 72063064567312ff6412c6d9996e8a6684df3f1d)
+---
+ src/devices/nm-device.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 152814c..4384f6a 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -3420,6 +3420,11 @@ act_stage3_ip6_config_start (NMDevice *self,
+ 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+ 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
+ 		ret = linklocal6_start (self);
++		if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
++			/* New blank config; LL address is already in priv->ext_ip6_config */
++			*out_config = nm_ip6_config_new ();
++			g_assert (*out_config);
++		}
+ 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
+ 		/* Router advertisements shouldn't be used in pure DHCP mode */
+ 		if (priv->ip6_accept_ra_path)
+-- 
+1.8.5.3
+
+
+From 82abe50b59f09d80507d286d7259eb9038708363 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Thu, 7 Nov 2013 22:59:43 +0100
+Subject: [PATCH 08/14] core: wait for IPv6 link local address with method
+ 'auto' and 'link-local' (bgo#707155)
+
+With the methods 'auto' and 'link-local' we check now, that the device
+has a usable IPv6 LL address configured (after DAD, no longer tentative).
+
+We wait for up to 5 seconds, for a suitable LL address to appear.
+Currently, if the address does not get ready, we don't create one and
+IPv6 configuration fails.
+
+This is relevant for the methods 'link-local' and 'auto'. In the latter
+case, because we cannot send router solitations without link local
+address.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=707155
+
+(cherry picked from commit c4a087c36d34e584a48b6b866fc3c12f248f8512)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c | 170 +++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 140 insertions(+), 30 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 4384f6a..8c20a0d 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -269,6 +269,8 @@ typedef struct {
+ 	/* IP6 config from autoconf */
+ 	NMIP6Config *  ac_ip6_config;
+ 
++	guint          linklocal6_timeout_id;
++
+ 	char *         ip6_accept_ra_path;
+ 	gint32         ip6_accept_ra_save;
+ 
+@@ -345,6 +347,8 @@ static void nm_device_queued_ip_config_change_clear (NMDevice *self);
+ static void update_ip_config (NMDevice *self);
+ static void device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformReason reason, gpointer user_data);
+ 
++static void addrconf6_start_with_link_ready (NMDevice *self);
++
+ static const char const *platform_ip_signals[] = {
+ 	NM_PLATFORM_IP4_ADDRESS_ADDED,
+ 	NM_PLATFORM_IP4_ADDRESS_CHANGED,
+@@ -3092,6 +3096,104 @@ dhcp6_start (NMDevice *self,
+ 
+ /******************************************/
+ 
++static gboolean
++linklocal6_config_is_ready (const NMIP6Config *ip6_config)
++{
++	int i;
++
++	if (!ip6_config)
++		return FALSE;
++
++	for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
++		const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ip6_config, i);
++
++		if (IN6_IS_ADDR_LINKLOCAL (&addr->address) &&
++		    !(addr->flags & IFA_F_TENTATIVE))
++			return TRUE;
++	}
++
++	return FALSE;
++}
++
++static void
++linklocal6_cleanup (NMDevice *self)
++{
++	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
++
++	if (priv->linklocal6_timeout_id) {
++		g_source_remove (priv->linklocal6_timeout_id);
++		priv->linklocal6_timeout_id = 0;
++	}
++}
++
++static gboolean
++linklocal6_timeout_cb (gpointer user_data)
++{
++	NMDevice *self = user_data;
++
++	linklocal6_cleanup (self);
++
++	nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: waiting for link-local addresses failed due to timeout",
++	             nm_device_get_iface (self));
++
++	nm_device_activate_schedule_ip6_config_timeout (self);
++	return G_SOURCE_REMOVE;
++}
++
++static void
++linklocal6_complete (NMDevice *self)
++{
++	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
++	NMConnection *connection;
++	const char *method;
++
++	g_assert (priv->linklocal6_timeout_id);
++	g_assert (linklocal6_config_is_ready (priv->ip6_config));
++
++	linklocal6_cleanup (self);
++
++	connection = nm_device_get_connection (self);
++	g_assert (connection);
++
++	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
++
++	nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: waiting for link-local addresses successful, continue with method %s",
++	             nm_device_get_iface (self), method);
++
++	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0)
++		addrconf6_start_with_link_ready (self);
++	else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
++		nm_device_activate_schedule_ip6_config_result (self);
++	else
++		g_return_if_fail (FALSE);
++}
++
++static NMActStageReturn
++linklocal6_start (NMDevice *self)
++{
++	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
++	NMConnection *connection;
++	const char *method;
++
++	linklocal6_cleanup (self);
++
++	if (linklocal6_config_is_ready (priv->ip6_config))
++		return NM_ACT_STAGE_RETURN_SUCCESS;
++
++	connection = nm_device_get_connection (self);
++	g_assert (connection);
++
++	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
++	nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.",
++	            nm_device_get_iface (self), method);
++
++	priv->linklocal6_timeout_id = g_timeout_add_seconds (5, linklocal6_timeout_cb, self);
++
++	return NM_ACT_STAGE_RETURN_POSTPONE;
++}
++
++/******************************************/
++
+ static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release);
+ 
+ static void
+@@ -3218,6 +3320,7 @@ addrconf6_start (NMDevice *self)
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ 	NMConnection *connection;
+ 	const char *ip_iface = nm_device_get_ip_iface (self);
++	NMActStageReturn ret;
+ 
+ 	connection = nm_device_get_connection (self);
+ 	g_assert (connection);
+@@ -3229,23 +3332,40 @@ addrconf6_start (NMDevice *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, "(%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);
++	/* ensure link local is ready... */
++	ret = linklocal6_start (self);
++
++	if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
++		addrconf6_start_with_link_ready (self);
++	else
++		g_return_val_if_fail (ret == NM_ACT_STAGE_RETURN_POSTPONE, TRUE);
++
++	return TRUE;
++}
++
++static void
++addrconf6_start_with_link_ready (NMDevice *self)
++{
++	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
++
++	g_assert (priv->rdisc);
+ 
+ 	/* FIXME: what if interface has no lladdr, like PPP? */
+ 	if (priv->hw_addr_len)
+ 		nm_rdisc_set_lladdr (priv->rdisc, (const char *) priv->hw_addr, priv->hw_addr_len);
+ 
+-	nm_rdisc_start (priv->rdisc);
++	nm_platform_sysctl_set (priv->ip6_accept_ra_path, "0");
+ 
+-	return TRUE;
++	priv->rdisc_config_changed_sigid = g_signal_connect (priv->rdisc, NM_RDISC_CONFIG_CHANGED,
++	                                                     G_CALLBACK (rdisc_config_changed), self);
++
++	nm_rdisc_start (priv->rdisc);
+ }
+ 
+ static void
+@@ -3265,31 +3385,6 @@ addrconf6_cleanup (NMDevice *self)
+ 
+ /******************************************/
+ 
+-static int
+-linklocal6_start (NMDevice *self)
+-{
+-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+-	int i;
+-
+-	if (priv->ip6_config) {
+-		for (i = 0; i < nm_ip6_config_get_num_addresses (priv->ip6_config); i++) {
+-			const NMPlatformIP6Address *addr = nm_ip6_config_get_address (priv->ip6_config, i);
+-
+-			if (addr->plen == 128 && IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+-				/* FIXME: only accept the address if it is no longer TENTATIVE */
+-				return NM_ACT_STAGE_RETURN_SUCCESS;
+-			}
+-		}
+-	}
+-
+-	/* FIXME: we should NM_ACT_STAGE_RETURN_POSTPONE and wait until with timeout
+-	 * we get a link local address that is no longer TENTATIVE. */
+-	nm_log_warn (LOGD_DEVICE, "[%s] starting IPv6 with mode 'link-local', but the device has no link-local addresses configured.",
+-	             nm_device_get_iface (self));
+-
+-	return NM_ACT_STAGE_RETURN_STOP;
+-}
+-
+ /* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or
+  * /lib/sysctl.d/sysctl.conf
+  */
+@@ -4397,6 +4492,7 @@ nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
+ 
+ 	dhcp4_cleanup (self, TRUE, FALSE);
+ 	dhcp6_cleanup (self, TRUE, FALSE);
++	linklocal6_cleanup (self);
+ 	addrconf6_cleanup (self);
+ 	dnsmasq_cleanup (self);
+ 	aipd_cleanup (self);
+@@ -5179,6 +5275,7 @@ dispose (GObject *object)
+ 	/* Clean up and stop DHCP */
+ 	dhcp4_cleanup (self, deconfigure, FALSE);
+ 	dhcp6_cleanup (self, deconfigure, FALSE);
++	linklocal6_cleanup (self);
+ 	addrconf6_cleanup (self);
+ 	dnsmasq_cleanup (self);
+ 
+@@ -6346,6 +6443,7 @@ update_ip_config (NMDevice *self)
+ {
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ 	int ifindex;
++	gboolean linklocal6_just_completed = FALSE;
+ 
+ 	ifindex = nm_device_get_ip_ifindex (self);
+ 	if (!ifindex)
+@@ -6367,6 +6465,11 @@ update_ip_config (NMDevice *self)
+ 	g_clear_object (&priv->ext_ip6_config);
+ 	priv->ext_ip6_config = nm_ip6_config_capture (ifindex);
+ 	if (priv->ext_ip6_config) {
++
++		/* Check this before modifying ext_ip6_config */
++		linklocal6_just_completed = priv->linklocal6_timeout_id &&
++		                            linklocal6_config_is_ready (priv->ext_ip6_config);
++
+ 		if (priv->ac_ip6_config)
+ 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config);
+ 		if (priv->dhcp6_ip6_config)
+@@ -6376,6 +6479,13 @@ update_ip_config (NMDevice *self)
+ 
+ 		ip6_config_merge_and_apply (self, FALSE, NULL);
+ 	}
++
++	if (linklocal6_just_completed) {
++		/* linklocal6 is ready now, do the state transition... we are also
++		 * invoked as g_idle_add, so no problems with reentrance doing it now.
++		 */
++		linklocal6_complete (self);
++	}
+ }
+ 
+ static gboolean
+-- 
+1.8.5.3
+
+
+From 6133195b274165a8494edcf58560e2e96b39a89d Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Mon, 6 Jan 2014 21:05:00 +0100
+Subject: [PATCH 09/14] core/rdisc: limit the number of autoconf addresses to
+ 'max_addresses'
+
+NetworkManager uses the sysctl value 'max_addresses' as the kernel does.
+There is however a difference in what addresses are taken into account.
+The kernel counts all addresses on the interface (including temporary,
+private addresses and user configured ones).
+NM instead only limits the number of public autoconf addresses to
+'max_addresses'. This is because it is difficult for NM to count all
+addresses (which can come from different sources) and it is not
+necessarily a more logical behavior. Only be aware, that NM uses
+the same config value as the kernel, but counts differently.
+
+Especially, the kernel might reach the limit earlier then NM in the
+presence of temporary addresses or addresses not from SLAAC.
+
+Note, that the kernel uses 'max_addresses' only to limit public, autoconf
+addresses. So this limit does not affect NM adding as many addresses as
+it wants.
+
+(cherry picked from commit 84dc64c8affd658077fa3967d42374d6c3a2951c)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c   | 23 ++++++++++++++++++++++-
+ src/rdisc/nm-fake-rdisc.c |  3 ++-
+ src/rdisc/nm-fake-rdisc.h |  2 +-
+ src/rdisc/nm-lndp-rdisc.c | 10 +++++++++-
+ src/rdisc/nm-lndp-rdisc.h |  2 +-
+ src/rdisc/nm-rdisc.h      |  1 +
+ src/rdisc/tests/rdisc.c   |  4 ++--
+ 7 files changed, 38 insertions(+), 7 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 8c20a0d..e8ed2ba 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -449,6 +449,21 @@ update_ip6_privacy_save (NMDevice *self)
+ 	}
+ }
+ 
++static gint32
++sysctl_get_ipv6_max_addresses (const char *dev)
++{
++	gint32 max_addresses = 16;
++	char *path;
++
++	g_return_val_if_fail (dev && *dev, max_addresses);
++
++	path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/max_addresses", dev);
++	max_addresses = nm_platform_sysctl_get_int32 (path, max_addresses);
++	g_free (path);
++
++	return max_addresses;
++}
++
+ /*
+  * Get driver info from SIOCETHTOOL ioctl() for 'iface'
+  * Returns driver and firmware versions to 'driver_version and' 'firmware_version'
+@@ -3225,6 +3240,11 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
+ 		/* Rebuild address list from router discovery cache. */
+ 		nm_ip6_config_reset_addresses (priv->ac_ip6_config);
+ 
++		/* rdisc->addresses contains at most max_addresses entries.
++		 * This is different from what the kernel does, which
++		 * also counts static and temporary addresses when checking
++		 * max_addresses.
++		 **/
+ 		for (i = 0; i < rdisc->addresses->len; i++) {
+ 			NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+ 			NMPlatformIP6Address address;
+@@ -3331,7 +3351,8 @@ addrconf6_start (NMDevice *self)
+ 		priv->ac_ip6_config = NULL;
+ 	}
+ 
+-	priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface);
++	priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface,
++	                                 sysctl_get_ipv6_max_addresses (ip_iface));
+ 
+ 	if (!priv->rdisc) {
+ 		nm_log_err (LOGD_IP6, "(%s): failed to start router discovery.", ip_iface);
+diff --git a/src/rdisc/nm-fake-rdisc.c b/src/rdisc/nm-fake-rdisc.c
+index f39c5a2..38faa50 100644
+--- a/src/rdisc/nm-fake-rdisc.c
++++ b/src/rdisc/nm-fake-rdisc.c
+@@ -36,7 +36,7 @@ G_DEFINE_TYPE (NMFakeRDisc, nm_fake_rdisc, NM_TYPE_RDISC)
+ /******************************************************************/
+ 
+ NMRDisc *
+-nm_fake_rdisc_new (int ifindex, const char *ifname)
++nm_fake_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses)
+ {
+ 	NMRDisc *rdisc = g_object_new (NM_TYPE_FAKE_RDISC, NULL);
+ 
+@@ -44,6 +44,7 @@ nm_fake_rdisc_new (int ifindex, const char *ifname)
+ 
+ 	rdisc->ifindex = ifindex;
+ 	rdisc->ifname = g_strdup (ifname);
++	rdisc->max_addresses = max_addresses;
+ 
+ 	return rdisc;
+ }
+diff --git a/src/rdisc/nm-fake-rdisc.h b/src/rdisc/nm-fake-rdisc.h
+index 248283b..cff9ee4 100644
+--- a/src/rdisc/nm-fake-rdisc.h
++++ b/src/rdisc/nm-fake-rdisc.h
+@@ -44,6 +44,6 @@ typedef struct {
+ 
+ GType nm_fake_rdisc_get_type (void);
+ 
+-NMRDisc *nm_fake_rdisc_new (int ifindex, const char *ifname);
++NMRDisc *nm_fake_rdisc_new (int ifindex, const char *ifname, gint32 max_addressses);
+ 
+ #endif /* NM_FAKE_RDISC_H */
+diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c
+index c9bb2cf..c328f99 100644
+--- a/src/rdisc/nm-lndp-rdisc.c
++++ b/src/rdisc/nm-lndp-rdisc.c
+@@ -48,7 +48,7 @@ G_DEFINE_TYPE (NMLNDPRDisc, nm_lndp_rdisc, NM_TYPE_RDISC)
+ /******************************************************************/
+ 
+ NMRDisc *
+-nm_lndp_rdisc_new (int ifindex, const char *ifname)
++nm_lndp_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses)
+ {
+ 	NMRDisc *rdisc;
+ 	NMLNDPRDiscPrivate *priv;
+@@ -59,6 +59,7 @@ nm_lndp_rdisc_new (int ifindex, const char *ifname)
+ 
+ 	rdisc->ifindex = ifindex;
+ 	rdisc->ifname = g_strdup (ifname);
++	rdisc->max_addresses = max_addresses;
+ 
+ 	priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+ 	error = ndp_open (&priv->ndp);
+@@ -113,6 +114,13 @@ add_address (NMRDisc *rdisc, const NMRDiscAddress *new)
+ 		}
+ 	}
+ 
++	/* we create at most max_addresses autoconf addresses. This is different from
++	 * what the kernel does, because it considers *all* addresses (including
++	 * static and other temporary addresses).
++	 **/
++	if (rdisc->max_addresses && rdisc->addresses->len >= rdisc->max_addresses)
++		return FALSE;
++
+ 	g_array_insert_val (rdisc->addresses, i, *new);
+ 	return TRUE;
+ }
+diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h
+index eb6a1df..30d53db 100644
+--- a/src/rdisc/nm-lndp-rdisc.h
++++ b/src/rdisc/nm-lndp-rdisc.h
+@@ -44,6 +44,6 @@ typedef struct {
+ 
+ GType nm_lndp_rdisc_get_type (void);
+ 
+-NMRDisc *nm_lndp_rdisc_new (int ifindex, const char *ifname);
++NMRDisc *nm_lndp_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses);
+ 
+ #endif /* NM_LNDP_RDISC_H */
+diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h
+index c645bda..219b6b2 100644
+--- a/src/rdisc/nm-rdisc.h
++++ b/src/rdisc/nm-rdisc.h
+@@ -106,6 +106,7 @@ typedef struct {
+ 	int ifindex;
+ 	char *ifname;
+ 	GBytes *lladdr;
++	gint32 max_addresses;
+ 
+ 	NMRDiscDHCPLevel dhcp_level;
+ 	GArray *gateways;
+diff --git a/src/rdisc/tests/rdisc.c b/src/rdisc/tests/rdisc.c
+index 680bb2e..e8e2a9f 100644
+--- a/src/rdisc/tests/rdisc.c
++++ b/src/rdisc/tests/rdisc.c
+@@ -12,7 +12,7 @@ main (int argc, char **argv)
+ {
+ 	GMainLoop *loop;
+ 	NMRDisc *rdisc;
+-	NMRDisc *(*new) (int ifindex, const char *ifname) = nm_lndp_rdisc_new;
++	NMRDisc *(*new) (int ifindex, const char *ifname, gint32 max_addresses) = nm_lndp_rdisc_new;
+ 	int ifindex = 1;
+ 	char ifname[IF_NAMESIZE];
+ 	char mac[6] = { 0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+@@ -34,7 +34,7 @@ main (int argc, char **argv)
+ 		}
+ 	}
+ 
+-	rdisc = new (ifindex, ifname);
++	rdisc = new (ifindex, ifname, 0);
+ 	if (!rdisc)
+ 		return EXIT_FAILURE;
+ 
+-- 
+1.8.5.3
+
+
+From dc11f1ca2ee02f986b714f1def9fe64e1de14f35 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Fri, 3 Jan 2014 16:07:36 +0100
+Subject: [PATCH 10/14] core/platform: workaround new address flag in
+ address_to_string
+
+The kernel and libnl adds two new flags IFA_F_MANAGETEMPADDR
+and IFA_F_NOPREFIXROUTE. Older versions of libnl do not recognize
+this flag, so add a workaround to nm_platform_ip6_address_to_string()
+to show "mngtmpaddr" and "noprefixroute", respectively.
+
+Also, add function nm_platform_check_support_libnl_extended_ifa_flags()
+that checks whether libnl supports extended ifa_flags that were
+added recently.
+
+Extended flags and the two ifa-flags above were added to libnl in close
+succession.
+
+(cherry picked from commit 2bc61d1ad3278d4fc38d17bd6178e7e304c6339a)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/platform/nm-platform.c | 41 ++++++++++++++++++++++++++++++++++++++++-
+ src/platform/nm-platform.h |  2 ++
+ 2 files changed, 42 insertions(+), 1 deletion(-)
+
+diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
+index cf21e5c..92c9c30 100644
+--- a/src/platform/nm-platform.c
++++ b/src/platform/nm-platform.c
+@@ -31,6 +31,14 @@
+ #include "nm-logging.h"
+ #include "nm-enum-types.h"
+ 
++/* workaround for older libnl version, that does not define these flags. */
++#ifndef IFA_F_MANAGETEMPADDR
++#define IFA_F_MANAGETEMPADDR 0x100
++#endif
++#ifndef IFA_F_NOPREFIXROUTE
++#define IFA_F_NOPREFIXROUTE 0x200
++#endif
++
+ #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
+ 
+ #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate))
+@@ -196,6 +204,22 @@ reset_error (void)
+ 	platform->error = NM_PLATFORM_ERROR_NONE;
+ }
+ 
++#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr"
++#define IFA_F_NOPREFIXROUTE_STR "noprefixroute"
++gboolean
++nm_platform_check_support_libnl_extended_ifa_flags ()
++{
++	static int supported = -1;
++
++	/* support for extended ifa-flags was added together
++	 * with the IFA_F_MANAGETEMPADDR flag. So, check if libnl
++	 * is able to parse this flag. */
++	if (supported == -1)
++		supported = rtnl_addr_str2flags (IFA_F_MANAGETEMPADDR_STR) == IFA_F_MANAGETEMPADDR;
++
++	return supported;
++}
++
+ /******************************************************************/
+ 
+ /**
+@@ -1688,7 +1712,22 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address)
+ 	inet_ntop (AF_INET6, &address->address, s_address, sizeof (s_address));
+ 	s_dev = address->ifindex > 0 ? nm_platform_link_get_name (address->ifindex) : NULL;
+ 
+-	rtnl_addr_flags2str(address->flags, s_flags, sizeof(s_flags));
++	rtnl_addr_flags2str(address->flags, s_flags, sizeof (s_flags));
++
++	/* There are two recent flags IFA_F_MANAGETEMPADDR and IFA_F_NOPREFIXROUTE.
++	 * If libnl does not yet support them, add them by hand.
++	 * These two flags were introduced together with the extended ifa_flags,
++	 * so, check for that.
++	 **/
++	if ((address->flags && IFA_F_MANAGETEMPADDR) & !nm_platform_check_support_libnl_extended_ifa_flags ()) {
++		strncat (s_flags, s_flags[0] ? "," IFA_F_MANAGETEMPADDR_STR : IFA_F_MANAGETEMPADDR_STR,
++		         sizeof (s_flags) - strlen (s_flags) - 1);
++	}
++	if ((address->flags && IFA_F_NOPREFIXROUTE) & !nm_platform_check_support_libnl_extended_ifa_flags ()) {
++		strncat (s_flags, s_flags[0] ? "," IFA_F_NOPREFIXROUTE_STR : IFA_F_NOPREFIXROUTE_STR,
++		         sizeof (s_flags) - strlen (s_flags) - 1);
++	}
++
+ 	str_flags = s_flags[0] ? g_strconcat (" flags ", s_flags, NULL) : NULL;
+ 
+ 	g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u dev %s%s",
+diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
+index d4864d0..26a6737 100644
+--- a/src/platform/nm-platform.h
++++ b/src/platform/nm-platform.h
+@@ -444,6 +444,8 @@ int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatform
+ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b);
+ int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
+ 
++gboolean nm_platform_check_support_libnl_extended_ifa_flags (void);
++
+ #define auto_g_free __attribute__((cleanup(put_g_free)))
+ static void __attribute__((unused))
+ put_g_free (void *ptr)
+-- 
+1.8.5.3
+
+
+From 57af43c6118d8d62a8671919833864e2d5e3a270 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Tue, 7 Jan 2014 17:21:12 +0100
+Subject: [PATCH 11/14] core/platform: add
+ check_support_kernel_extended_ifa_flags function
+
+The kernel adds a new capability to allow user space to manage
+temporary IPv6 addresses. We need to detect this capability
+to act differently, depending on whether NM has an older kernel
+at hand.
+
+This capability got introduced together when extending the
+ifa_flags to 32 bit. So, we can check the netlink message,
+whether we have such an nl attribute at hand.
+
+(cherry picked from commit 7841f9ea0a4efdcb4540628cf65d7d9356b748f7)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/platform/nm-linux-platform.c | 57 ++++++++++++++++++++++++++++++++++++++++
+ src/platform/nm-platform.c       | 11 ++++++++
+ src/platform/nm-platform.h       |  3 +++
+ 3 files changed, 71 insertions(+)
+
+diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
+index 1a44c40..b60f101 100644
+--- a/src/platform/nm-linux-platform.c
++++ b/src/platform/nm-linux-platform.c
+@@ -65,6 +65,8 @@ typedef struct {
+ 
+ 	GUdevClient *udev_client;
+ 	GHashTable *udev_devices;
++
++	int support_kernel_extended_ifa_flags;
+ } NMLinuxPlatformPrivate;
+ 
+ #define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate))
+@@ -427,6 +429,45 @@ ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *s
+ 
+ /******************************************************************/
+ 
++static void
++_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg)
++{
++	struct nlmsghdr *msg_hdr = nlmsg_hdr (msg);
++
++	g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0);
++	g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR);
++
++	/* the extended address flags are only set for AF_INET6 */
++	if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6)
++		return;
++
++	/* see if the nl_msg contains the IFA_FLAGS attribute. If it does,
++	 * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR
++	 * and IFA_F_NOPREFIXROUTE (they were added together).
++	 **/
++	priv->support_kernel_extended_ifa_flags =
++	    nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */)
++	    ? 1 : -1;
++}
++
++static gboolean
++check_support_kernel_extended_ifa_flags (NMPlatform *platform)
++{
++	NMLinuxPlatformPrivate *priv;
++
++	g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
++
++	priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
++
++	if (priv->support_kernel_extended_ifa_flags == 0) {
++		g_warn_if_reached ();
++		priv->support_kernel_extended_ifa_flags = -1;
++	}
++
++	return priv->support_kernel_extended_ifa_flags > 0;
++}
++
++
+ /* Object type specific utilities */
+ 
+ static const char *
+@@ -1195,6 +1236,14 @@ event_notification (struct nl_msg *msg, gpointer user_data)
+ 	int nle;
+ 
+ 	event = nlmsg_hdr (msg)->nlmsg_type;
++
++	if (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) {
++		/* if kernel support for extended ifa flags is still undecided, use the opportunity
++		 * now and use @msg to decide it. This saves a blocking net link request.
++		 **/
++		_check_support_kernel_extended_ifa_flags_init (priv, msg);
++	}
++
+ 	nl_msg_parse (msg, ref_object, &object);
+ 	g_return_val_if_fail (object, NL_OK);
+ 
+@@ -2744,6 +2793,12 @@ setup (NMPlatform *platform)
+ 	g_list_free (devices);
+ 	g_object_unref (enumerator);
+ 
++	/* request all IPv6 addresses (hopeing that there is at least one), to check for
++	 * the IFA_FLAGS attribute. */
++	nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP);
++	if (nle != 0)
++		nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle));
++
+ 	return TRUE;
+ }
+ 
+@@ -2848,4 +2903,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
+ 	platform_class->ip6_route_delete = ip6_route_delete;
+ 	platform_class->ip4_route_exists = ip4_route_exists;
+ 	platform_class->ip6_route_exists = ip6_route_exists;
++
++	platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags;
+ }
+diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
+index 92c9c30..5871f86 100644
+--- a/src/platform/nm-platform.c
++++ b/src/platform/nm-platform.c
+@@ -220,6 +220,17 @@ nm_platform_check_support_libnl_extended_ifa_flags ()
+ 	return supported;
+ }
+ 
++gboolean
++nm_platform_check_support_kernel_extended_ifa_flags ()
++{
++	g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE);
++
++	if (!klass->check_support_kernel_extended_ifa_flags)
++		return FALSE;
++
++	return klass->check_support_kernel_extended_ifa_flags (platform);
++}
++
+ /******************************************************************/
+ 
+ /**
+diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
+index 26a6737..d5b3672 100644
+--- a/src/platform/nm-platform.h
++++ b/src/platform/nm-platform.h
+@@ -302,6 +302,8 @@ typedef struct {
+ 	gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric);
+ 	gboolean (*ip4_route_exists) (NMPlatform *, int ifindex, in_addr_t network, int plen, int metric);
+ 	gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric);
++
++	gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *);
+ } NMPlatformClass;
+ 
+ /* NMPlatform signals
+@@ -445,6 +447,7 @@ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4R
+ int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
+ 
+ gboolean nm_platform_check_support_libnl_extended_ifa_flags (void);
++gboolean nm_platform_check_support_kernel_extended_ifa_flags (void);
+ 
+ #define auto_g_free __attribute__((cleanup(put_g_free)))
+ static void __attribute__((unused))
+-- 
+1.8.5.3
+
+
+From 399f5d8dfd7a74dbd9043015e15df3fa1644fefb Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Fri, 31 Jan 2014 14:54:31 +0100
+Subject: [PATCH 12/14] core/platform: fix wrong warning log in
+ nm-linux-platform
+
+According to documentation, nl_rtgen_request() returns 0 on success.
+Due to a bug (fixed upstream) in older libnl versions, nl_rtgen_request()
+returns the number of bytes sent, which caused logging although
+succeeding.
+
+(cherry picked from commit 6c2f96421b1c7bfd65032bf4de2a6cfc10b3b262)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/platform/nm-linux-platform.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
+index b60f101..7933678 100644
+--- a/src/platform/nm-linux-platform.c
++++ b/src/platform/nm-linux-platform.c
+@@ -2796,7 +2796,7 @@ setup (NMPlatform *platform)
+ 	/* request all IPv6 addresses (hopeing that there is at least one), to check for
+ 	 * the IFA_FLAGS attribute. */
+ 	nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP);
+-	if (nle != 0)
++	if (nle < 0)
+ 		nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle));
+ 
+ 	return TRUE;
+-- 
+1.8.5.3
+
+
+From b7a960b1287b11ff3db02204a2ff6888efa571f5 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Fri, 3 Jan 2014 17:03:35 +0100
+Subject: [PATCH 13/14] core/rdisc: add autoconf addresses as /64 (instead of
+ /128)
+
+This feature needs support from the kernel and libnl.
+
+If there is no system support, NM acts as before, adding the
+autoconf address as /128. It does so, to prevent the kernel
+from adding a route for this prefix. With system support, we
+add the address as /64 and set the flag IFA_F_NOPREFIXROUTE.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1044590
+https://bugzilla.redhat.com/show_bug.cgi?id=1045118
+
+(cherry picked from commit 39cbe772a67aece69dc30f8f394bba2eea9ca762)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index e8ed2ba..2e870a8 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -36,6 +36,7 @@
+ #include <arpa/inet.h>
+ #include <fcntl.h>
+ #include <linux/if.h>
++#include <netlink/route/addr.h>
+ 
+ #include "libgsystem.h"
+ #include "nm-glib-compat.h"
+@@ -68,6 +69,11 @@
+ #include "nm-config.h"
+ #include "nm-platform.h"
+ 
++/* workaround for older libnl version, that does not define this flag. */
++#ifndef IFA_F_NOPREFIXROUTE
++#define IFA_F_NOPREFIXROUTE 0x200
++#endif
++
+ static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context);
+ 
+ #include "nm-device-glue.h"
+@@ -3218,6 +3224,25 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
+ 	NMConnection *connection;
+ 	int i;
+ 	NMDeviceStateReason reason;
++	static int system_support = -1;
++	guint ifa_flags;
++
++	if (system_support == -1) {
++		/*
++		 * Check, if both libnl and the kernel are recent enough,
++		 * to help user space handling RA. If it's not supported,
++		 * we must add autoconf addresses as /128.
++		 * The reason for /128 is to prevent the kernel from adding
++		 * a prefix route for this address.
++		 **/
++		system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
++		                 nm_platform_check_support_kernel_extended_ifa_flags ();
++	}
++
++	/* without system_support, this flag will be ignored.
++	 * Still, we set it (why not?).
++	 **/
++	ifa_flags = IFA_F_NOPREFIXROUTE;
+ 
+ 	g_return_if_fail (priv->act_request);
+ 	connection = nm_device_get_connection (device);
+@@ -3251,10 +3276,11 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
+ 
+ 			memset (&address, 0, sizeof (address));
+ 			address.address = discovered_address->address;
+-			address.plen = 128;
++			address.plen = system_support ? 64 : 128;
+ 			address.timestamp = discovered_address->timestamp;
+ 			address.lifetime = discovered_address->lifetime;
+ 			address.preferred = discovered_address->preferred;
++			address.flags = ifa_flags;
+ 
+ 			nm_ip6_config_add_address (priv->ac_ip6_config, &address);
+ 		}
+-- 
+1.8.5.3
+
+
+From 14705adcd0a1f87d31623cd3c99978d1a5c0161a Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Fri, 3 Jan 2014 17:03:35 +0100
+Subject: [PATCH 14/14] core/rdisc: add support for IPv6 privacy
+
+Add support for ipv6-private addresses. This feature
+needs support from the kernel and libnl.
+
+If there is no system support, temporary addresses are
+not supported. Log a warning in this case.
+
+Depending on whether ipv6-privacy (use_tempaddr) is enabled,
+we add the address flag IFA_F_MANAGETEMPADDR and the kernel
+will add temporary addresses for us.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=705170
+https://bugzilla.redhat.com/show_bug.cgi?id=1003859
+https://bugzilla.redhat.com/show_bug.cgi?id=1047139
+
+(cherry picked from commit 1dea2714697b8cfe386c6665d7e556d537bebaf0)
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+---
+ src/devices/nm-device.c | 122 +++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 96 insertions(+), 26 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 2e870a8..39146a6 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -69,7 +69,10 @@
+ #include "nm-config.h"
+ #include "nm-platform.h"
+ 
+-/* workaround for older libnl version, that does not define this flag. */
++/* workaround for older libnl version, that does not define these flags. */
++#ifndef IFA_F_MANAGETEMPADDR
++#define IFA_F_MANAGETEMPADDR 0x100
++#endif
+ #ifndef IFA_F_NOPREFIXROUTE
+ #define IFA_F_NOPREFIXROUTE 0x200
+ #endif
+@@ -272,6 +275,7 @@ typedef struct {
+ 
+ 	NMRDisc *      rdisc;
+ 	gulong         rdisc_config_changed_sigid;
++	NMSettingIP6ConfigPrivacy rdisc_use_tempaddr;
+ 	/* IP6 config from autoconf */
+ 	NMIP6Config *  ac_ip6_config;
+ 
+@@ -3218,6 +3222,53 @@ linklocal6_start (NMDevice *self)
+ static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release);
+ 
+ static void
++print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
++{
++	static gint8 warn = 0;
++	static gint8 s_libnl = -1, s_kernel;
++
++	if (warn >= 2)
++		return;
++
++	if (s_libnl == -1) {
++		s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags ();
++		s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags ();
++
++		if (s_libnl && s_kernel) {
++			nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)");
++			warn = 2;
++			return;
++		}
++	}
++
++	if (   use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
++	    && use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) {
++		if (warn == 0) {
++			nm_log_dbg (LOGD_IP6, "%s%s%s %s not support extended IFA_FLAGS (needed by NM for IPv6 private addresses)",
++			                      !s_kernel ? "kernel" : "",
++			                      !s_kernel && !s_libnl ? " and " : "",
++			                      !s_libnl ? "libnl" : "",
++			                      !s_kernel && !s_libnl ? "do" : "does");
++			warn = 1;
++		}
++		return;
++	}
++
++	if (!s_libnl && !s_kernel) {
++		nm_log_warn (LOGD_IP6, "libnl and the kernel do not support extended IFA_FLAGS needed by NM for "
++		                       "IPv6 private addresses. This feature is not available");
++	} else if (!s_libnl) {
++		nm_log_warn (LOGD_IP6, "libnl does not support extended IFA_FLAGS needed by NM for "
++		                       "IPv6 private addresses. This feature is not available");
++	} else if (!s_kernel) {
++		nm_log_warn (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
++		                       "IPv6 private addresses. This feature is not available");
++	}
++
++	warn = 2;
++}
++
++static void
+ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device)
+ {
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+@@ -3231,18 +3282,21 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
+ 		/*
+ 		 * Check, if both libnl and the kernel are recent enough,
+ 		 * to help user space handling RA. If it's not supported,
+-		 * we must add autoconf addresses as /128.
+-		 * The reason for /128 is to prevent the kernel from adding
+-		 * a prefix route for this address.
++		 * we have no ipv6-privacy and must add autoconf addresses
++		 * as /128. The reason for the /128 is to prevent the kernel
++		 * from adding a prefix route for this address.
+ 		 **/
+ 		system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
+ 		                 nm_platform_check_support_kernel_extended_ifa_flags ();
+ 	}
+ 
+-	/* without system_support, this flag will be ignored.
+-	 * Still, we set it (why not?).
++	/* without system_support, these flags will be ignored.
++	 * Still, we set them (why not?).
+ 	 **/
+ 	ifa_flags = IFA_F_NOPREFIXROUTE;
++	if (priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
++	    || priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
++		ifa_flags |= IFA_F_MANAGETEMPADDR;
+ 
+ 	g_return_if_fail (priv->act_request);
+ 	connection = nm_device_get_connection (device);
+@@ -3361,7 +3415,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
+ }
+ 
+ static gboolean
+-addrconf6_start (NMDevice *self)
++addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
+ {
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ 	NMConnection *connection;
+@@ -3385,6 +3439,9 @@ addrconf6_start (NMDevice *self)
+ 		return FALSE;
+ 	}
+ 
++	priv->rdisc_use_tempaddr = use_tempaddr;
++	print_support_extended_ifa_flags (use_tempaddr);
++
+ 	/* ensure link local is ready... */
+ 	ret = linklocal6_start (self);
+ 
+@@ -3432,10 +3489,23 @@ addrconf6_cleanup (NMDevice *self)
+ 
+ /******************************************/
+ 
++static NMSettingIP6ConfigPrivacy
++use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
++{
++	switch (use_tempaddr) {
++	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
++	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
++	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
++		return use_tempaddr;
++	default:
++		return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
++	}
++}
++
+ /* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or
+  * /lib/sysctl.d/sysctl.conf
+  */
+-static int
++static NMSettingIP6ConfigPrivacy
+ ip6_use_tempaddr (void)
+ {
+ 	char *contents = NULL;
+@@ -3443,12 +3513,13 @@ ip6_use_tempaddr (void)
+ 	char *sysctl_data = NULL;
+ 	GKeyFile *keyfile;
+ 	GError *error = NULL;
+-	int tmp, ret = -1;
++	gint tmp;
++	NMSettingIP6ConfigPrivacy ret = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+ 
+ 	/* Read file contents to a string. */
+ 	if (!g_file_get_contents ("/etc/sysctl.conf", &contents, NULL, NULL))
+ 		if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, NULL, NULL))
+-			return -1;
++			return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+ 
+ 	/* Prepend a group so that we can use GKeyFile parser. */
+ 	sysctl_data = g_strdup_printf ("%s%s", group_name, contents);
+@@ -3459,7 +3530,7 @@ ip6_use_tempaddr (void)
+ 
+ 	tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error);
+ 	if (error == NULL)
+-		ret = tmp;
++		ret = use_tempaddr_clamp (tmp);
+ 
+ done:
+ 	g_free (contents);
+@@ -3499,8 +3570,7 @@ act_stage3_ip6_config_start (NMDevice *self,
+ 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+ 	NMConnection *connection;
+ 	NMSettingIP6Config *s_ip6;
+-	const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+-	int conf_use_tempaddr;
++	const char *method;
+ 	NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+ 	const char *ip6_privacy_str = "0\n";
+ 	GSList *slaves;
+@@ -3554,8 +3624,19 @@ act_stage3_ip6_config_start (NMDevice *self,
+ 
+ 	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
+ 
+-	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
+-		if (!addrconf6_start (self)) {
++	/* Enable/disable IPv6 Privacy Extensions.
++	 * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
++	 * use that value instead of per-connection value.
++	 */
++	ip6_privacy = ip6_use_tempaddr ();
++	if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) {
++		if (s_ip6)
++			ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6);
++	}
++	ip6_privacy = use_tempaddr_clamp (ip6_privacy);
++
++	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
++		if (!addrconf6_start (self, ip6_privacy)) {
+ 			/* IPv6 might be disabled; allow IPv4 to proceed */
+ 			ret = NM_ACT_STAGE_RETURN_STOP;
+ 		} else
+@@ -3597,17 +3678,6 @@ act_stage3_ip6_config_start (NMDevice *self,
+ 
+ 	/* Other methods (shared) aren't implemented yet */
+ 
+-	/* Enable/disable IPv6 Privacy Extensions.
+-	 * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
+-	 * use that value instead of per-connection value.
+-	 */
+-	conf_use_tempaddr = ip6_use_tempaddr ();
+-	if (conf_use_tempaddr >= 0)
+-		ip6_privacy = conf_use_tempaddr;
+-	else if (s_ip6)
+-		ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6);
+-	ip6_privacy = CLAMP (ip6_privacy, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
+-
+ 	switch (ip6_privacy) {
+ 	case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN:
+ 	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
+-- 
+1.8.5.3
+
diff --git a/NetworkManager.spec b/NetworkManager.spec
index a9cb6e8..58fa925 100644
--- a/NetworkManager.spec
+++ b/NetworkManager.spec
@@ -70,6 +70,7 @@ Patch40: platform-ignore-ipv6-ptp.patch
 Patch41: load-connections-ret-value.patch
 Patch42: bgo723163-add-and-activate-fix.patch
 Patch43: NM-before-network-service.patch
+Patch44: 0044-ipv6-privacy.patch
 
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -236,6 +237,7 @@ deployments.
 %patch41 -p1 -b .load-connections-ret-value
 %patch42 -p1 -b .bgo723163-add-and-activate-fix
 %patch43 -p1 -b .NM-before-network-service
+%patch44 -p1 -b .0044-ipv6-privacy.orig
 
 %build
 
@@ -436,6 +438,9 @@ fi
 %changelog
 * Sun Feb 16 2014 Thomas Haller <thaller at redhat.com> - 0.9.9.0-30.git20131003
 - revert previous snapshot release 0.9.9.0-29.git20140131, instead based on 0.9.9.0-28.git20131003
+- support for ipv6 private addresses (rfc4941) (rh #1047139)
+- add ipv6 autoconf addresses with /64 prefix (rh #1045118)
+- wait for IPv6 LL before starting autoconf
 
 * Tue Feb  4 2014 Thomas Haller <thaller at redhat.com> - 0.9.9.0-29.git20140131
 - update to new upstream snapshot


More information about the scm-commits mailing list