From 60811b4809e8cddd4ff3808d7d0c803c11801113 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 8 Sep 2015 20:22:17 +0200 Subject: [PATCH 1/8] setting-ip6-config: add addr-gen-mode property --- libnm-core/nm-setting-ip6-config.c | 92 ++++++++++++++++++++++++++++++ libnm-core/nm-setting-ip6-config.h | 23 ++++++++ libnm/libnm.ver | 2 + 3 files changed, 117 insertions(+) diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c index 9884b86c9c..104c922cea 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_EUI64); + + 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_EUI64, + 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; From f70c8f3d29afccb526ba890ab9778636166f4b63 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 26 Oct 2015 18:00:03 +0100 Subject: [PATCH 2/8] keyfile: add support for addr-gen-mode property --- libnm-core/nm-keyfile-reader.c | 25 +++++++++++++++++++++++++ libnm-core/nm-keyfile-writer.c | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index c639fc27bc..d38d37232b 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -559,6 +559,27 @@ ip6_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) g_strfreev (list); } +static void +ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + NMSettingIP6ConfigAddrGenMode addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + const char *setting_name = nm_setting_get_name (setting); + char *s; + + 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"); + } + g_free (s); + } + + 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) { @@ -1177,6 +1198,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..c62138b484 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -102,6 +102,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, @@ -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 }, From 60d25042911f0554e196f78210ff05a9cde2adc9 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 8 Sep 2015 20:38:41 +0200 Subject: [PATCH 3/8] ifcfg-rh: add support for addr-gen-mode property --- src/settings/plugins/ifcfg-rh/reader.c | 26 +++++++++++++++++++------- src/settings/plugins/ifcfg-rh/writer.c | 11 +++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 60d0bcee3c..518b90a897 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,17 @@ 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); + } + /* 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- file */ route6_path = utils_get_route6_path (ifcfg->fileName); if (!route6_path) { From 4d6649fa0e60c5790aaa87936ec295039be0498a Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 29 Oct 2015 16:11:14 +0100 Subject: [PATCH 4/8] cli: add addr-gen-mode property --- clients/cli/settings.c | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 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 */ From b3e0811b811b8021dc52b32b88e13468494d9d7a Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 3 Oct 2015 19:48:46 +0200 Subject: [PATCH 5/8] rdisc: move address generation into NMRDisc from NMLNDPRdisc It makes more sense in the generic place. It will make it possible for the NMRDisc to retry the address generation upon DAD failures. --- src/rdisc/nm-fake-rdisc.c | 110 +++++++++++++++-------------- src/rdisc/nm-fake-rdisc.h | 10 +-- src/rdisc/nm-lndp-rdisc.c | 7 +- src/rdisc/nm-rdisc-private.h | 10 +-- src/rdisc/nm-rdisc.c | 37 +++++++++- src/rdisc/tests/test-rdisc-fake.c | 32 ++++----- src/rdisc/tests/test-rdisc-linux.c | 3 + 7 files changed, 117 insertions(+), 92 deletions(-) diff --git a/src/rdisc/nm-fake-rdisc.c b/src/rdisc/nm-fake-rdisc.c index 36f386c36f..edf5342209 100644 --- a/src/rdisc/nm-fake-rdisc.c +++ b/src/rdisc/nm-fake-rdisc.c @@ -36,14 +36,23 @@ typedef struct { NMRDiscDHCPLevel dhcp_level; GArray *gateways; - GArray *addresses; - GArray *routes; + GArray *prefixes; GArray *dns_servers; GArray *dns_domains; int hop_limit; guint32 mtu; } 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; @@ -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,31 @@ 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); + 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_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)) + 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, + }; + + 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..fead72cba5 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; } } 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..a7c7f631c7 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -87,11 +87,46 @@ 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) +{ + 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); 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..2d8cd080ac 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"); @@ -66,6 +67,8 @@ main (int argc, char **argv) return EXIT_FAILURE; } + iid.id_u8[7] = 1; + nm_rdisc_set_iid (rdisc, iid); nm_rdisc_start (rdisc); g_main_loop_run (loop); From f85728ecff824b1fece43aba51d8171db2766ea2 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 3 Oct 2015 19:50:30 +0200 Subject: [PATCH 6/8] core: support IPv6 duplicate address detection NMDevice detects the DAD failures by watching the removal of tentative addresses (happens for DAD of addresses with valid lifetime, typically discovered addresses) or changes to addresses with dadfailed flag (permanent addresses, typically link-local and manually configured addresses). It retries creation of link-local addresses itself and lets RDisc know about the rest so that it can decide if it's rdisc-managed address and retry with a new address. Currently NMDevice doesn't do anything useful about link-local address DAD failures -- it just fails the link-local address addition instead of just timing out, which happened before. RDisc just logs a warning and removes the address from the list. However, with RFC7217 stable privacy addresses the use of a different address and thus a recovery from DAD failures would be possible. --- src/devices/nm-device.c | 60 +++++++++++++++++++++++++++++++++++---- src/nm-iface-helper.c | 18 ++++++++++++ src/rdisc/nm-fake-rdisc.c | 1 + src/rdisc/nm-rdisc.c | 22 ++++++++++++++ src/rdisc/nm-rdisc.h | 2 ++ 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index f1df6ef0a5..4ce5eb4627 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -201,6 +201,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 */ @@ -4774,16 +4775,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; } @@ -4840,7 +4845,8 @@ 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; } @@ -4855,7 +4861,16 @@ check_and_add_ipv6ll_addr (NMDevice *self) 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)); + + 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; + } + + _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, @@ -7794,6 +7809,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 +7820,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 +7863,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 +7879,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); @@ -9731,6 +9778,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..93ea17e531 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -321,6 +321,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[]) { @@ -467,6 +481,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 edf5342209..b66f958039 100644 --- a/src/rdisc/nm-fake-rdisc.c +++ b/src/rdisc/nm-fake-rdisc.c @@ -274,6 +274,7 @@ receive_ra (gpointer user_data) .timestamp = item->timestamp, .lifetime = item->lifetime, .preferred = item->preferred, + .dad_counter = 0, }; if (nm_rdisc_complete_and_add_address (rdisc, &address)) diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c index a7c7f631c7..66982f1163 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -385,6 +385,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 diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index def63ee6fc..4802e240fb 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -61,6 +61,7 @@ typedef struct { typedef struct { struct in6_addr address; + guint8 dad_counter; guint32 timestamp; guint32 lifetime; guint32 preferred; @@ -143,5 +144,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__ */ From e603c86926ce48a7dd53b892fe85d506e6322378 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 3 Oct 2015 19:44:27 +0200 Subject: [PATCH 7/8] core: add support for RFC7217 stable privacy addressing RFC7217 introduces an alternative mechanism for creating addresses during stateless IPv6 address configuration. It's supposed to create addresses whose host part stays stable in a particular network but changes when the hosts enters another network to mitigate possibility of tracking the host movement. It can be used alongside RFC 4941 privacy extensions (temporary addresses) and replaces the use of RFC 4862 interface identifiers. The address creation mode is controlld by ip6.addr_gen_mode property (ADDR_GEN_MODE in ifcfg-rh), with values of "stable-privacy" and "eui-64", defaulting to "eui-64" if unspecified. The host part of an address is computed by hashing a system-specific secret salted with various stable values that identify the connection with a secure hash algorithm: RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) For NetworkManager we use these parameters: * F() SHA256 hash function. * Prefix This is a network part of the /64 address * Net_Iface We use the interface name (e.g. "eth0"). This ensures the address won't change with the change of interface hardware. * Network_ID We use the connection UUID here. This ensures the salt is different for wireless networks with a different SSID as suggested by RFC7217. * DAD_Counter A per-address counter that increases with each DAD failure. * secret_key We store the secret key in /var/lib/NetworkManager/secret_key. If it's shorter than 128 bits then it's rejected. If the file is not present we initialize it by fetching 256 pseudo-random bits from /dev/urandom on first use. Duplicate address detection uses IDGEN_RETRIES = 3 and does not utilize the IDGEN_DELAY delay (despite it SHOULD). This is for ease of implementation and may change in future. Neither parameter is currently configurable. --- src/NetworkManagerUtils.c | 121 ++++++++++++++++++++++++++++- src/NetworkManagerUtils.h | 6 ++ src/devices/nm-device.c | 83 +++++++++++++++----- src/nm-iface-helper.c | 5 +- src/rdisc/nm-lndp-rdisc.c | 4 +- src/rdisc/nm-lndp-rdisc.h | 2 +- src/rdisc/nm-rdisc.c | 29 ++++++- src/rdisc/nm-rdisc.h | 3 + src/rdisc/tests/test-rdisc-linux.c | 5 +- src/tests/Makefile.am | 22 +++++- src/tests/test-utils.c | 63 +++++++++++++++ 11 files changed, 314 insertions(+), 29 deletions(-) create mode 100644 src/tests/test-utils.c 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 #include #include +#include #include #include @@ -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 4ce5eb4627..4b6d2d45fc 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "nm-default.h" #include "nm-device.h" @@ -325,6 +326,7 @@ typedef struct { NMIP6Config * ac_ip6_config; guint linklocal6_timeout_id; + guint8 linklocal6_dad_counter; GHashTable * ip6_saved_properties; @@ -4832,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; @@ -4853,21 +4857,43 @@ check_and_add_ipv6ll_addr (NMDevice *self) } } - 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); - 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; + 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)); @@ -5143,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 */ @@ -5167,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; } @@ -5178,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); @@ -5189,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; } @@ -8627,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); @@ -8875,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; } diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 93ea17e531..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 */ @@ -470,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) diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index fead72cba5..f0f56abb41 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -297,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; @@ -307,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.c b/src/rdisc/nm-rdisc.c index 66982f1163..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 + #define _NMLOG_PREFIX_NAME "rdisc" typedef struct { @@ -104,6 +106,23 @@ nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new) 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; @@ -268,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) @@ -277,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); @@ -693,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 4802e240fb..c150a72912 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -26,6 +26,7 @@ #include #include "nm-default.h" +#include "nm-setting-ip6-config.h" #include "NetworkManagerUtils.h" #define NM_TYPE_RDISC (nm_rdisc_get_type ()) @@ -115,6 +116,8 @@ typedef struct { int ifindex; char *ifname; + char *uuid; + NMSettingIP6ConfigAddrGenMode addr_gen_mode; NMUtilsIPv6IfaceId iid; gint32 max_addresses; gint32 rtr_solicitations; diff --git a/src/rdisc/tests/test-rdisc-linux.c b/src/rdisc/tests/test-rdisc-linux.c index 2d8cd080ac..a5b68494dd 100644 --- a/src/rdisc/tests/test-rdisc-linux.c +++ b/src/rdisc/tests/test-rdisc-linux.c @@ -61,7 +61,10 @@ 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; 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 +#include +#include + +#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 (); +} From e9dfdfe9fe586f3fcfaebbf8d6a786b6f6bec03a Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 26 Oct 2015 18:21:18 +0100 Subject: [PATCH 8/8] libnm-core: default to ip6.addr-gen-mode=stable-privacy Take a missing value in keyfile/ifcfg-rh as EUI-64 to keep the compatibility with the old conneciton. Nevertheless, the new connections should default to the RFC7217 addresses. --- libnm-core/nm-keyfile-reader.c | 15 ++++++++------- libnm-core/nm-keyfile-writer.c | 3 ++- libnm-core/nm-setting-ip6-config.c | 4 ++-- src/settings/plugins/ifcfg-rh/reader.c | 5 +++++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index d38d37232b..43e2618435 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -562,20 +562,21 @@ 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 = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + NMSettingIP6ConfigAddrGenMode addr_gen_mode; const char *setting_name = nm_setting_get_name (setting); - char *s; + 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)) { + (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"); + _("invalid option '%s', use one of [%s]"), + s, "eui64,stable-privacy"); + return; } - g_free (s); - } + } else + addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; g_object_set (G_OBJECT (setting), key, (gint) addr_gen_mode, NULL); } diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index c62138b484..5afee4302d 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -608,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 104c922cea..bc516d6e70 100644 --- a/libnm-core/nm-setting-ip6-config.c +++ b/libnm-core/nm-setting-ip6-config.c @@ -115,7 +115,7 @@ 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_EUI64); + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY); return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->addr_gen_mode; } @@ -618,7 +618,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class) (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_EUI64, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 518b90a897..6ca9b92b09 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -1509,6 +1509,11 @@ make_ip6_setting (shvarFile *ifcfg, 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