diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2015-11-02 20:34:47 +0100 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2015-11-02 20:35:42 +0100 |
commit | 9b31d6e2103ac3d81db98910d0ed1f38bb2848d7 (patch) | |
tree | 0cb37794b4532129f13dc529c4adf92a7a818366 | |
parent | 1cf69d9724a85c81e80f77802fcc5d025663f983 (diff) | |
parent | e9dfdfe9fe586f3fcfaebbf8d6a786b6f6bec03a (diff) | |
download | NetworkManager-9b31d6e2103ac3d81db98910d0ed1f38bb2848d7.tar.gz |
merge: branch 'lr/stable-privacy-rfc7217'
https://bugzilla.gnome.org/show_bug.cgi?id=755216
-rw-r--r-- | clients/cli/settings.c | 48 | ||||
-rw-r--r-- | libnm-core/nm-keyfile-reader.c | 26 | ||||
-rw-r--r-- | libnm-core/nm-keyfile-writer.c | 24 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip6-config.c | 92 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip6-config.h | 23 | ||||
-rw-r--r-- | libnm/libnm.ver | 2 | ||||
-rw-r--r-- | src/NetworkManagerUtils.c | 121 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 6 | ||||
-rw-r--r-- | src/devices/nm-device.c | 131 | ||||
-rw-r--r-- | src/nm-iface-helper.c | 23 | ||||
-rw-r--r-- | src/rdisc/nm-fake-rdisc.c | 113 | ||||
-rw-r--r-- | src/rdisc/nm-fake-rdisc.h | 10 | ||||
-rw-r--r-- | src/rdisc/nm-lndp-rdisc.c | 11 | ||||
-rw-r--r-- | src/rdisc/nm-lndp-rdisc.h | 2 | ||||
-rw-r--r-- | src/rdisc/nm-rdisc-private.h | 10 | ||||
-rw-r--r-- | src/rdisc/nm-rdisc.c | 88 | ||||
-rw-r--r-- | src/rdisc/nm-rdisc.h | 5 | ||||
-rw-r--r-- | src/rdisc/tests/test-rdisc-fake.c | 32 | ||||
-rw-r--r-- | src/rdisc/tests/test-rdisc-linux.c | 8 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/reader.c | 31 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/writer.c | 11 | ||||
-rw-r--r-- | src/tests/Makefile.am | 22 | ||||
-rw-r--r-- | src/tests/test-utils.c | 63 |
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 (); +} |