core/rdisc: add support for IPv6 privacy

Add support for ipv6-private addresses. This feature
needs support from the kernel and libnl.

If there is no system support, temporary addresses are
not supported. Log a warning in this case.

Depending on whether ipv6-privacy (use_tempaddr) is enabled,
we add the address flag IFA_F_MANAGETEMPADDR and the kernel
will add temporary addresses for us.

https://bugzilla.gnome.org/show_bug.cgi?id=705170
https://bugzilla.redhat.com/show_bug.cgi?id=1003859
https://bugzilla.redhat.com/show_bug.cgi?id=1047139

Signed-off-by: Thomas Haller <thaller@redhat.com>
This commit is contained in:
Thomas Haller 2014-01-03 17:03:35 +01:00
parent 39cbe772a6
commit 1dea271469

View file

@ -75,7 +75,10 @@
#include "nm-device-bond.h"
#include "nm-device-team.h"
/* workaround for older libnl version, that does not define this flag. */
/* workaround for older libnl version, that does not define these flags. */
#ifndef IFA_F_MANAGETEMPADDR
#define IFA_F_MANAGETEMPADDR 0x100
#endif
#ifndef IFA_F_NOPREFIXROUTE
#define IFA_F_NOPREFIXROUTE 0x200
#endif
@ -282,6 +285,7 @@ typedef struct {
NMRDisc * rdisc;
gulong rdisc_config_changed_sigid;
NMSettingIP6ConfigPrivacy rdisc_use_tempaddr;
/* IP6 config from autoconf */
NMIP6Config * ac_ip6_config;
@ -3312,6 +3316,53 @@ linklocal6_start (NMDevice *self)
static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release);
static void
print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
{
static gint8 warn = 0;
static gint8 s_libnl = -1, s_kernel;
if (warn >= 2)
return;
if (s_libnl == -1) {
s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags ();
s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags ();
if (s_libnl && s_kernel) {
nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)");
warn = 2;
return;
}
}
if ( use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
&& use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) {
if (warn == 0) {
nm_log_dbg (LOGD_IP6, "%s%s%s %s not support extended IFA_FLAGS (needed by NM for IPv6 private addresses)",
!s_kernel ? "kernel" : "",
!s_kernel && !s_libnl ? " and " : "",
!s_libnl ? "libnl" : "",
!s_kernel && !s_libnl ? "do" : "does");
warn = 1;
}
return;
}
if (!s_libnl && !s_kernel) {
nm_log_warn (LOGD_IP6, "libnl and the kernel do not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
} else if (!s_libnl) {
nm_log_warn (LOGD_IP6, "libnl does not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
} else if (!s_kernel) {
nm_log_warn (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
"IPv6 private addresses. This feature is not available");
}
warn = 2;
}
static void
rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device)
{
@ -3326,18 +3377,21 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
/*
* Check, if both libnl and the kernel are recent enough,
* to help user space handling RA. If it's not supported,
* we must add autoconf addresses as /128.
* The reason for /128 is to prevent the kernel from adding
* a prefix route for this address.
* we have no ipv6-privacy and must add autoconf addresses
* as /128. The reason for the /128 is to prevent the kernel
* from adding a prefix route for this address.
**/
system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
nm_platform_check_support_kernel_extended_ifa_flags ();
}
/* without system_support, this flag will be ignored.
* Still, we set it (why not?).
/* without system_support, these flags will be ignored.
* Still, we set them (why not?).
**/
ifa_flags = IFA_F_NOPREFIXROUTE;
if (priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
|| priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
ifa_flags |= IFA_F_MANAGETEMPADDR;
g_return_if_fail (priv->act_request);
connection = nm_device_get_connection (device);
@ -3458,7 +3512,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
}
static gboolean
addrconf6_start (NMDevice *self)
addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMConnection *connection;
@ -3481,6 +3535,9 @@ addrconf6_start (NMDevice *self)
return FALSE;
}
priv->rdisc_use_tempaddr = use_tempaddr;
print_support_extended_ifa_flags (use_tempaddr);
/* ensure link local is ready... */
ret = linklocal6_start (self);
if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
@ -3527,10 +3584,23 @@ addrconf6_cleanup (NMDevice *self)
/******************************************/
static NMSettingIP6ConfigPrivacy
use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
{
switch (use_tempaddr) {
case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
return use_tempaddr;
default:
return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
}
}
/* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or
* /lib/sysctl.d/sysctl.conf
*/
static int
static NMSettingIP6ConfigPrivacy
ip6_use_tempaddr (void)
{
char *contents = NULL;
@ -3538,12 +3608,13 @@ ip6_use_tempaddr (void)
char *sysctl_data = NULL;
GKeyFile *keyfile;
GError *error = NULL;
int tmp, ret = -1;
gint tmp;
NMSettingIP6ConfigPrivacy ret = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
/* Read file contents to a string. */
if (!g_file_get_contents ("/etc/sysctl.conf", &contents, NULL, NULL))
if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, NULL, NULL))
return -1;
return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
/* Prepend a group so that we can use GKeyFile parser. */
sysctl_data = g_strdup_printf ("%s%s", group_name, contents);
@ -3554,7 +3625,7 @@ ip6_use_tempaddr (void)
tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error);
if (error == NULL)
ret = tmp;
ret = use_tempaddr_clamp (tmp);
done:
g_free (contents);
@ -3591,7 +3662,6 @@ act_stage3_ip6_config_start (NMDevice *self,
NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
NMConnection *connection;
const char *method;
int conf_use_tempaddr;
NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
const char *ip6_privacy_str = "0\n";
GSList *slaves;
@ -3643,8 +3713,21 @@ act_stage3_ip6_config_start (NMDevice *self,
/* Re-enable IPv6 on the interface */
nm_platform_sysctl_set (priv->ip6_disable_ipv6_path, "0");
/* Enable/disable IPv6 Privacy Extensions.
* If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
* use that value instead of per-connection value.
*/
ip6_privacy = ip6_use_tempaddr ();
if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) {
NMSettingIP6Config *s_ip6 = nm_connection_get_setting_ip6_config (connection);
if (s_ip6)
ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6);
}
ip6_privacy = use_tempaddr_clamp (ip6_privacy);
if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
if (!addrconf6_start (self)) {
if (!addrconf6_start (self, ip6_privacy)) {
/* IPv6 might be disabled; allow IPv4 to proceed */
ret = NM_ACT_STAGE_RETURN_STOP;
} else
@ -3672,21 +3755,6 @@ act_stage3_ip6_config_start (NMDevice *self,
/* Other methods (shared) aren't implemented yet */
/* Enable/disable IPv6 Privacy Extensions.
* If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
* use that value instead of per-connection value.
*/
conf_use_tempaddr = ip6_use_tempaddr ();
if (conf_use_tempaddr >= 0)
ip6_privacy = conf_use_tempaddr;
else {
NMSettingIP6Config *s_ip6 = nm_connection_get_setting_ip6_config (connection);
if (s_ip6)
ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6);
}
ip6_privacy = CLAMP (ip6_privacy, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
switch (ip6_privacy) {
case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN:
case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: