summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2015-11-02 20:34:47 +0100
committerLubomir Rintel <lkundrak@v3.sk>2015-11-02 20:35:42 +0100
commit9b31d6e2103ac3d81db98910d0ed1f38bb2848d7 (patch)
tree0cb37794b4532129f13dc529c4adf92a7a818366
parent1cf69d9724a85c81e80f77802fcc5d025663f983 (diff)
parente9dfdfe9fe586f3fcfaebbf8d6a786b6f6bec03a (diff)
downloadNetworkManager-9b31d6e2103ac3d81db98910d0ed1f38bb2848d7.tar.gz
merge: branch 'lr/stable-privacy-rfc7217'
https://bugzilla.gnome.org/show_bug.cgi?id=755216
-rw-r--r--clients/cli/settings.c48
-rw-r--r--libnm-core/nm-keyfile-reader.c26
-rw-r--r--libnm-core/nm-keyfile-writer.c24
-rw-r--r--libnm-core/nm-setting-ip6-config.c92
-rw-r--r--libnm-core/nm-setting-ip6-config.h23
-rw-r--r--libnm/libnm.ver2
-rw-r--r--src/NetworkManagerUtils.c121
-rw-r--r--src/NetworkManagerUtils.h6
-rw-r--r--src/devices/nm-device.c131
-rw-r--r--src/nm-iface-helper.c23
-rw-r--r--src/rdisc/nm-fake-rdisc.c113
-rw-r--r--src/rdisc/nm-fake-rdisc.h10
-rw-r--r--src/rdisc/nm-lndp-rdisc.c11
-rw-r--r--src/rdisc/nm-lndp-rdisc.h2
-rw-r--r--src/rdisc/nm-rdisc-private.h10
-rw-r--r--src/rdisc/nm-rdisc.c88
-rw-r--r--src/rdisc/nm-rdisc.h5
-rw-r--r--src/rdisc/tests/test-rdisc-fake.c32
-rw-r--r--src/rdisc/tests/test-rdisc-linux.c8
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c31
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c11
-rw-r--r--src/tests/Makefile.am22
-rw-r--r--src/tests/test-utils.c63
23 files changed, 768 insertions, 134 deletions
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index e5139aeb11..1fbc6959c0 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -322,8 +322,9 @@ NmcOutputField nmc_fields_setting_ip6_config[] = {
SETTING_FIELD (NM_SETTING_IP_CONFIG_NEVER_DEFAULT), /* 11 */
SETTING_FIELD (NM_SETTING_IP_CONFIG_MAY_FAIL), /* 12 */
SETTING_FIELD (NM_SETTING_IP6_CONFIG_IP6_PRIVACY), /* 13 */
- SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME), /* 14 */
- SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME), /* 15 */
+ SETTING_FIELD (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE), /* 14 */
+ SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME), /* 15 */
+ SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME), /* 16 */
{NULL, NULL, 0, NULL, FALSE, FALSE, 0}
};
#define NMC_FIELDS_SETTING_IP6_CONFIG_ALL "name"","\
@@ -340,6 +341,7 @@ NmcOutputField nmc_fields_setting_ip6_config[] = {
NM_SETTING_IP_CONFIG_NEVER_DEFAULT","\
NM_SETTING_IP_CONFIG_MAY_FAIL","\
NM_SETTING_IP6_CONFIG_IP6_PRIVACY","\
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE","\
NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME","\
NM_SETTING_IP_CONFIG_DHCP_HOSTNAME
#define NMC_FIELDS_SETTING_IP6_CONFIG_COMMON NMC_FIELDS_SETTING_IP4_CONFIG_ALL
@@ -4129,6 +4131,36 @@ nmc_property_ipv6_set_ip6_privacy (NMSetting *setting, const char *prop, const c
return TRUE;
}
+/* 'addr_gen_mode' */
+static char *
+nmc_property_ipv6_get_addr_gen_mode (NMSetting *setting, NmcPropertyGetType get_type)
+{
+ NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG (setting);
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+
+ addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode (s_ip6);
+ return nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (), addr_gen_mode);
+}
+
+
+static gboolean
+nmc_property_ipv6_set_addr_gen_mode (NMSetting *setting, const char *prop,
+ const char *val, GError **error)
+{
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+
+ if (!nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), val,
+ (int *) &addr_gen_mode, NULL)) {
+ g_set_error (error, 1, 0, _("invalid option '%s', use one of [%s]"),
+ val, "eui64,stable-privacy");
+ return FALSE;
+ }
+
+ g_object_set (setting, prop, addr_gen_mode, NULL);
+ return TRUE;
+}
+
+
/* --- NM_SETTING_OLPC_MESH_SETTING_NAME property setter functions --- */
static gboolean
nmc_property_olpc_set_channel (NMSetting *setting, const char *prop, const char *val, GError **error)
@@ -6104,6 +6136,13 @@ nmc_properties_init (void)
NULL,
NULL,
NULL);
+ nmc_add_prop_funcs (GLUE (IP6_CONFIG, ADDR_GEN_MODE),
+ nmc_property_ipv6_get_addr_gen_mode,
+ nmc_property_ipv6_set_addr_gen_mode,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
nmc_add_prop_funcs (GLUE_IP (6, DHCP_SEND_HOSTNAME),
nmc_property_ipv6_get_dhcp_send_hostname,
nmc_property_set_bool,
@@ -7355,8 +7394,9 @@ setting_ip6_config_details (NMSetting *setting, NmCli *nmc, const char *one_pro
set_val_str (arr, 11, nmc_property_ipv6_get_never_default (setting, NMC_PROPERTY_GET_PRETTY));
set_val_str (arr, 12, nmc_property_ipv6_get_may_fail (setting, NMC_PROPERTY_GET_PRETTY));
set_val_str (arr, 13, nmc_property_ipv6_get_ip6_privacy (setting, NMC_PROPERTY_GET_PRETTY));
- set_val_str (arr, 14, nmc_property_ipv6_get_dhcp_send_hostname (setting, NMC_PROPERTY_GET_PRETTY));
- set_val_str (arr, 15, nmc_property_ipv6_get_dhcp_hostname (setting, NMC_PROPERTY_GET_PRETTY));
+ set_val_str (arr, 14, nmc_property_ipv6_get_addr_gen_mode (setting, NMC_PROPERTY_GET_PRETTY));
+ set_val_str (arr, 15, nmc_property_ipv6_get_dhcp_send_hostname (setting, NMC_PROPERTY_GET_PRETTY));
+ set_val_str (arr, 16, nmc_property_ipv6_get_dhcp_hostname (setting, NMC_PROPERTY_GET_PRETTY));
g_ptr_array_add (nmc->output_data, arr);
print_data (nmc); /* Print all data */
diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c
index c639fc27bc..43e2618435 100644
--- a/libnm-core/nm-keyfile-reader.c
+++ b/libnm-core/nm-keyfile-reader.c
@@ -560,6 +560,28 @@ ip6_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
}
static void
+ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+ const char *setting_name = nm_setting_get_name (setting);
+ gs_free char *s = NULL;
+
+ s = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
+ if (s) {
+ if (!nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), s,
+ (int *) &addr_gen_mode, NULL)) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid option '%s', use one of [%s]"),
+ s, "eui64,stable-privacy");
+ return;
+ }
+ } else
+ addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64;
+
+ g_object_set (G_OBJECT (setting), key, (gint) addr_gen_mode, NULL);
+}
+
+static void
mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length)
{
const char *setting_name = nm_setting_get_name (setting);
@@ -1177,6 +1199,10 @@ static KeyParser key_parsers[] = {
NM_SETTING_IP_CONFIG_DNS,
FALSE,
ip6_dns_parser },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
+ FALSE,
+ ip6_addr_gen_mode_parser },
{ NM_SETTING_WIRED_SETTING_NAME,
NM_SETTING_WIRED_MAC_ADDRESS,
TRUE,
diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c
index ccf516ea12..5afee4302d 100644
--- a/libnm-core/nm-keyfile-writer.c
+++ b/libnm-core/nm-keyfile-writer.c
@@ -103,6 +103,24 @@ dns_writer (KeyfileWriterInfo *info,
}
static void
+ip6_addr_gen_mode_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+ const char *str;
+
+ addr_gen_mode = (NMSettingIP6ConfigAddrGenMode) g_value_get_int (value);
+ str = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
+ addr_gen_mode);
+ nm_keyfile_plugin_kf_set_string (info->keyfile,
+ nm_setting_get_name (setting),
+ key,
+ str);
+}
+
+static void
write_ip_values (GKeyFile *file,
const char *setting_name,
GPtrArray *array,
@@ -557,6 +575,9 @@ static KeyWriter key_writers[] = {
{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
NM_SETTING_IP_CONFIG_DNS,
dns_writer },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
+ ip6_addr_gen_mode_writer },
{ NM_SETTING_WIRELESS_SETTING_NAME,
NM_SETTING_WIRELESS_SSID,
ssid_writer },
@@ -587,7 +608,8 @@ static KeyWriter key_writers[] = {
static gboolean
can_omit_default_value (NMSetting *setting, const char *property)
{
- if (NM_IS_SETTING_VLAN (setting) && !strcmp (property, NM_SETTING_VLAN_FLAGS))
+ if ( (NM_IS_SETTING_VLAN (setting) && !strcmp (property, NM_SETTING_VLAN_FLAGS))
+ || (NM_IS_SETTING_IP6_CONFIG (setting) && !strcmp (property, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE)))
return FALSE;
return TRUE;
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index 9884b86c9c..bc516d6e70 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -26,6 +26,7 @@
#include "nm-setting-ip6-config.h"
#include "nm-setting-private.h"
#include "nm-core-enum-types.h"
+#include "nm-macros-internal.h"
/**
* SECTION:nm-setting-ip6-config
@@ -57,12 +58,14 @@ NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP6_CONFIG)
typedef struct {
NMSettingIP6ConfigPrivacy ip6_privacy;
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
} NMSettingIP6ConfigPrivate;
enum {
PROP_0,
PROP_IP6_PRIVACY,
+ PROP_ADDR_GEN_MODE,
LAST_PROP
};
@@ -97,9 +100,30 @@ nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting)
return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ip6_privacy;
}
+/**
+ * nm_setting_ip6_config_get_addr_gen_mode:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:addr-gen-mode
+ * property.
+ *
+ * Returns: IPv6 Address Generation Mode.
+ *
+ * Since: 1.2
+ **/
+NMSettingIP6ConfigAddrGenMode
+nm_setting_ip6_config_get_addr_gen_mode (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting),
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->addr_gen_mode;
+}
+
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
NMSettingVerifyResult ret;
const char *method;
@@ -166,6 +190,17 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
+ if (!NM_IN_SET (priv->addr_gen_mode,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY)) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD);
+ return FALSE;
+ }
+
return TRUE;
}
@@ -330,6 +365,9 @@ set_property (GObject *object, guint prop_id,
case PROP_IP6_PRIVACY:
priv->ip6_privacy = g_value_get_enum (value);
break;
+ case PROP_ADDR_GEN_MODE:
+ priv->addr_gen_mode = g_value_get_int (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -346,6 +384,9 @@ get_property (GObject *object, guint prop_id,
case PROP_IP6_PRIVACY:
g_value_set_enum (value, priv->ip6_privacy);
break;
+ case PROP_ADDR_GEN_MODE:
+ g_value_set_int (value, priv->addr_gen_mode);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -511,6 +552,10 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
*
* If also global configuration is unspecified or set to "-1", fallback to read
* "/proc/sys/net/ipv6/conf/default/use_tempaddr".
+ *
+ * Note that this setting is distinct from the Stable Privacy addresses
+ * that can be enabled with the "addr-gen-mode" property's "stable-privacy"
+ * setting as another way of avoiding host tracking with IPv6 addresses.
**/
/* ---ifcfg-rh---
* property: ip6-privacy
@@ -531,6 +576,53 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+ /**
+ * NMSettingIP6Config:addr-gen-mode:
+ *
+ * Configure method for creating the address for use with RFC4862 IPv6
+ * Stateless Address Autoconfiguration. The permitted values are: "eui64",
+ * "stable-privacy" or unset.
+ *
+ * If the property is set to "eui64", the addresses will be generated
+ * using the interface tokens derived from hardware address. This makes
+ * the host part of the address to stay constant, making it possible
+ * to track host's presence when it changes networks. The address changes
+ * when the interface hardware is replaced.
+ *
+ * The value of "stable-privacy" enables use of cryptographically
+ * secure hash of a secret host-specific key along with the connection
+ * identification and the network address as specified by RFC7217.
+ * This makes it impossible to use the address track host's presence,
+ * and makes the address stable when the network interface hardware is
+ * replaced.
+ *
+ * Leaving this unset causes a default that could be subject to change
+ * in future versions to be used.
+ *
+ * Note that this setting is distinct from the Privacy Extensions as
+ * configured by "ip6-privacy" property and it does not affect the
+ * temporary addresses configured with this option.
+ *
+ * Since: 1.2
+ **/
+ /* ---ifcfg-rh---
+ * property: addr-gen-mode
+ * variable: IPV6_ADDR_GEN_MODE
+ * values: IPV6_ADDR_GEN_MODE: eui64, stable-privacy
+ * default: eui64
+ * description: Configure IPv6 Stable Privacy addressing for SLAAC (RFC7217).
+ * example: IPV6_ADDR_GEN_MODE=stable-privacy
+ * ---end---
+ */
+ g_object_class_install_property
+ (object_class, PROP_ADDR_GEN_MODE,
+ g_param_spec_int (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, "", "",
+ G_MININT, G_MAXINT,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
/* IP6-specific property overrides */
/* ---dbus---
diff --git a/libnm-core/nm-setting-ip6-config.h b/libnm-core/nm-setting-ip6-config.h
index b791e937b4..fc7dc86c74 100644
--- a/libnm-core/nm-setting-ip6-config.h
+++ b/libnm-core/nm-setting-ip6-config.h
@@ -41,6 +41,8 @@ G_BEGIN_DECLS
#define NM_SETTING_IP6_CONFIG_IP6_PRIVACY "ip6-privacy"
+#define NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE "addr-gen-mode"
+
/**
* NM_SETTING_IP6_CONFIG_METHOD_IGNORE:
*
@@ -114,6 +116,25 @@ typedef enum {
NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR = 2
} NMSettingIP6ConfigPrivacy;
+/**
+ * NMSettingIP6ConfigAddrGenMode:
+ * @NM_SETTING_IP6_CONFIG_PRIVACY_EUI64: The Interface Identifier is derived
+ * from the interface hardware address.
+ * @NM_SETTING_IP6_CONFIG_PRIVACY_STABLE_PRIVACY: The Interface Identifier
+ * is created by using a cryptographically secure hash of a secret host-specific
+ * key along with the connection identification and the network address as
+ * specified by RFC7217.
+ *
+ * #NMSettingIP6ConfigAddrGenMode controls how the the Interface Identifier for
+ * RFC4862 Stateless Address Autoconfiguration is created.
+ *
+ * Since: 1.2
+ */
+typedef enum {
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 = 0,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY = 1,
+} NMSettingIP6ConfigAddrGenMode;
+
struct _NMSettingIP6Config {
NMSettingIPConfig parent;
};
@@ -130,6 +151,8 @@ GType nm_setting_ip6_config_get_type (void);
NMSetting *nm_setting_ip6_config_new (void);
NMSettingIP6ConfigPrivacy nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting);
+NM_AVAILABLE_IN_1_2
+NMSettingIP6ConfigAddrGenMode nm_setting_ip6_config_get_addr_gen_mode (NMSettingIP6Config *setting);
G_END_DECLS
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 71ae69f3aa..c99fc81917 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -882,6 +882,8 @@ global:
nm_setting_connection_get_metered;
nm_setting_connection_lldp_get_type;
nm_setting_ip4_config_get_dhcp_timeout;
+ nm_setting_ip6_config_addr_gen_mode_get_type;
+ nm_setting_ip6_config_get_addr_gen_mode;
nm_setting_ip_config_add_dns_option;
nm_setting_ip_config_clear_dns_options;
nm_setting_ip_config_get_dns_option;
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index deab2a5869..cd8d717e96 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -29,6 +29,7 @@
#include <resolv.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include <linux/if.h>
#include <linux/if_infiniband.h>
@@ -3213,7 +3214,6 @@ nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
}
return FALSE;
}
-
void
nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
const NMUtilsIPv6IfaceId iid)
@@ -3228,6 +3228,125 @@ nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
memcpy (iid, addr->s6_addr + 8, 8);
}
+static gboolean
+_set_stable_privacy (struct in6_addr *addr,
+ const char *ifname,
+ const char *uuid,
+ guint dad_counter,
+ gchar *secret_key,
+ gsize key_len,
+ GError **error)
+{
+ GChecksum *sum;
+ guint8 digest[32];
+ guint32 tmp[2];
+ gsize len = sizeof (digest);
+
+ g_return_val_if_fail (key_len, FALSE);
+
+ /* Documentation suggests that this can fail.
+ * Maybe in case of a missing algorithm in crypto library? */
+ sum = g_checksum_new (G_CHECKSUM_SHA256);
+ if (!sum) {
+ g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Can't create a SHA256 hash");
+ return FALSE;
+ }
+
+ key_len = CLAMP (key_len, 0, G_MAXUINT32);
+
+ g_checksum_update (sum, addr->s6_addr, 8);
+ g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1);
+ if (!uuid)
+ uuid = "";
+ g_checksum_update (sum, (const guchar *) uuid, strlen (uuid) + 1);
+ tmp[0] = htonl (dad_counter);
+ tmp[1] = htonl (key_len);
+ g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp));
+ g_checksum_update (sum, (const guchar *) secret_key, key_len);
+
+ g_checksum_get_digest (sum, digest, &len);
+ g_checksum_free (sum);
+
+ g_return_val_if_fail (len == 32, FALSE);
+
+ memcpy (addr->s6_addr + 8, &digest[0], 8);
+
+ return TRUE;
+}
+
+#define RFC7217_IDGEN_RETRIES 3
+/**
+ * nm_utils_ipv6_addr_set_stable_privacy:
+ *
+ * Extend the address prefix with an interface identifier using the
+ * RFC 7217 Stable Privacy mechanism.
+ *
+ * Returns: %TRUE on success, %FALSE if the address could not be generated.
+ */
+gboolean
+nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
+ const char *ifname,
+ const char *uuid,
+ guint dad_counter,
+ GError **error)
+{
+ gchar *secret_key = NULL;
+ gsize key_len = 0;
+ gboolean success = FALSE;
+
+ if (dad_counter >= RFC7217_IDGEN_RETRIES) {
+ g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Too many DAD collisions");
+ return FALSE;
+ }
+
+ /* Let's try to load a saved secret key first. */
+ if (g_file_get_contents (NMSTATEDIR "/secret_key", &secret_key, &key_len, NULL)) {
+ if (key_len < 16) {
+ g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Key is too short to be usable");
+ key_len = 0;
+ }
+ } else {
+ int urandom = open ("/dev/urandom", O_RDONLY);
+ mode_t key_mask;
+
+ if (!urandom) {
+ g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Can't open /dev/urandom: %s", strerror (errno));
+ return FALSE;
+ }
+
+ /* RFC7217 mandates the key SHOULD be at least 128 bits.
+ * Let's use twice as much. */
+ key_len = 32;
+ secret_key = g_malloc (key_len);
+
+ key_mask = umask (0077);
+ if (read (urandom, secret_key, key_len) == key_len) {
+ if (!g_file_set_contents (NMSTATEDIR "/secret_key", secret_key, key_len, error)) {
+ g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key");
+ key_len = 0;
+ }
+ } else {
+ g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Could not obtain a secret");
+ key_len = 0;
+ }
+ umask (key_mask);
+ close (urandom);
+ }
+
+ if (key_len) {
+ success = _set_stable_privacy (addr, ifname, uuid, dad_counter,
+ secret_key, key_len, error);
+ }
+
+ g_free (secret_key);
+ return success;
+}
+
/**
* nm_utils_setpgid:
* @unused: unused
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 3da2235559..2d29b5bb90 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -286,6 +286,12 @@ gboolean nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
void nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
const NMUtilsIPv6IfaceId iid);
+gboolean nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
+ const char *ifname,
+ const char *uuid,
+ guint dad_counter,
+ GError **error);
+
void nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
const struct in6_addr *addr);
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index f1df6ef0a5..4b6d2d45fc 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -32,6 +32,7 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <netlink/route/addr.h>
+#include <linux/if_addr.h>
#include "nm-default.h"
#include "nm-device.h"
@@ -201,6 +202,7 @@ typedef struct {
guint queued_ip4_config_id;
guint queued_ip6_config_id;
GSList *pending_actions;
+ GSList *dad6_failed_addrs;
char * udi;
char * iface; /* may change, could be renamed by user */
@@ -324,6 +326,7 @@ typedef struct {
NMIP6Config * ac_ip6_config;
guint linklocal6_timeout_id;
+ guint8 linklocal6_dad_counter;
GHashTable * ip6_saved_properties;
@@ -4774,16 +4777,20 @@ linklocal6_cleanup (NMDevice *self)
}
}
+static void
+linklocal6_failed (NMDevice *self)
+{
+ linklocal6_cleanup (self);
+ nm_device_activate_schedule_ip6_config_timeout (self);
+}
+
static gboolean
linklocal6_timeout_cb (gpointer user_data)
{
NMDevice *self = user_data;
- linklocal6_cleanup (self);
-
_LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout");
-
- nm_device_activate_schedule_ip6_config_timeout (self);
+ linklocal6_failed (self);
return G_SOURCE_REMOVE;
}
@@ -4827,9 +4834,11 @@ check_and_add_ipv6ll_addr (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
int ip_ifindex = nm_device_get_ip_ifindex (self);
- NMUtilsIPv6IfaceId iid;
struct in6_addr lladdr;
guint i, n;
+ NMConnection *connection;
+ NMSettingIP6Config *s_ip6 = NULL;
+ GError *error = NULL;
if (priv->nm_ipv6ll == FALSE)
return;
@@ -4840,22 +4849,54 @@ check_and_add_ipv6ll_addr (NMDevice *self)
const NMPlatformIP6Address *addr;
addr = nm_ip6_config_get_address (priv->ip6_config, i);
- if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+ if ( IN6_IS_ADDR_LINKLOCAL (&addr->address)
+ && !(addr->flags & IFA_F_DADFAILED)) {
/* Already have an LL address, nothing to do */
return;
}
}
}
- if (!nm_device_get_ip_iface_identifier (self, &iid)) {
- _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 may be broken");
- return;
- }
-
memset (&lladdr, 0, sizeof (lladdr));
lladdr.s6_addr16[0] = htons (0xfe80);
- nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid);
- _LOGD (LOGD_IP6, "adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
+
+ connection = nm_device_get_applied_connection (self);
+ if (connection)
+ s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
+
+ if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
+ if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr,
+ nm_device_get_iface (self),
+ nm_connection_get_uuid (connection),
+ priv->linklocal6_dad_counter++,
+ &error)) {
+ _LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message);
+ g_clear_error (&error);
+ linklocal6_failed (self);
+ return;
+ }
+ _LOGD (LOGD_IP6, "linklocal6: using IPv6 stable-privacy addressing");
+ } else {
+ NMUtilsIPv6IfaceId iid;
+
+ if (priv->linklocal6_timeout_id) {
+ /* We already started and attempt to add a LL address. For the EUI-64
+ * mode we can't pick a new one, we'll just fail. */
+ _LOGW (LOGD_IP6, "linklocal6: DAD failed for an EUI-64 address");
+ linklocal6_failed (self);
+ return;
+ }
+
+ if (!nm_device_get_ip_iface_identifier (self, &iid)) {
+ _LOGW (LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue");
+ return;
+ }
+ _LOGD (LOGD_IP6, "linklocal6: using EUI-64 identifier to generate IPv6LL address");
+
+ nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid);
+ }
+
+ _LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
if (!nm_platform_ip6_address_add (NM_PLATFORM_GET,
ip_ifindex,
lladdr,
@@ -5128,10 +5169,15 @@ addrconf6_start_with_link_ready (NMDevice *self)
g_assert (priv->rdisc);
if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) {
- _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
- } else if (!nm_device_get_ip_iface_identifier (self, &iid)) {
- _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue");
- return FALSE;
+ _LOGD (LOGD_IP6, "addrconf6: IPv6 tokenized identifier present");
+ nm_rdisc_set_iid (priv->rdisc, iid);
+ } else if (nm_device_get_ip_iface_identifier (self, &iid)) {
+ _LOGD (LOGD_IP6, "addrconf6: using the device EUI-64 identifier");
+ nm_rdisc_set_iid (priv->rdisc, iid);
+ } else {
+ /* Don't abort the addrconf at this point -- if rdisc needs the iid
+ * it will notice this itself. */
+ _LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 adddress creation may fail");
}
/* Apply any manual configuration before starting RA */
@@ -5152,7 +5198,6 @@ addrconf6_start_with_link_ready (NMDevice *self)
G_CALLBACK (rdisc_ra_timeout),
self);
- nm_rdisc_set_iid (priv->rdisc, iid);
nm_rdisc_start (priv->rdisc);
return TRUE;
}
@@ -5163,7 +5208,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMConnection *connection;
NMActStageReturn ret;
- const char *ip_iface = nm_device_get_ip_iface (self);
+ NMSettingIP6Config *s_ip6 = NULL;
connection = nm_device_get_applied_connection (self);
g_assert (connection);
@@ -5174,9 +5219,15 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
priv->ac_ip6_config = NULL;
}
- priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface);
+ s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
+ g_assert (s_ip6);
+
+ priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self),
+ nm_device_get_ip_iface (self),
+ nm_connection_get_uuid (connection),
+ nm_setting_ip6_config_get_addr_gen_mode (s_ip6));
if (!priv->rdisc) {
- _LOGE (LOGD_IP6, "failed to start router discovery (%s)", ip_iface);
+ _LOGE (LOGD_IP6, "addrconf6: failed to start router discovery");
return FALSE;
}
@@ -7794,6 +7845,8 @@ queued_ip6_config_change (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ GSList *iter;
+ gboolean need_ipv6ll = FALSE;
/* Wait for any queued state changes */
if (priv->queued_state.id)
@@ -7803,12 +7856,32 @@ queued_ip6_config_change (gpointer user_data)
g_object_ref (self);
update_ip6_config (self, FALSE);
+ /* Handle DAD falures */
+ for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) {
+ NMPlatformIP6Address *addr = iter->data;
+
+ if (addr->source >= NM_IP_CONFIG_SOURCE_USER)
+ continue;
+
+ _LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address",
+ nm_platform_ip6_address_to_string (addr, NULL, 0));
+
+ if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
+ need_ipv6ll = TRUE;
+ else
+ nm_rdisc_dad_failed (priv->rdisc, &addr->address);
+ }
+ g_slist_free_full (priv->dad6_failed_addrs, g_free);
+
/* If no IPv6 link-local address exists but other addresses do then we
* must add the LL address to remain conformant with RFC 3513 chapter 2.1
* ("Addressing Model"): "All interfaces are required to have at least
* one link-local unicast address".
*/
if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config))
+ need_ipv6ll = TRUE;
+
+ if (need_ipv6ll)
check_and_add_ipv6ll_addr (self);
g_object_unref (self);
@@ -7826,11 +7899,13 @@ device_ipx_changed (NMPlatform *platform,
NMDevice *self)
{
NMDevicePrivate *priv;
+ NMPlatformIP6Address *addr;
if (nm_device_get_ip_ifindex (self) != ifindex)
return;
priv = NM_DEVICE_GET_PRIVATE (self);
+
switch (obj_type) {
case NMP_OBJECT_TYPE_IP4_ADDRESS:
case NMP_OBJECT_TYPE_IP4_ROUTE:
@@ -7840,6 +7915,14 @@ device_ipx_changed (NMPlatform *platform,
}
break;
case NMP_OBJECT_TYPE_IP6_ADDRESS:
+ addr = platform_object;
+
+ if ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->flags & IFA_F_DADFAILED)
+ || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->flags & IFA_F_TENTATIVE)) {
+ priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs,
+ g_memdup (addr, sizeof (NMPlatformIP6Address)));
+ }
+ /* fallthrough */
case NMP_OBJECT_TYPE_IP6_ROUTE:
if (!priv->queued_ip6_config_id) {
priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
@@ -8580,6 +8663,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
priv->v4_commit_first_time = TRUE;
priv->v6_commit_first_time = TRUE;
+ priv->linklocal6_dad_counter = 0;
+
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
@@ -8828,6 +8913,9 @@ nm_device_spawn_iface_helper (NMDevice *self)
g_ptr_array_add (argv, hex_iid);
}
+ g_ptr_array_add (argv, g_strdup ("--addr-gen-mode"));
+ g_ptr_array_add (argv, g_strdup_printf ("%d", nm_setting_ip6_config_get_addr_gen_mode (NM_SETTING_IP6_CONFIG (s_ip6))));
+
configured = TRUE;
}
@@ -9731,6 +9819,7 @@ finalize (GObject *object)
g_free (priv->perm_hw_addr);
g_free (priv->initial_hw_addr);
g_slist_free_full (priv->pending_actions, g_free);
+ g_slist_free_full (priv->dad6_failed_addrs, g_free);
g_clear_pointer (&priv->physical_port_id, g_free);
g_free (priv->udi);
g_free (priv->iface);
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index ecb67ea7bb..fdd714f918 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -44,6 +44,7 @@ extern unsigned int if_nametoindex (const char *__ifname);
#include "nm-rdisc.h"
#include "nm-lndp-rdisc.h"
#include "nm-utils.h"
+#include "nm-setting-ip6-config.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
@@ -69,6 +70,7 @@ static struct {
char *dhcp4_clientid;
char *dhcp4_hostname;
char *iid_str;
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
char *logging_backend;
char *opt_log_level;
char *opt_log_domains;
@@ -292,6 +294,7 @@ do_early_setup (int *argc, char **argv[])
{ "priority4", '\0', 0, G_OPTION_ARG_INT64, &priority64_v4, N_("Route priority for IPv4"), N_("0") },
{ "priority6", '\0', 0, G_OPTION_ARG_INT64, &priority64_v6, N_("Route priority for IPv6"), N_("1024") },
{ "iid", 'e', 0, G_OPTION_ARG_STRING, &global_opt.iid_str, N_("Hex-encoded Interface Identifier"), "" },
+ { "addr-gen-mode", 'e', 0, G_OPTION_ARG_INT, &global_opt.addr_gen_mode, N_("IPv6 SLAAC address generation mode"), "eui64" },
{ "logging-backend", '\0', 0, G_OPTION_ARG_STRING, &global_opt.logging_backend, N_("The logging backend configuration value. See logging.backend in NetworkManager.conf"), NULL },
/* Logging/debugging */
@@ -321,6 +324,20 @@ do_early_setup (int *argc, char **argv[])
global_opt.priority_v6 = (guint32) priority64_v6;
}
+static void
+ip6_address_changed (NMPlatform *platform,
+ NMPObjectType obj_type,
+ int iface,
+ NMPlatformIP6Address *addr,
+ NMPlatformSignalChangeType change_type,
+ NMPlatformReason reason,
+ NMRDisc *rdisc)
+{
+ if ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->flags & IFA_F_DADFAILED)
+ || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->flags & IFA_F_TENTATIVE))
+ nm_rdisc_dad_failed (rdisc, &addr->address);
+}
+
int
main (int argc, char *argv[])
{
@@ -456,7 +473,7 @@ main (int argc, char *argv[])
if (global_opt.slaac) {
nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, TRUE);
- rdisc = nm_lndp_rdisc_new (ifindex, global_opt.ifname);
+ rdisc = nm_lndp_rdisc_new (ifindex, global_opt.ifname, global_opt.uuid, global_opt.addr_gen_mode);
g_assert (rdisc);
if (iid)
@@ -467,6 +484,10 @@ main (int argc, char *argv[])
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo"), "0");
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref"), "0");
+ g_signal_connect (NM_PLATFORM_GET,
+ NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
+ G_CALLBACK (ip6_address_changed),
+ rdisc);
g_signal_connect (rdisc,
NM_RDISC_CONFIG_CHANGED,
G_CALLBACK (rdisc_config_changed),
diff --git a/src/rdisc/nm-fake-rdisc.c b/src/rdisc/nm-fake-rdisc.c
index 36f386c36f..b66f958039 100644
--- a/src/rdisc/nm-fake-rdisc.c
+++ b/src/rdisc/nm-fake-rdisc.c
@@ -36,8 +36,7 @@ typedef struct {
NMRDiscDHCPLevel dhcp_level;
GArray *gateways;
- GArray *addresses;
- GArray *routes;
+ GArray *prefixes;
GArray *dns_servers;
GArray *dns_domains;
int hop_limit;
@@ -45,6 +44,16 @@ typedef struct {
} FakeRa;
typedef struct {
+ struct in6_addr network;
+ int plen;
+ struct in6_addr gateway;
+ guint32 timestamp;
+ guint32 lifetime;
+ guint32 preferred;
+ NMRDiscPreference preference;
+} FakePrefix;
+
+typedef struct {
guint receive_ra_id;
GSList *ras;
} NMFakeRDiscPrivate;
@@ -67,8 +76,7 @@ fake_ra_free (gpointer data)
FakeRa *ra = data;
g_array_free (ra->gateways, TRUE);
- g_array_free (ra->addresses, TRUE);
- g_array_free (ra->routes, TRUE);
+ g_array_free (ra->prefixes, TRUE);
g_array_free (ra->dns_servers, TRUE);
g_array_free (ra->dns_domains, TRUE);
g_free (ra);
@@ -110,8 +118,7 @@ nm_fake_rdisc_add_ra (NMFakeRDisc *self,
ra->hop_limit = hop_limit;
ra->mtu = mtu;
ra->gateways = g_array_new (FALSE, FALSE, sizeof (NMRDiscGateway));
- ra->addresses = g_array_new (FALSE, FALSE, sizeof (NMRDiscAddress));
- ra->routes = g_array_new (FALSE, FALSE, sizeof (NMRDiscRoute));
+ ra->prefixes = g_array_new (FALSE, FALSE, sizeof (FakePrefix));
ra->dns_servers = g_array_new (FALSE, FALSE, sizeof (NMRDiscDNSServer));
ra->dns_domains = g_array_new (FALSE, FALSE, sizeof (NMRDiscDNSDomain));
g_array_set_clear_func (ra->dns_domains, ra_dns_domain_free);
@@ -142,49 +149,31 @@ nm_fake_rdisc_add_gateway (NMFakeRDisc *self,
}
void
-nm_fake_rdisc_add_address (NMFakeRDisc *self,
- guint ra_id,
- const char *addr,
- guint32 timestamp,
- guint32 lifetime,
- guint32 preferred)
+nm_fake_rdisc_add_prefix (NMFakeRDisc *self,
+ guint ra_id,
+ const char *network,
+ guint plen,
+ const char *gateway,
+ guint32 timestamp,
+ guint32 lifetime,
+ guint32 preferred,
+ NMRDiscPreference preference)
{
NMFakeRDiscPrivate *priv = NM_FAKE_RDISC_GET_PRIVATE (self);
FakeRa *ra = find_ra (priv->ras, ra_id);
- NMRDiscAddress *a;
+ FakePrefix *prefix;
g_assert (ra);
- g_array_set_size (ra->addresses, ra->addresses->len + 1);
- a = &g_array_index (ra->addresses, NMRDiscAddress, ra->addresses->len - 1);
- g_assert (inet_pton (AF_INET6, addr, &a->address) == 1);
- a->timestamp = timestamp;
- a->lifetime = lifetime;
- a->preferred = preferred;
-}
-
-void
-nm_fake_rdisc_add_route (NMFakeRDisc *self,
- guint ra_id,
- const char *network,
- guint plen,
- const char *gateway,
- guint32 timestamp,
- guint32 lifetime,
- NMRDiscPreference preference)
-{
- NMFakeRDiscPrivate *priv = NM_FAKE_RDISC_GET_PRIVATE (self);
- FakeRa *ra = find_ra (priv->ras, ra_id);
- NMRDiscRoute *route;
-
- g_assert (ra);
- g_array_set_size (ra->routes, ra->routes->len + 1);
- route = &g_array_index (ra->routes, NMRDiscRoute, ra->routes->len - 1);
- g_assert (inet_pton (AF_INET6, network, &route->network) == 1);
- g_assert (inet_pton (AF_INET6, gateway, &route->gateway) == 1);
- route->plen = plen;
- route->timestamp = timestamp;
- route->lifetime = lifetime;
- route->preference = preference;
+ 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;
}
void
@@ -265,18 +254,32 @@ receive_ra (gpointer user_data)
changed |= NM_RDISC_CONFIG_GATEWAYS;
}
- for (i = 0; i < ra->addresses->len; i++) {
- NMRDiscAddress *item = &g_array_index (ra->addresses, NMRDiscAddress, i);
-
- if (nm_rdisc_add_address (rdisc, item))
- changed |= NM_RDISC_CONFIG_ADDRESSES;
- }
-
- for (i = 0; i < ra->routes->len; i++) {
- NMRDiscRoute *item = &g_array_index (ra->routes, NMRDiscRoute, i);
-
- if (nm_rdisc_add_route (rdisc, item))
+ for (i = 0; i < ra->prefixes->len; i++) {
+ FakePrefix *item = &g_array_index (ra->prefixes, FakePrefix, i);
+ NMRDiscRoute route = {
+ .network = item->network,
+ .plen = item->plen,
+ .gateway = item->gateway,
+ .timestamp = item->timestamp,
+ .lifetime = item->lifetime,
+ .preference = item->preference,
+ };
+
+ if (nm_rdisc_add_route (rdisc, &route))
changed |= NM_RDISC_CONFIG_ROUTES;
+
+ if (item->plen == 64) {
+ NMRDiscAddress address = {
+ .address = item->network,
+ .timestamp = item->timestamp,
+ .lifetime = item->lifetime,
+ .preferred = item->preferred,
+ .dad_counter = 0,
+ };
+
+ if (nm_rdisc_complete_and_add_address (rdisc, &address))
+ changed |= NM_RDISC_CONFIG_ADDRESSES;
+ }
}
for (i = 0; i < ra->dns_servers->len; i++) {
diff --git a/src/rdisc/nm-fake-rdisc.h b/src/rdisc/nm-fake-rdisc.h
index 74f74897fb..1082ba5ef1 100644
--- a/src/rdisc/nm-fake-rdisc.h
+++ b/src/rdisc/nm-fake-rdisc.h
@@ -59,20 +59,14 @@ void nm_fake_rdisc_add_gateway (NMFakeRDisc *self,
guint32 lifetime,
NMRDiscPreference preference);
-void nm_fake_rdisc_add_address (NMFakeRDisc *self,
- guint ra_id,
- const char *addr,
- guint32 timestamp,
- guint32 lifetime,
- guint32 preferred);
-
-void nm_fake_rdisc_add_route (NMFakeRDisc *self,
+void nm_fake_rdisc_add_prefix (NMFakeRDisc *self,
guint ra_id,
const char *network,
guint plen,
const char *gateway,
guint32 timestamp,
guint32 lifetime,
+ guint32 preferred,
NMRDiscPreference preference);
void nm_fake_rdisc_add_dns_server (NMFakeRDisc *self,
diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c
index a65ab03239..f0f56abb41 100644
--- a/src/rdisc/nm-lndp-rdisc.c
+++ b/src/rdisc/nm-lndp-rdisc.c
@@ -163,7 +163,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
/* Address */
if (ndp_msg_opt_prefix_flag_auto_addr_conf (msg, offset)) {
- if (route.plen == 64 && rdisc->iid.id) {
+ if (route.plen == 64) {
memset (&address, 0, sizeof (address));
address.address = route.network;
address.timestamp = now;
@@ -172,10 +172,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
if (address.preferred > address.lifetime)
address.preferred = address.lifetime;
- /* Add the Interface Identifier to the lower 64 bits */
- nm_utils_ipv6_addr_set_interface_identfier (&address.address, rdisc->iid);
-
- if (nm_rdisc_add_address (rdisc, &address))
+ if (nm_rdisc_complete_and_add_address (rdisc, &address))
changed |= NM_RDISC_CONFIG_ADDRESSES;
}
}
@@ -300,7 +297,7 @@ ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval)
}
NMRDisc *
-nm_lndp_rdisc_new (int ifindex, const char *ifname)
+nm_lndp_rdisc_new (int ifindex, const char *ifname, const char *uuid, NMSettingIP6ConfigAddrGenMode addr_gen_mode)
{
NMRDisc *rdisc;
NMLNDPRDiscPrivate *priv;
@@ -310,6 +307,8 @@ nm_lndp_rdisc_new (int ifindex, const char *ifname)
rdisc->ifindex = ifindex;
rdisc->ifname = g_strdup (ifname);
+ rdisc->uuid = g_strdup (uuid);
+ rdisc->addr_gen_mode = addr_gen_mode;
rdisc->max_addresses = ipv6_sysctl_get (ifname, "max_addresses",
NM_RDISC_MAX_ADDRESSES_DEFAULT);
diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h
index 407e1f9258..4172a3043a 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, const char *uuid, NMSettingIP6ConfigAddrGenMode addr_gen_mode);
#endif /* __NETWORKMANAGER_LNDP_RDISC_H__ */
diff --git a/src/rdisc/nm-rdisc-private.h b/src/rdisc/nm-rdisc-private.h
index 59c0cf3757..c0ec739aba 100644
--- a/src/rdisc/nm-rdisc-private.h
+++ b/src/rdisc/nm-rdisc-private.h
@@ -27,11 +27,11 @@
void nm_rdisc_ra_received (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed);
-gboolean nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new);
-gboolean nm_rdisc_add_address (NMRDisc *rdisc, const NMRDiscAddress *new);
-gboolean nm_rdisc_add_route (NMRDisc *rdisc, const NMRDiscRoute *new);
-gboolean nm_rdisc_add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new);
-gboolean nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new);
+gboolean nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new);
+gboolean nm_rdisc_complete_and_add_address (NMRDisc *rdisc, NMRDiscAddress *new);
+gboolean nm_rdisc_add_route (NMRDisc *rdisc, const NMRDiscRoute *new);
+gboolean nm_rdisc_add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new);
+gboolean nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new);
/*********************************************************************************************/
diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c
index cad3a90eed..37cc9ca8c2 100644
--- a/src/rdisc/nm-rdisc.c
+++ b/src/rdisc/nm-rdisc.c
@@ -30,6 +30,8 @@
#include "nm-default.h"
#include "nm-utils.h"
+#include <nm-setting-ip6-config.h>
+
#define _NMLOG_PREFIX_NAME "rdisc"
typedef struct {
@@ -87,11 +89,63 @@ nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new)
return !!new->lifetime;
}
+/**
+ * complete_address:
+ * @rdisc: the #NMRDisc
+ * @addr: the #NMRDiscAddress
+ *
+ * Adds the host part to the address that has network part set.
+ * If the address already has a host part, add a different host part
+ * if possible (this is useful in case DAD failed).
+ *
+ * Can fail if a different address can not be generated (DAD failure
+ * for an EUI-64 address or DAD counter overflow).
+ *
+ * Returns: %TRUE if the address could be completed, %FALSE otherwise.
+ **/
+static gboolean
+complete_address (NMRDisc *rdisc, NMRDiscAddress *addr)
+{
+ GError *error = NULL;
+
+ if (rdisc->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
+ if (!nm_utils_ipv6_addr_set_stable_privacy (&addr->address,
+ rdisc->ifname,
+ rdisc->uuid,
+ addr->dad_counter++,
+ &error)) {
+ _LOGW ("complete-address: failed to generate an stable-privacy address: %s",
+ error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+ _LOGD ("complete-address: using an stable-privacy address");
+ return TRUE;
+ }
+
+ if (!rdisc->iid.id) {
+ _LOGW ("complete-address: can't generate an EUI-64 address: no interface identifier");
+ return FALSE;
+ }
+
+ if (addr->address.s6_addr32[2] == 0x0 && addr->address.s6_addr32[3] == 0x0) {
+ _LOGD ("complete-address: adding an EUI-64 address");
+ nm_utils_ipv6_addr_set_interface_identfier (&addr->address, rdisc->iid);
+ return TRUE;
+ }
+
+ _LOGW ("complete-address: can't generate a new EUI-64 address");
+ return FALSE;
+}
+
gboolean
-nm_rdisc_add_address (NMRDisc *rdisc, const NMRDiscAddress *new)
+nm_rdisc_complete_and_add_address (NMRDisc *rdisc, NMRDiscAddress *new)
{
int i;
+ if (!complete_address (rdisc, new))
+ return FALSE;
+
for (i = 0; i < rdisc->addresses->len; i++) {
NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
@@ -233,7 +287,10 @@ nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new)
* the old identifier are removed. The caller should ensure the addresses
* will be reset by soliciting router advertisements.
*
- * Returns: %TRUE if the token was changed, %FALSE otherwise.
+ * In case the stable privacy addressing is used %FALSE is returned and
+ * addresses are left untouched.
+ *
+ * Returns: %TRUE if addresses need to be regenerated, %FALSE otherwise.
**/
gboolean
nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid)
@@ -242,6 +299,10 @@ nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid)
if (rdisc->iid.id != iid.id) {
rdisc->iid = iid;
+
+ if (rdisc->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY)
+ return FALSE;
+
if (rdisc->addresses->len) {
_LOGD ("IPv6 interface identifier changed, flushing addresses");
g_array_remove_range (rdisc->addresses, 0, rdisc->addresses->len);
@@ -350,6 +411,28 @@ nm_rdisc_start (NMRDisc *rdisc)
solicit (rdisc);
}
+void
+nm_rdisc_dad_failed (NMRDisc *rdisc, struct in6_addr *address)
+{
+ int i;
+ gboolean changed = FALSE;
+
+ for (i = 0; i < rdisc->addresses->len; i++) {
+ NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+
+ if (!IN6_ARE_ADDR_EQUAL (&item->address, address))
+ continue;
+
+ _LOGD ("DAD failed for discovered address %s", nm_utils_inet6_ntop (address, NULL));
+ if (!complete_address (rdisc, item))
+ g_array_remove_index (rdisc->addresses, i--);
+ changed = TRUE;
+ }
+
+ if (changed)
+ g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, NM_RDISC_CONFIG_ADDRESSES);
+}
+
#define CONFIG_MAP_MAX_STR 7
static void
@@ -636,6 +719,7 @@ finalize (GObject *object)
NMRDisc *rdisc = NM_RDISC (object);
g_free (rdisc->ifname);
+ g_free (rdisc->uuid);
g_array_unref (rdisc->gateways);
g_array_unref (rdisc->addresses);
g_array_unref (rdisc->routes);
diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h
index def63ee6fc..c150a72912 100644
--- a/src/rdisc/nm-rdisc.h
+++ b/src/rdisc/nm-rdisc.h
@@ -26,6 +26,7 @@
#include <netinet/in.h>
#include "nm-default.h"
+#include "nm-setting-ip6-config.h"
#include "NetworkManagerUtils.h"
#define NM_TYPE_RDISC (nm_rdisc_get_type ())
@@ -61,6 +62,7 @@ typedef struct {
typedef struct {
struct in6_addr address;
+ guint8 dad_counter;
guint32 timestamp;
guint32 lifetime;
guint32 preferred;
@@ -114,6 +116,8 @@ typedef struct {
int ifindex;
char *ifname;
+ char *uuid;
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
NMUtilsIPv6IfaceId iid;
gint32 max_addresses;
gint32 rtr_solicitations;
@@ -143,5 +147,6 @@ GType nm_rdisc_get_type (void);
gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid);
void nm_rdisc_start (NMRDisc *rdisc);
+void nm_rdisc_dad_failed (NMRDisc *rdisc, struct in6_addr *address);
#endif /* __NETWORKMANAGER_RDISC_H__ */
diff --git a/src/rdisc/tests/test-rdisc-fake.c b/src/rdisc/tests/test-rdisc-fake.c
index 3b248dc69b..6ba8d27133 100644
--- a/src/rdisc/tests/test-rdisc-fake.c
+++ b/src/rdisc/tests/test-rdisc-fake.c
@@ -37,8 +37,11 @@ rdisc_new (void)
NMRDisc *rdisc;
const int ifindex = 1;
const char *ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex);
+ NMUtilsIPv6IfaceId iid;
rdisc = nm_fake_rdisc_new (ifindex, ifname);
+ iid.id_u8[7] = 1;
+ nm_rdisc_set_iid (rdisc, iid);
g_assert (rdisc);
return NM_FAKE_RDISC (rdisc);
}
@@ -145,8 +148,7 @@ test_simple (void)
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_OTHERCONF, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", now, 10, NM_RDISC_PREFERENCE_MEDIUM);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 10, 10);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 10);
nm_fake_rdisc_add_dns_server (rdisc, id, "2001:db8:c:c::1", now, 10);
nm_fake_rdisc_add_dns_domain (rdisc, id, "foobar.com", now, 10);
@@ -198,7 +200,7 @@ test_everything_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *dat
g_assert_cmpint (rdisc->gateways->len, ==, 1);
match_gateway (rdisc->gateways, 0, "fe80::2", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
g_assert_cmpint (rdisc->addresses->len, ==, 1);
- match_address (rdisc->addresses, 0, "2001:db8:a:a::2", data->timestamp1, 10, 10);
+ match_address (rdisc->addresses, 0, "2001:db8:a:b::1", data->timestamp1, 10, 10);
g_assert_cmpint (rdisc->routes->len, ==, 1);
match_route (rdisc->routes, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1, 10, 10);
g_assert_cmpint (rdisc->dns_servers->len, ==, 1);
@@ -225,8 +227,7 @@ test_everything (void)
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", now, 10, NM_RDISC_PREFERENCE_MEDIUM);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 10, 10);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 10);
nm_fake_rdisc_add_dns_server (rdisc, id, "2001:db8:c:c::1", now, 10);
nm_fake_rdisc_add_dns_domain (rdisc, id, "foobar.com", now, 10);
@@ -234,15 +235,13 @@ test_everything (void)
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", now, 0, NM_RDISC_PREFERENCE_MEDIUM);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 0, 0);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 0, 0);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 0, 0, 0);
nm_fake_rdisc_add_dns_server (rdisc, id, "2001:db8:c:c::1", now, 0);
nm_fake_rdisc_add_dns_domain (rdisc, id, "foobar.com", now, 0);
/* and add some new stuff */
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::2", now, 10, NM_RDISC_PREFERENCE_MEDIUM);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::2", now, 10, 10);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10);
nm_fake_rdisc_add_dns_server (rdisc, id, "2001:db8:c:c::2", now, 10);
nm_fake_rdisc_add_dns_domain (rdisc, id, "foobar2.com", now, 10);
@@ -276,7 +275,7 @@ test_preference_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *dat
match_gateway (rdisc->gateways, 1, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_LOW);
g_assert_cmpint (rdisc->addresses->len, ==, 2);
match_address (rdisc->addresses, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
- match_address (rdisc->addresses, 1, "2001:db8:a:a::2", data->timestamp1 + 1, 10, 10);
+ match_address (rdisc->addresses, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
g_assert_cmpint (rdisc->routes->len, ==, 2);
match_route (rdisc->routes, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10);
match_route (rdisc->routes, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 5);
@@ -290,7 +289,7 @@ test_preference_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *dat
match_gateway (rdisc->gateways, 1, "fe80::2", data->timestamp1 + 1, 10, NM_RDISC_PREFERENCE_MEDIUM);
g_assert_cmpint (rdisc->addresses->len, ==, 2);
match_address (rdisc->addresses, 0, "2001:db8:a:a::1", data->timestamp1 + 2, 10, 10);
- match_address (rdisc->addresses, 1, "2001:db8:a:a::2", data->timestamp1 + 1, 10, 10);
+ match_address (rdisc->addresses, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
g_assert_cmpint (rdisc->routes->len, ==, 2);
match_route (rdisc->routes, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1 + 2, 10, 15);
match_route (rdisc->routes, 1, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10);
@@ -318,20 +317,17 @@ test_preference (void)
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", now, 10, NM_RDISC_PREFERENCE_LOW);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 10, 10);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 5);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 5);
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::2", ++now, 10, NM_RDISC_PREFERENCE_MEDIUM);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::2", now, 10, 10);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10);
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", ++now, 10, NM_RDISC_PREFERENCE_HIGH);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 10, 10);
- nm_fake_rdisc_add_route (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 15);
+ nm_fake_rdisc_add_prefix (rdisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 15);
g_signal_connect (rdisc,
NM_RDISC_CONFIG_CHANGED,
@@ -380,7 +376,6 @@ test_dns_solicit_loop_rs_sent (NMFakeRDisc *rdisc, TestData *data)
id = nm_fake_rdisc_add_ra (rdisc, 0, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", now, 10, NM_RDISC_PREFERENCE_MEDIUM);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 10, 10);
nm_fake_rdisc_emit_new_ras (rdisc);
} else if (data->rs_counter >= 6) {
@@ -410,7 +405,6 @@ test_dns_solicit_loop (void)
id = nm_fake_rdisc_add_ra (rdisc, 1, NM_RDISC_DHCP_LEVEL_NONE, 4, 1500);
g_assert (id);
nm_fake_rdisc_add_gateway (rdisc, id, "fe80::1", now, 10, NM_RDISC_PREFERENCE_LOW);
- nm_fake_rdisc_add_address (rdisc, id, "2001:db8:a:a::1", now, 10, 10);
nm_fake_rdisc_add_dns_server (rdisc, id, "2001:db8:c:c::1", now, 6);
g_signal_connect (rdisc,
diff --git a/src/rdisc/tests/test-rdisc-linux.c b/src/rdisc/tests/test-rdisc-linux.c
index 184573bc2e..a5b68494dd 100644
--- a/src/rdisc/tests/test-rdisc-linux.c
+++ b/src/rdisc/tests/test-rdisc-linux.c
@@ -40,6 +40,7 @@ main (int argc, char **argv)
NMRDisc *rdisc;
int ifindex = 1;
const char *ifname;
+ NMUtilsIPv6IfaceId iid;
nmtst_init_with_logging (&argc, &argv, NULL, "DEFAULT");
@@ -60,12 +61,17 @@ main (int argc, char **argv)
return EXIT_FAILURE;
}
- rdisc = nm_lndp_rdisc_new (ifindex, ifname);
+ rdisc = nm_lndp_rdisc_new (ifindex,
+ ifname,
+ "8ce666e8-d34d-4fb1-b858-f15a7al28086",
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64);
if (!rdisc) {
g_print ("Failed to create NMRDisc instance\n");
return EXIT_FAILURE;
}
+ iid.id_u8[7] = 1;
+ nm_rdisc_set_iid (rdisc, iid);
nm_rdisc_start (rdisc);
g_main_loop_run (loop);
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index 60d0bcee3c..6ca9b92b09 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -1317,8 +1317,9 @@ make_ip6_setting (shvarFile *ifcfg,
shvarFile *network_ifcfg;
gboolean never_default = FALSE;
gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip;
- char *ip6_privacy_str;
NMSettingIP6ConfigPrivacy ip6_privacy_val;
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+ char *tmp;
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
@@ -1402,20 +1403,20 @@ make_ip6_setting (shvarFile *ifcfg,
/* TODO - handle other methods */
/* Read IPv6 Privacy Extensions configuration */
- ip6_privacy_str = svGetValue (ifcfg, "IPV6_PRIVACY", FALSE);
- if (ip6_privacy_str) {
+ tmp = svGetValue (ifcfg, "IPV6_PRIVACY", FALSE);
+ if (tmp) {
ip6_privacy = svGetValueBoolean (ifcfg, "IPV6_PRIVACY", FALSE);
if (!ip6_privacy)
- ip6_privacy = g_strcmp0 (ip6_privacy_str, "rfc4941") == 0 ||
- g_strcmp0 (ip6_privacy_str, "rfc3041") == 0;
+ ip6_privacy = g_strcmp0 (tmp, "rfc4941") == 0 ||
+ g_strcmp0 (tmp, "rfc3041") == 0;
}
ip6_privacy_prefer_public_ip = svGetValueBoolean (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE);
- ip6_privacy_val = ip6_privacy_str ?
+ ip6_privacy_val = tmp ?
(ip6_privacy ?
(ip6_privacy_prefer_public_ip ? NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR : NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) :
NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED) :
NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
- g_free (ip6_privacy_str);
+ g_free (tmp);
g_object_set (s_ip6,
NM_SETTING_IP_CONFIG_METHOD, method,
@@ -1499,6 +1500,22 @@ make_ip6_setting (shvarFile *ifcfg,
}
}
+ /* IPv6 addressing mode configuration */
+ tmp = svGetValue (ifcfg, "IPV6_ADDR_GEN_MODE", FALSE);
+ if (tmp) {
+ if (nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), tmp,
+ (int *) &addr_gen_mode, NULL))
+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, addr_gen_mode, NULL);
+ else
+ PARSE_WARNING ("Invalid IPV6_ADDR_GEN_MODE");
+ g_free (tmp);
+ } else {
+ g_object_set (s_ip6,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
+ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+ NULL);
+ }
+
/* DNS servers
* Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
*/
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index b2d1419123..ca45721934 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -2391,6 +2391,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
gint64 route_metric;
GString *ip_str1, *ip_str2, *ip_ptr;
char *route6_path;
+ NMSettingIP6ConfigAddrGenMode addr_gen_mode;
s_ip6 = nm_connection_get_setting_ip6_config (connection);
if (!s_ip6) {
@@ -2403,6 +2404,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
svSetValue (ifcfg, "IPV6_PEERROUTES", "yes", FALSE);
svSetValue (ifcfg, "IPV6_FAILURE_FATAL", "no", FALSE);
svSetValue (ifcfg, "IPV6_ROUTE_METRIC", NULL, FALSE);
+ svSetValue (ifcfg, "IPV6_ADDR_GEN_MODE", "stable-privacy", FALSE);
return TRUE;
}
@@ -2540,6 +2542,15 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
break;
}
+ /* IPv6 Address generation mode */
+ addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode (NM_SETTING_IP6_CONFIG (s_ip6));
+ if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
+ tmp = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
+ addr_gen_mode);
+ svSetValue (ifcfg, "IPV6_ADDR_GEN_MODE", tmp, FALSE);
+ g_free (tmp);
+ }
+
/* Static routes go to route6-<dev> file */
route6_path = utils_get_route6_path (ifcfg->fileName);
if (!route6_path) {
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index c4dd716f38..219e0270ba 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -24,7 +24,8 @@ noinst_PROGRAMS = \
test-route-manager-fake \
test-dcb \
test-resolvconf-capture \
- test-wired-defname
+ test-wired-defname \
+ test-utils
####### ip4 config test #######
@@ -110,6 +111,22 @@ test_wired_defname_SOURCES = \
test_wired_defname_LDADD = \
$(top_builddir)/src/libNetworkManager.la
+####### utils test #######
+
+test_utils_SOURCES = \
+ test-utils.c
+
+test_utils_DEPENDENCIES = \
+ $(top_srcdir)/src/NetworkManagerUtils.c
+
+test_utils_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -DPREFIX=\"/nonexistent\" \
+ -DNMSTATEDIR=\"/nonsense\"
+
+test_utils_LDADD = \
+ $(top_builddir)/src/libNetworkManager.la
+
####### secret agent interface test #######
EXTRA_DIST = test-secret-agent.py
@@ -126,7 +143,8 @@ TESTS = \
test-resolvconf-capture \
test-general \
test-general-with-expect \
- test-wired-defname
+ test-wired-defname \
+ test-utils
if ENABLE_TESTS
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
new file mode 100644
index 0000000000..79a7e68030
--- /dev/null
+++ b/src/tests/test-utils.c
@@ -0,0 +1,63 @@
+/* -*- 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) 2015 Red Hat, Inc.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include "NetworkManagerUtils.c"
+
+#include "nm-test-utils.h"
+
+static void
+test_stable_privacy (void)
+{
+ struct in6_addr addr1;
+
+ inet_pton (AF_INET6, "1234::", &addr1);
+ _set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, "key", 3, NULL);
+ nmtst_assert_ip6_address (&addr1, "1234::4ceb:14cd:3d54:793f");
+
+ /* We get an address without the UUID. */
+ inet_pton (AF_INET6, "1::", &addr1);
+ _set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL);
+ nmtst_assert_ip6_address (&addr1, "1::11aa:2530:9144:dafa");
+
+ /* We get a different address in a different network. */
+ inet_pton (AF_INET6, "2::", &addr1);
+ _set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL);
+ nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726");
+}
+
+/*******************************************/
+
+NMTST_DEFINE ();
+
+int
+main (int argc, char **argv)
+{
+ nmtst_init_with_logging (&argc, &argv, NULL, "ALL");
+
+ g_test_add_func ("/utils/stable_privacy", test_stable_privacy);
+
+ return g_test_run ();
+}