From 84dc64c8affd658077fa3967d42374d6c3a2951c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 6 Jan 2014 21:05:00 +0100 Subject: [PATCH 1/5] core/rdisc: limit the number of autoconf addresses to 'max_addresses' NetworkManager uses the sysctl value 'max_addresses' as the kernel does. There is however a difference in what addresses are taken into account. The kernel counts all addresses on the interface (including temporary, private addresses and user configured ones). NM instead only limits the number of public autoconf addresses to 'max_addresses'. This is because it is difficult for NM to count all addresses (which can come from different sources) and it is not necessarily a more logical behavior. Only be aware, that NM uses the same config value as the kernel, but counts differently. Especially, the kernel might reach the limit earlier then NM in the presence of temporary addresses or addresses not from SLAAC. Note, that the kernel uses 'max_addresses' only to limit public, autoconf addresses. So this limit does not affect NM adding as many addresses as it wants. Signed-off-by: Thomas Haller --- src/devices/nm-device.c | 23 ++++++++++++++++++++++- src/rdisc/nm-fake-rdisc.c | 3 ++- src/rdisc/nm-fake-rdisc.h | 2 +- src/rdisc/nm-lndp-rdisc.c | 10 +++++++++- src/rdisc/nm-lndp-rdisc.h | 2 +- src/rdisc/nm-rdisc.h | 1 + src/rdisc/tests/rdisc.c | 4 ++-- 7 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 35103e54fa..90ffff4b32 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -477,6 +477,21 @@ restore_ip6_properties (NMDevice *self) } } +static gint32 +sysctl_get_ipv6_max_addresses (const char *dev) +{ + gint32 max_addresses = 16; + char *path; + + g_return_val_if_fail (dev && *dev, max_addresses); + + path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/max_addresses", dev); + max_addresses = nm_platform_sysctl_get_int32 (path, max_addresses); + g_free (path); + + return max_addresses; +} + /* * Get driver info from SIOCETHTOOL ioctl() for 'iface' * Returns driver and firmware versions to 'driver_version and' 'firmware_version' @@ -3320,6 +3335,11 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device /* Rebuild address list from router discovery cache. */ nm_ip6_config_reset_addresses (priv->ac_ip6_config); + /* rdisc->addresses contains at most max_addresses entries. + * This is different from what the kernel does, which + * also counts static and temporary addresses when checking + * max_addresses. + **/ for (i = 0; i < rdisc->addresses->len; i++) { NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i); NMPlatformIP6Address address; @@ -3428,7 +3448,8 @@ addrconf6_start (NMDevice *self) priv->ac_ip6_config = NULL; } - priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface); + priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface, + sysctl_get_ipv6_max_addresses (ip_iface)); if (!priv->rdisc) { nm_log_err (LOGD_IP6, "(%s): failed to start router discovery.", ip_iface); return FALSE; diff --git a/src/rdisc/nm-fake-rdisc.c b/src/rdisc/nm-fake-rdisc.c index f39c5a208c..38faa5078a 100644 --- a/src/rdisc/nm-fake-rdisc.c +++ b/src/rdisc/nm-fake-rdisc.c @@ -36,7 +36,7 @@ G_DEFINE_TYPE (NMFakeRDisc, nm_fake_rdisc, NM_TYPE_RDISC) /******************************************************************/ NMRDisc * -nm_fake_rdisc_new (int ifindex, const char *ifname) +nm_fake_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses) { NMRDisc *rdisc = g_object_new (NM_TYPE_FAKE_RDISC, NULL); @@ -44,6 +44,7 @@ nm_fake_rdisc_new (int ifindex, const char *ifname) rdisc->ifindex = ifindex; rdisc->ifname = g_strdup (ifname); + rdisc->max_addresses = max_addresses; return rdisc; } diff --git a/src/rdisc/nm-fake-rdisc.h b/src/rdisc/nm-fake-rdisc.h index 248283b22b..cff9ee4494 100644 --- a/src/rdisc/nm-fake-rdisc.h +++ b/src/rdisc/nm-fake-rdisc.h @@ -44,6 +44,6 @@ typedef struct { GType nm_fake_rdisc_get_type (void); -NMRDisc *nm_fake_rdisc_new (int ifindex, const char *ifname); +NMRDisc *nm_fake_rdisc_new (int ifindex, const char *ifname, gint32 max_addressses); #endif /* NM_FAKE_RDISC_H */ diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index 04b52067fc..e7124f1b7a 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -48,7 +48,7 @@ G_DEFINE_TYPE (NMLNDPRDisc, nm_lndp_rdisc, NM_TYPE_RDISC) /******************************************************************/ NMRDisc * -nm_lndp_rdisc_new (int ifindex, const char *ifname) +nm_lndp_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses) { NMRDisc *rdisc; NMLNDPRDiscPrivate *priv; @@ -59,6 +59,7 @@ nm_lndp_rdisc_new (int ifindex, const char *ifname) rdisc->ifindex = ifindex; rdisc->ifname = g_strdup (ifname); + rdisc->max_addresses = max_addresses; priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); error = ndp_open (&priv->ndp); @@ -113,6 +114,13 @@ add_address (NMRDisc *rdisc, const NMRDiscAddress *new) } } + /* we create at most max_addresses autoconf addresses. This is different from + * what the kernel does, because it considers *all* addresses (including + * static and other temporary addresses). + **/ + if (rdisc->max_addresses && rdisc->addresses->len >= rdisc->max_addresses) + return FALSE; + g_array_insert_val (rdisc->addresses, i, *new); return TRUE; } diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h index eb6a1df6d9..30d53db409 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, gint32 max_addresses); #endif /* NM_LNDP_RDISC_H */ diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index 75262d862b..4690a5f5e7 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -106,6 +106,7 @@ typedef struct { int ifindex; char *ifname; GBytes *lladdr; + gint32 max_addresses; NMRDiscDHCPLevel dhcp_level; GArray *gateways; diff --git a/src/rdisc/tests/rdisc.c b/src/rdisc/tests/rdisc.c index fc6e86be08..6a0591af54 100644 --- a/src/rdisc/tests/rdisc.c +++ b/src/rdisc/tests/rdisc.c @@ -12,7 +12,7 @@ main (int argc, char **argv) { GMainLoop *loop; NMRDisc *rdisc; - NMRDisc *(*new) (int ifindex, const char *ifname) = nm_lndp_rdisc_new; + NMRDisc *(*new) (int ifindex, const char *ifname, gint32 max_addresses) = nm_lndp_rdisc_new; int ifindex = 1; char ifname[IF_NAMESIZE]; char mac[6] = { 0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0xee }; @@ -34,7 +34,7 @@ main (int argc, char **argv) } } - rdisc = new (ifindex, ifname); + rdisc = new (ifindex, ifname, 0); if (!rdisc) return EXIT_FAILURE; From 2bc61d1ad3278d4fc38d17bd6178e7e304c6339a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jan 2014 16:07:36 +0100 Subject: [PATCH 2/5] core/platform: workaround new address flag in address_to_string The kernel and libnl adds two new flags IFA_F_MANAGETEMPADDR and IFA_F_NOPREFIXROUTE. Older versions of libnl do not recognize this flag, so add a workaround to nm_platform_ip6_address_to_string() to show "mngtmpaddr" and "noprefixroute", respectively. Also, add function nm_platform_check_support_libnl_extended_ifa_flags() that checks whether libnl supports extended ifa_flags that were added recently. Extended flags and the two ifa-flags above were added to libnl in close succession. Signed-off-by: Thomas Haller --- src/platform/nm-platform.c | 41 +++++++++++++++++++++++++++++++++++++- src/platform/nm-platform.h | 2 ++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 44d50c2dca..25f73fdf88 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -31,6 +31,14 @@ #include "nm-logging.h" #include "nm-enum-types.h" +/* 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 + #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate)) @@ -196,6 +204,22 @@ reset_error (void) platform->error = NM_PLATFORM_ERROR_NONE; } +#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" +#define IFA_F_NOPREFIXROUTE_STR "noprefixroute" +gboolean +nm_platform_check_support_libnl_extended_ifa_flags () +{ + static int supported = -1; + + /* support for extended ifa-flags was added together + * with the IFA_F_MANAGETEMPADDR flag. So, check if libnl + * is able to parse this flag. */ + if (supported == -1) + supported = rtnl_addr_str2flags (IFA_F_MANAGETEMPADDR_STR) == IFA_F_MANAGETEMPADDR; + + return supported; +} + /******************************************************************/ /** @@ -1842,7 +1866,22 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) s_dev = address->ifindex > 0 ? nm_platform_link_get_name (address->ifindex) : NULL; str_dev = s_dev ? g_strconcat (" dev ", s_dev, NULL) : NULL; - rtnl_addr_flags2str(address->flags, s_flags, sizeof(s_flags)); + rtnl_addr_flags2str(address->flags, s_flags, sizeof (s_flags)); + + /* There are two recent flags IFA_F_MANAGETEMPADDR and IFA_F_NOPREFIXROUTE. + * If libnl does not yet support them, add them by hand. + * These two flags were introduced together with the extended ifa_flags, + * so, check for that. + **/ + if ((address->flags && IFA_F_MANAGETEMPADDR) & !nm_platform_check_support_libnl_extended_ifa_flags ()) { + strncat (s_flags, s_flags[0] ? "," IFA_F_MANAGETEMPADDR_STR : IFA_F_MANAGETEMPADDR_STR, + sizeof (s_flags) - strlen (s_flags) - 1); + } + if ((address->flags && IFA_F_NOPREFIXROUTE) & !nm_platform_check_support_libnl_extended_ifa_flags ()) { + strncat (s_flags, s_flags[0] ? "," IFA_F_NOPREFIXROUTE_STR : IFA_F_NOPREFIXROUTE_STR, + sizeof (s_flags) - strlen (s_flags) - 1); + } + str_flags = s_flags[0] ? g_strconcat (" flags ", s_flags, NULL) : NULL; g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u%s%s%s src %s", diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 6ccaac79e9..bb9cd2efe6 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -465,6 +465,8 @@ int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatform int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b); int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b); +gboolean nm_platform_check_support_libnl_extended_ifa_flags (void); + #define auto_g_free __attribute__((cleanup(put_g_free))) static void __attribute__((unused)) put_g_free (void *ptr) From 7841f9ea0a4efdcb4540628cf65d7d9356b748f7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 7 Jan 2014 17:21:12 +0100 Subject: [PATCH 3/5] core/platform: add check_support_kernel_extended_ifa_flags function The kernel adds a new capability to allow user space to manage temporary IPv6 addresses. We need to detect this capability to act differently, depending on whether NM has an older kernel at hand. This capability got introduced together when extending the ifa_flags to 32 bit. So, we can check the netlink message, whether we have such an nl attribute at hand. Signed-off-by: Thomas Haller --- src/platform/nm-linux-platform.c | 57 ++++++++++++++++++++++++++++++++ src/platform/nm-platform.c | 11 ++++++ src/platform/nm-platform.h | 3 ++ 3 files changed, 71 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f6510eca79..d99cb1d580 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -67,6 +67,8 @@ typedef struct { GUdevClient *udev_client; GHashTable *udev_devices; + + int support_kernel_extended_ifa_flags; } NMLinuxPlatformPrivate; #define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate)) @@ -429,6 +431,45 @@ ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *s /******************************************************************/ +static void +_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr = nlmsg_hdr (msg); + + g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0); + g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR); + + /* the extended address flags are only set for AF_INET6 */ + if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE (they were added together). + **/ + priv->support_kernel_extended_ifa_flags = + nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */) + ? 1 : -1; +} + +static gboolean +check_support_kernel_extended_ifa_flags (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv; + + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + if (priv->support_kernel_extended_ifa_flags == 0) { + g_warn_if_reached (); + priv->support_kernel_extended_ifa_flags = -1; + } + + return priv->support_kernel_extended_ifa_flags > 0; +} + + /* Object type specific utilities */ static const char * @@ -1224,6 +1265,14 @@ event_notification (struct nl_msg *msg, gpointer user_data) int nle; event = nlmsg_hdr (msg)->nlmsg_type; + + if (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) { + /* if kernel support for extended ifa flags is still undecided, use the opportunity + * now and use @msg to decide it. This saves a blocking net link request. + **/ + _check_support_kernel_extended_ifa_flags_init (priv, msg); + } + nl_msg_parse (msg, ref_object, &object); g_return_val_if_fail (object, NL_OK); @@ -2806,6 +2855,12 @@ setup (NMPlatform *platform) g_list_free (devices); g_object_unref (enumerator); + /* request all IPv6 addresses (hopeing that there is at least one), to check for + * the IFA_FLAGS attribute. */ + nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP); + if (nle != 0) + nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle)); + return TRUE; } @@ -2910,4 +2965,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip6_route_delete = ip6_route_delete; platform_class->ip4_route_exists = ip4_route_exists; platform_class->ip6_route_exists = ip6_route_exists; + + platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 25f73fdf88..79eb06523a 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -220,6 +220,17 @@ nm_platform_check_support_libnl_extended_ifa_flags () return supported; } +gboolean +nm_platform_check_support_kernel_extended_ifa_flags () +{ + g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + + if (!klass->check_support_kernel_extended_ifa_flags) + return FALSE; + + return klass->check_support_kernel_extended_ifa_flags (platform); +} + /******************************************************************/ /** diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index bb9cd2efe6..a219a3794c 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -323,6 +323,8 @@ typedef struct { gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric); gboolean (*ip4_route_exists) (NMPlatform *, int ifindex, in_addr_t network, int plen, int metric); gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric); + + gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); } NMPlatformClass; /* NMPlatform signals @@ -466,6 +468,7 @@ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4R int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b); gboolean nm_platform_check_support_libnl_extended_ifa_flags (void); +gboolean nm_platform_check_support_kernel_extended_ifa_flags (void); #define auto_g_free __attribute__((cleanup(put_g_free))) static void __attribute__((unused)) From 39cbe772a67aece69dc30f8f394bba2eea9ca762 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jan 2014 17:03:35 +0100 Subject: [PATCH 4/5] core/rdisc: add autoconf addresses as /64 (instead of /128) This feature needs support from the kernel and libnl. If there is no system support, NM acts as before, adding the autoconf address as /128. It does so, to prevent the kernel from adding a route for this prefix. With system support, we add the address as /64 and set the flag IFA_F_NOPREFIXROUTE. https://bugzilla.redhat.com/show_bug.cgi?id=1044590 https://bugzilla.redhat.com/show_bug.cgi?id=1045118 Signed-off-by: Thomas Haller --- src/devices/nm-device.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 90ffff4b32..c6581b4b61 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "libgsystem.h" #include "nm-glib-compat.h" @@ -74,6 +75,11 @@ #include "nm-device-bond.h" #include "nm-device-team.h" +/* workaround for older libnl version, that does not define this flag. */ +#ifndef IFA_F_NOPREFIXROUTE +#define IFA_F_NOPREFIXROUTE 0x200 +#endif + static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context); #include "nm-device-glue.h" @@ -3313,6 +3319,25 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device NMConnection *connection; int i; NMDeviceStateReason reason; + static int system_support = -1; + guint ifa_flags; + + if (system_support == -1) { + /* + * 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. + **/ + 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?). + **/ + ifa_flags = IFA_F_NOPREFIXROUTE; g_return_if_fail (priv->act_request); connection = nm_device_get_connection (device); @@ -3346,11 +3371,12 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device memset (&address, 0, sizeof (address)); address.address = discovered_address->address; - address.plen = 128; + address.plen = system_support ? 64 : 128; address.timestamp = discovered_address->timestamp; address.lifetime = discovered_address->lifetime; address.preferred = discovered_address->preferred; address.source = NM_PLATFORM_SOURCE_RDISC; + address.flags = ifa_flags; nm_ip6_config_add_address (priv->ac_ip6_config, &address); } From 1dea2714697b8cfe386c6665d7e556d537bebaf0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jan 2014 17:03:35 +0100 Subject: [PATCH 5/5] 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 --- src/devices/nm-device.c | 124 +++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 28 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c6581b4b61..50440d09dd 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -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: