merge: branch 'lr/stable-privacy-rfc7217'

https://bugzilla.gnome.org/show_bug.cgi?id=755216
This commit is contained in:
Lubomir Rintel 2015-11-02 20:34:47 +01:00
commit 9b31d6e210
23 changed files with 767 additions and 133 deletions

View file

@ -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 */

View file

@ -559,6 +559,28 @@ 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;
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)
{
@ -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,

View file

@ -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 },
@ -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;

View file

@ -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---

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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),

View file

@ -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,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);
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,
.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++) {

View file

@ -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,

View file

@ -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);

View file

@ -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__ */

View file

@ -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);
/*********************************************************************************************/

View file

@ -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);

View file

@ -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__ */

View file

@ -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,

View file

@ -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);

View file

@ -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())
*/

View file

@ -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) {

View file

@ -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

63
src/tests/test-utils.c Normal file
View file

@ -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 ();
}