From c65ed2dd6c69be36f48477e57c220f342a69b520 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 14 Mar 2014 17:49:49 +0100 Subject: [PATCH 1/3] libnm-util: add private header file nm-test-utils.h This is intended to contain utility functions for tests. It will be header only (containing inline functions). Signed-off-by: Thomas Haller --- libnm-util/Makefile.am | 3 +- libnm-util/nm-test-utils.h | 261 +++++++++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 libnm-util/nm-test-utils.h diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am index 84f4aa303d..f586f91825 100644 --- a/libnm-util/Makefile.am +++ b/libnm-util/Makefile.am @@ -58,7 +58,8 @@ libnm_util_la_private_headers = \ nm-param-spec-specialized.h \ nm-util-private.h \ nm-utils-private.h \ - nm-setting-private.h + nm-setting-private.h \ + nm-test-utils.h libnm_util_la_csources = \ crypto.c \ diff --git a/libnm-util/nm-test-utils.h b/libnm-util/nm-test-utils.h new file mode 100644 index 0000000000..a3de61ad64 --- /dev/null +++ b/libnm-util/nm-test-utils.h @@ -0,0 +1,261 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NM_TEST_UTILS_H__ +#define __NM_TEST_UTILS_H__ + + +#include +#include + + +struct __nmtst_internal +{ + GRand *rand0; + guint32 rand_seed; + GRand *rand; +}; + +extern struct __nmtst_internal __nmtst_internal; + +#define NMTST_DEFINE() \ + struct __nmtst_internal __nmtst_internal = { 0 }; + +inline void nmtst_init (int *argc, char ***argv); + +inline void +nmtst_init (int *argc, char ***argv) +{ + g_assert (!__nmtst_internal.rand0); + + g_assert (!((!!argc) ^ (!!argv))); + if (argc) { + /* g_test_init() is a variadic function, so we cannot pass it + * (variadic) arguments. If you need to pass additional parameters, + * call nmtst_init() with argc==NULL and call g_test_init() yourself. */ + g_test_init (argc, argv, NULL); + } + + g_type_init (); + + __nmtst_internal.rand0 = g_rand_new_with_seed (0); +} + +inline GRand *nmtst_get_rand0 (void); + +inline GRand * +nmtst_get_rand0 () +{ + g_assert (__nmtst_internal.rand0); + return __nmtst_internal.rand0; +} + +inline GRand *nmtst_get_rand (void); + +inline GRand * +nmtst_get_rand () +{ + if (G_UNLIKELY (!__nmtst_internal.rand)) { + guint32 seed; + const char *str; + + if ((str = g_getenv ("NMTST_SEED_RAND"))) { + gchar *s; + gint64 i; + + i = g_ascii_strtoll (str, &s, 0); + g_assert (s[0] == '\0' && i >= 0 && i < G_MAXINT32); + + seed = i; + __nmtst_internal.rand = g_rand_new_with_seed (seed); + } else { + __nmtst_internal.rand = g_rand_new (); + + seed = g_rand_int (__nmtst_internal.rand); + g_rand_set_seed (__nmtst_internal.rand, seed); + } + __nmtst_internal.rand_seed = seed; + + g_message (">> initialize nmtst_get_rand() with seed=%u", seed); + } + return __nmtst_internal.rand; +} + +#define NMTST_SWAP(x,y) \ + G_STMT_START { \ + char __nmtst_swap_temp[sizeof(x) == sizeof(y) ? (signed) sizeof(x) : -1]; \ + memcpy(__nmtst_swap_temp, &y, sizeof(x)); \ + memcpy(&y, &x, sizeof(x)); \ + memcpy(&x, __nmtst_swap_temp, sizeof(x)); \ + } G_STMT_END + +inline guint32 nmtst_inet4_from_string (const char *str); +inline guint32 +nmtst_inet4_from_string (const char *str) +{ + guint32 addr; + int success; + + if (!str) + return 0; + + success = inet_pton (AF_INET, str, &addr); + + g_assert (success == 1); + + return addr; +} + +inline struct in6_addr *nmtst_inet6_from_string (const char *str); +inline struct in6_addr * +nmtst_inet6_from_string (const char *str) +{ + static struct in6_addr addr; + int success; + + if (!str) + addr = in6addr_any; + else { + success = inet_pton (AF_INET6, str, &addr); + g_assert (success == 1); + } + + return &addr; +} + +#ifdef NM_PLATFORM_H + +inline NMPlatformIP6Address *nmtst_platform_ip6_address (const char *address, const char *peer_address, guint plen); + +inline NMPlatformIP6Address * +nmtst_platform_ip6_address (const char *address, const char *peer_address, guint plen) +{ + static NMPlatformIP6Address addr; + + memset (&addr, 0, sizeof (addr)); + addr.address = *nmtst_inet6_from_string (address); + addr.peer_address = *nmtst_inet6_from_string (peer_address); + addr.plen = plen; + + return &addr; +} + + +inline NMPlatformIP6Address * +nmtst_platform_ip6_address_full (const char *address, const char *peer_address, guint plen, + int ifindex, NMPlatformSource source, guint32 timestamp, + guint32 lifetime, guint32 preferred, guint flags); + +inline NMPlatformIP6Address * +nmtst_platform_ip6_address_full (const char *address, const char *peer_address, guint plen, + int ifindex, NMPlatformSource source, guint32 timestamp, + guint32 lifetime, guint32 preferred, guint flags) +{ + NMPlatformIP6Address *addr = nmtst_platform_ip6_address (address, peer_address, plen); + + addr->ifindex = ifindex; + addr->source = source; + addr->timestamp = timestamp; + addr->lifetime = lifetime; + addr->preferred = preferred; + addr->flags = flags; + + return addr; +} + + +inline NMPlatformIP6Route * nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway); + +inline NMPlatformIP6Route * +nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway) +{ + static NMPlatformIP6Route route; + + memset (&route, 0, sizeof (route)); + route.network = *nmtst_inet6_from_string (network); + route.plen = plen; + route.gateway = *nmtst_inet6_from_string (gateway); + + return &route; +} + + +inline NMPlatformIP6Route * +nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gateway, + int ifindex, NMPlatformSource source, + guint metric, guint mss); + +inline NMPlatformIP6Route * +nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gateway, + int ifindex, NMPlatformSource source, + guint metric, guint mss) +{ + NMPlatformIP6Route *route = nmtst_platform_ip6_route (network, plen, gateway); + + route->ifindex = ifindex; + route->source = source; + route->metric = metric; + route->mss = mss; + + return route; +} + +#endif + + +#ifdef NM_IP4_CONFIG_H + +inline NMIP4Config *nmtst_ip4_config_clone (NMIP4Config *config); + +inline NMIP4Config * +nmtst_ip4_config_clone (NMIP4Config *config) +{ + NMIP4Config *copy = nm_ip4_config_new (); + + g_assert (copy); + g_assert (config); + nm_ip4_config_replace (copy, config, NULL); + return copy; +} + +#endif + + +#ifdef NM_IP6_CONFIG_H + +inline NMIP6Config *nmtst_ip6_config_clone (NMIP6Config *config); + +inline NMIP6Config * +nmtst_ip6_config_clone (NMIP6Config *config) +{ + NMIP6Config *copy = nm_ip6_config_new (); + + g_assert (copy); + g_assert (config); + nm_ip6_config_replace (copy, config, NULL); + return copy; +} + +#endif + + +#endif /* __NM_TEST_UTILS_H__ */ + From 65bc042e8f1dc0f7b757d6450e436697d7e7b7b1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 14 Mar 2014 18:14:52 +0100 Subject: [PATCH 2/3] test: make use of new libnm-util/nm-test-utils.h header Signed-off-by: Thomas Haller --- src/tests/test-ip6-config.c | 84 ++++++++++--------------------------- 1 file changed, 21 insertions(+), 63 deletions(-) diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index c79ac20a17..b804891d9f 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -24,61 +24,24 @@ #include "nm-ip6-config.h" -static void -addr_init (NMPlatformIP6Address *a, const char *addr, const char *peer, guint plen) -{ - memset (a, 0, sizeof (*a)); - g_assert (inet_pton (AF_INET6, addr, (void *) &a->address) == 1); - if (peer) - g_assert (inet_pton (AF_INET6, peer, (void *) &a->peer_address) == 1); - a->plen = plen; -} - -static void -route_new (NMPlatformIP6Route *route, const char *network, guint plen, const char *gw) -{ - g_assert (route); - memset (route, 0, sizeof (*route)); - g_assert (inet_pton (AF_INET6, network, (void *) &route->network) == 1); - route->plen = plen; - if (gw) - g_assert (inet_pton (AF_INET6, gw, (void *) &route->gateway) == 1); -} - -static void -addr_to_num (const char *addr, struct in6_addr *out_addr) -{ - memset (out_addr, 0, sizeof (*out_addr)); - g_assert (inet_pton (AF_INET6, addr, (void *) out_addr) == 1); -} +#include "nm-test-utils.h" static NMIP6Config * build_test_config (void) { NMIP6Config *config; - NMPlatformIP6Address addr; - NMPlatformIP6Route route; - struct in6_addr tmp; /* Build up the config to subtract */ config = nm_ip6_config_new (); - addr_init (&addr, "abcd:1234:4321::cdde", "1:2:3:4::5", 64); - nm_ip6_config_add_address (config, &addr); + nm_ip6_config_add_address (config, nmtst_platform_ip6_address ("abcd:1234:4321::cdde", "1:2:3:4::5", 64)); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2")); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001:abba::", 16, "2001:abba::2234")); - route_new (&route, "abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); - nm_ip6_config_add_route (config, &route); + nm_ip6_config_set_gateway (config, nmtst_inet6_from_string ("3001:abba::3234")); - route_new (&route, "2001:abba::", 16, "2001:abba::2234"); - nm_ip6_config_add_route (config, &route); - - addr_to_num ("3001:abba::3234", &tmp); - nm_ip6_config_set_gateway (config, &tmp); - - addr_to_num ("1:2:3:4::1", &tmp); - nm_ip6_config_add_nameserver (config, &tmp); - addr_to_num ("1:2:3:4::2", &tmp); - nm_ip6_config_add_nameserver (config, &tmp); + nm_ip6_config_add_nameserver (config, nmtst_inet6_from_string ("1:2:3:4::1")); + nm_ip6_config_add_nameserver (config, nmtst_inet6_from_string ("1:2:3:4::2")); nm_ip6_config_add_domain (config, "foobar.com"); nm_ip6_config_add_domain (config, "baz.com"); nm_ip6_config_add_search (config, "blahblah.com"); @@ -91,8 +54,6 @@ static void test_subtract (void) { NMIP6Config *src, *dst; - NMPlatformIP6Address addr; - NMPlatformIP6Route route; const NMPlatformIP6Address *test_addr; const NMPlatformIP6Route *test_route; const char *expected_addr = "1122:3344:5566::7788"; @@ -110,15 +71,12 @@ test_subtract (void) /* add a couple more things to the test config */ dst = build_test_config (); - addr_init (&addr, expected_addr, NULL, expected_addr_plen); - nm_ip6_config_add_address (dst, &addr); + nm_ip6_config_add_address (dst, nmtst_platform_ip6_address (expected_addr, NULL, expected_addr_plen)); + nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop)); - route_new (&route, expected_route_dest, expected_route_plen, expected_route_next_hop); - nm_ip6_config_add_route (dst, &route); - - addr_to_num ("2222:3333:4444::5555", &expected_ns1); + expected_ns1 = *nmtst_inet6_from_string ("2222:3333:4444::5555"); nm_ip6_config_add_nameserver (dst, &expected_ns1); - addr_to_num ("2222:3333:4444::5556", &expected_ns2); + expected_ns2 = *nmtst_inet6_from_string ("2222:3333:4444::5556"); nm_ip6_config_add_nameserver (dst, &expected_ns2); nm_ip6_config_add_domain (dst, expected_domain); @@ -130,7 +88,7 @@ test_subtract (void) g_assert_cmpuint (nm_ip6_config_get_num_addresses (dst), ==, 1); test_addr = nm_ip6_config_get_address (dst, 0); g_assert (test_addr != NULL); - addr_to_num (expected_addr, &tmp); + tmp = *nmtst_inet6_from_string (expected_addr); g_assert (memcmp (&test_addr->address, &tmp, sizeof (tmp)) == 0); g_assert (memcmp (&test_addr->peer_address, &in6addr_any, sizeof (tmp)) == 0); g_assert_cmpuint (test_addr->plen, ==, expected_addr_plen); @@ -141,10 +99,10 @@ test_subtract (void) test_route = nm_ip6_config_get_route (dst, 0); g_assert (test_route != NULL); - addr_to_num (expected_route_dest, &tmp); + tmp = *nmtst_inet6_from_string (expected_route_dest); g_assert (memcmp (&test_route->network, &tmp, sizeof (tmp)) == 0); g_assert_cmpuint (test_route->plen, ==, expected_route_plen); - addr_to_num (expected_route_next_hop, &tmp); + tmp = *nmtst_inet6_from_string (expected_route_next_hop); g_assert (memcmp (&test_route->gateway, &tmp, sizeof (tmp)) == 0); g_assert_cmpuint (nm_ip6_config_get_num_nameservers (dst), ==, 2); @@ -171,7 +129,7 @@ test_compare_with_source (void) b = nm_ip6_config_new (); /* Address */ - addr_init (&addr, "1122:3344:5566::7788", NULL, 64); + addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64); addr.source = NM_PLATFORM_SOURCE_USER; nm_ip6_config_add_address (a, &addr); @@ -179,7 +137,7 @@ test_compare_with_source (void) nm_ip6_config_add_address (b, &addr); /* Route */ - route_new (&route, "abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); + route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); route.source = NM_PLATFORM_SOURCE_USER; nm_ip6_config_add_route (a, &route); @@ -203,7 +161,7 @@ test_add_address_with_source (void) a = nm_ip6_config_new (); /* Test that a higher priority source is not overwritten */ - addr_init (&addr, "1122:3344:5566::7788", NULL, 64); + addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64); addr.source = NM_PLATFORM_SOURCE_USER; nm_ip6_config_add_address (a, &addr); @@ -243,7 +201,7 @@ test_add_route_with_source (void) a = nm_ip6_config_new (); /* Test that a higher priority source is not overwritten */ - route_new (&route, "abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); + route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); route.source = NM_PLATFORM_SOURCE_USER; nm_ip6_config_add_route (a, &route); @@ -275,12 +233,12 @@ test_add_route_with_source (void) /*******************************************/ +NMTST_DEFINE(); + int main (int argc, char **argv) { - g_test_init (&argc, &argv, NULL); - - g_type_init (); + nmtst_init (&argc, &argv); g_test_add_func ("/ip6-config/subtract", test_subtract); g_test_add_func ("/ip6-config/compare-with-source", test_compare_with_source); From 86f8066177ffdfd2890774f4f230919dfd322c1d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 14 Mar 2014 14:08:51 +0100 Subject: [PATCH 3/3] core: sort IPv6 addresses (add nm_ip6_config_addresses_sort()) Clients such as gnome-control-center or nm-applet show at some places only one (IPv6) address. They most likely just pick the first address from the list of addresses, so we should order them. Sorting has the advantage to make the order deterministic -- contrary to before where the order depended on run time conditions. Note, that it might be desirable to show the address that the kernel will use as source address for new connections. However, this depends on routing and cannot be easily determined in general. Still, the ordering tries to account for this and sorts the addresses accordingly. https://bugzilla.gnome.org/show_bug.cgi?id=726525 Signed-off-by: Thomas Haller --- src/devices/nm-device.c | 5 +- src/nm-ip6-config.c | 120 ++++++++++++++++++++++++++++++++++-- src/nm-ip6-config.h | 4 +- src/tests/test-ip6-config.c | 87 ++++++++++++++++++++++++++ 4 files changed, 209 insertions(+), 7 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 817b924334..b6e5a20203 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3062,6 +3062,9 @@ ip6_config_merge_and_apply (NMDevice *self, if (connection) nm_ip6_config_merge_setting (composite, nm_connection_get_setting_ip6_config (connection)); + nm_ip6_config_addresses_sort (composite, + priv->rdisc ? priv->rdisc_use_tempaddr : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + success = nm_device_set_ip6_config (self, composite, commit, out_reason); g_object_unref (composite); return success; @@ -7037,7 +7040,7 @@ update_ip_config (NMDevice *self, gboolean initial) /* IPv6 */ g_clear_object (&priv->ext_ip6_config); - priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf); + priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); if (priv->ext_ip6_config) { /* Check this before modifying ext_ip6_config */ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 8ca7c0cd05..36f5e72fdc 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -172,8 +172,115 @@ routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, (!consider_gateway_and_metric || (IN6_ARE_ADDR_EQUAL (&a->gateway, &b->gateway) && a->metric == b->metric)); } +static gint +_addresses_sort_cmp_get_prio (const struct in6_addr *addr) +{ + if (IN6_IS_ADDR_V4MAPPED (addr)) + return 0; + if (IN6_IS_ADDR_V4COMPAT (addr)) + return 1; + if (IN6_IS_ADDR_UNSPECIFIED (addr)) + return 2; + if (IN6_IS_ADDR_LOOPBACK (addr)) + return 3; + if (IN6_IS_ADDR_LINKLOCAL (addr)) + return 4; + if (IN6_IS_ADDR_SITELOCAL (addr)) + return 5; + return 6; +} + +static gint +_addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) +{ + gint p1, p2, c; + gboolean perm1, perm2, tent1, tent2; + gboolean ipv6_privacy1, ipv6_privacy2; + const NMPlatformIP6Address *a1 = a, *a2 = b; + + /* tentative addresses are always sorted back... */ + /* sort tentative addresses after non-tentative. */ + tent1 = (a1->flags & IFA_F_TENTATIVE); + tent2 = (a2->flags & IFA_F_TENTATIVE); + if (tent1 != tent2) + return tent1 ? 1 : -1; + + /* Sort by address type. For example link local will + * be sorted *after* site local or global. */ + p1 = _addresses_sort_cmp_get_prio (&a1->address); + p2 = _addresses_sort_cmp_get_prio (&a2->address); + if (p1 != p2) + return p1 > p2 ? -1 : 1; + + ipv6_privacy1 = !!(a1->flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); + ipv6_privacy2 = !!(a2->flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); + if (ipv6_privacy1 || ipv6_privacy2) { + gboolean prefer_temp = ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR; + gboolean public1 = TRUE, public2 = TRUE; + + if (ipv6_privacy1) { + if (a1->flags & IFA_F_TEMPORARY) + public1 = prefer_temp; + else + public1 = !prefer_temp; + } + if (ipv6_privacy2) { + if (a2->flags & IFA_F_TEMPORARY) + public2 = prefer_temp; + else + public2 = !prefer_temp; + } + + if (public1 != public2) + return public1 ? -1 : 1; + } + + /* Sort the addresses based on their source. */ + if (a1->source != a2->source) + return a1->source > a2->source ? -1 : 1; + + /* sort permanent addresses before non-permanent. */ + perm1 = (a1->flags & IFA_F_PERMANENT); + perm2 = (a2->flags & IFA_F_PERMANENT); + if (perm1 != perm2) + return perm1 ? -1 : 1; + + /* finally sort addresses lexically */ + c = memcmp (&a1->address, &a2->address, sizeof (a2->address)); + return c != 0 ? c : memcmp (a1, a2, sizeof (*a1)); +} + +gboolean +nm_ip6_config_addresses_sort (NMIP6Config *self, NMSettingIP6ConfigPrivacy use_temporary) +{ + NMIP6ConfigPrivate *priv; + size_t data_len = 0; + char *data_pre = NULL; + gboolean changed; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + if (priv->addresses->len > 1) { + data_len = priv->addresses->len * g_array_get_element_size (priv->addresses); + data_pre = g_new (char, data_len); + memcpy (data_pre, priv->addresses->data, data_len); + + g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary)); + + changed = memcmp (data_pre, priv->addresses->data, data_len) != 0; + g_free (data_pre); + + if (changed) { + _NOTIFY (self, PROP_ADDRESSES); + return TRUE; + } + } + return FALSE; +} + NMIP6Config * -nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf) +nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary) { NMIP6Config *config; NMIP6ConfigPrivate *priv; @@ -181,6 +288,7 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf) guint lowest_metric = G_MAXUINT; struct in6_addr old_gateway = IN6ADDR_ANY_INIT; gboolean has_gateway = FALSE; + gboolean notify_nameservers = FALSE; /* Slaves have no IP configuration */ if (nm_platform_link_get_master (ifindex) > 0) @@ -231,12 +339,14 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf) /* If the interface has the default route, and has IPv6 addresses, capture * nameservers from /etc/resolv.conf. */ - if (priv->addresses->len && has_gateway && capture_resolv_conf) { - if (nm_ip6_config_capture_resolv_conf (priv->nameservers, NULL)) - _NOTIFY (config, PROP_NAMESERVERS); - } + if (priv->addresses->len && has_gateway && capture_resolv_conf) + notify_nameservers = nm_ip6_config_capture_resolv_conf (priv->nameservers, NULL); + + g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary)); /* actually, nobody should be connected to the signal, just to be sure, notify */ + if (notify_nameservers) + _NOTIFY (config, PROP_NAMESERVERS); _NOTIFY (config, PROP_ADDRESSES); _NOTIFY (config, PROP_ROUTES); if (!IN6_ARE_ADDR_EQUAL (&priv->gateway, &old_gateway)) diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index eb93d0789f..32a3b21fd0 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -58,7 +58,7 @@ void nm_ip6_config_export (NMIP6Config *config); const char * nm_ip6_config_get_dbus_path (const NMIP6Config *config); /* Integration with nm-platform and nm-setting */ -NMIP6Config *nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf); +NMIP6Config *nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); gboolean nm_ip6_config_commit (const NMIP6Config *config, int ifindex, int priority); void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIP6Config *setting); void nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *setting); @@ -83,6 +83,8 @@ void nm_ip6_config_del_address (NMIP6Config *config, guint i); guint nm_ip6_config_get_num_addresses (const NMIP6Config *config); const NMPlatformIP6Address *nm_ip6_config_get_address (const NMIP6Config *config, guint i); gboolean nm_ip6_config_address_exists (const NMIP6Config *config, const NMPlatformIP6Address *address); +gboolean nm_ip6_config_addresses_sort (NMIP6Config *config, NMSettingIP6ConfigPrivacy use_temporary); + /* Routes */ void nm_ip6_config_reset_routes (NMIP6Config *config); diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index b804891d9f..21fa9b0c7a 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -231,6 +231,92 @@ test_add_route_with_source (void) g_object_unref (a); } +static void +test_nm_ip6_config_addresses_sort_check (NMIP6Config *config, NMSettingIP6ConfigPrivacy use_tempaddr, int repeat) +{ + int addr_count = nm_ip6_config_get_num_addresses (config); + int i, irepeat; + NMIP6Config *copy = nmtst_ip6_config_clone (config); + NMIP6Config *copy2 = nmtst_ip6_config_clone (config); + int *idx = g_new (int, addr_count); + + /* initialize the array of indeces, and keep shuffling them for every @repeat iteration. */ + for (i = 0; i < addr_count; i++) + idx[i] = i; + + for (irepeat = 0; irepeat < repeat; irepeat++) { + /* randomly shuffle the addresses. */ + nm_ip6_config_reset_addresses (copy); + for (i = 0; i < addr_count; i++) { + int j = g_rand_int_range (nmtst_get_rand (), i, addr_count); + + NMTST_SWAP (idx[i], idx[j]); + nm_ip6_config_add_address (copy, nm_ip6_config_get_address (config, idx[i])); + } + + /* reorder them again */ + nm_ip6_config_addresses_sort (copy, use_tempaddr); + + /* check equality using nm_ip6_config_equal() */ + if (!nm_ip6_config_equal (copy, config)) { + g_message ("%s", "SORTING yields unexpected output:"); + for (i = 0; i < addr_count; i++) { + g_message (" >> [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i))); + g_message (" << [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (copy, i))); + } + g_assert_not_reached (); + } + + /* also check equality using nm_ip6_config_replace() */ + g_assert (nm_ip6_config_replace (copy2, copy, NULL) == FALSE); + } + + g_free (idx); + g_object_unref (copy); + g_object_unref (copy2); +} + +static void +test_nm_ip6_config_addresses_sort (void) +{ + NMIP6Config *config = build_test_config (); + +#define ADDR_ADD(...) nm_ip6_config_add_address (config, nmtst_platform_ip6_address_full (__VA_ARGS__)) + + nm_ip6_config_reset_addresses (config); + ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::6", NULL, 64, 0, NM_PLATFORM_SOURCE_RDISC, 0, 0, 0, IFA_F_MANAGETEMPADDR); + ADDR_ADD("2607:f0d0:1002:51::3", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, IFA_F_SECONDARY); + ADDR_ADD("2607:f0d0:1002:51::8", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, IFA_F_SECONDARY); + ADDR_ADD("2607:f0d0:1002:51::0", NULL, 64, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, IFA_F_SECONDARY); + ADDR_ADD("fec0::1", NULL, 128, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("::1", NULL, 128, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::2", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, IFA_F_TENTATIVE); + test_nm_ip6_config_addresses_sort_check (config, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, 8); + test_nm_ip6_config_addresses_sort_check (config, NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED, 8); + test_nm_ip6_config_addresses_sort_check (config, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, 8); + + nm_ip6_config_reset_addresses (config); + ADDR_ADD("2607:f0d0:1002:51::3", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, IFA_F_SECONDARY); + ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::8", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, IFA_F_SECONDARY); + ADDR_ADD("2607:f0d0:1002:51::0", NULL, 64, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, IFA_F_SECONDARY); + ADDR_ADD("2607:f0d0:1002:51::6", NULL, 64, 0, NM_PLATFORM_SOURCE_RDISC, 0, 0, 0, IFA_F_MANAGETEMPADDR); + ADDR_ADD("fec0::1", NULL, 128, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_PLATFORM_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("::1", NULL, 128, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::2", NULL, 64, 0, NM_PLATFORM_SOURCE_USER, 0, 0, 0, IFA_F_TENTATIVE); + test_nm_ip6_config_addresses_sort_check (config, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, 8); + +#undef ADDR_ADD + g_object_unref (config); +} + /*******************************************/ NMTST_DEFINE(); @@ -244,6 +330,7 @@ main (int argc, char **argv) g_test_add_func ("/ip6-config/compare-with-source", test_compare_with_source); g_test_add_func ("/ip6-config/add-address-with-source", test_add_address_with_source); g_test_add_func ("/ip6-config/add-route-with-source", test_add_route_with_source); + g_test_add_func ("/ip6-config/test_nm_ip6_config_addresses_sort", test_nm_ip6_config_addresses_sort); return g_test_run (); }