diff options
-rw-r--r-- | src/devices/nm-device.c | 5 | ||||
-rw-r--r-- | src/nm-ip6-config.c | 142 | ||||
-rw-r--r-- | src/nm-ip6-config.h | 4 | ||||
-rw-r--r-- | src/tests/test-ip6-config.c | 68 |
4 files changed, 212 insertions, 7 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d5c4386c5e..18235eb1b9 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3046,6 +3046,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; @@ -6912,7 +6915,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..938f50d104 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -172,8 +172,137 @@ 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_UNSPECIFIED (addr)) + return 0; + if (IN6_IS_ADDR_LOOPBACK (addr)) + return 1; + if (IN6_IS_ADDR_LINKLOCAL (addr)) + return 2; + if (IN6_IS_ADDR_SITELOCAL (addr)) + return 3; + if (IN6_IS_ADDR_V4MAPPED (addr)) + return 4; + if (IN6_IS_ADDR_V4COMPAT (addr)) + return 5; + return 6; +} + +static gboolean +_addresses_sort_cmp_is_slaac (const NMPlatformIP6Address *addr) +{ + if (addr->source == NM_PLATFORM_SOURCE_RDISC) + return TRUE; + if (addr->source == NM_PLATFORM_SOURCE_KERNEL) + return !!(addr->flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); + + return FALSE; +} + +static gint +_addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) +{ + gint p1, p2; + gint slaac1, slaac2; + gboolean perm1, perm2, tent1, tent2; + const NMPlatformIP6Address *a1 = a, *a2 = b; + const NMSettingIP6ConfigPrivacy *use_temporary = user_data; + + /* sort the addresses baded on their source. */ + if (a1->source != a2->source) { + /* SLAAC temporary addresses have NM_PLATFORM_SOURCE_KERNEL, so we ignore + * the source if both addresses are detected as being created from SLAAC. */ + if (!_addresses_sort_cmp_is_slaac (a1) && !_addresses_sort_cmp_is_slaac (a2)) + return a1->source > a2->source ? -1 : 1; + } + + /* First we compare by address type. For example link local will always + * 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; + + /* 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; + + /* addresses that are not from SLAAC, are higher priority */ + slaac1 = (a1->flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); + slaac2 = (a2->flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); + if ((!!slaac1) != (!!slaac2)) + return slaac1 ? 1 : -1; + + if (slaac1 && slaac1 != slaac2) { + /* both addresses are SLAAC, but one is public, one private. */ + + if (*use_temporary == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) { + /* private address has higher priority */ + return slaac1 & IFA_F_TEMPORARY ? -1 : 1; + } + + /* permanent address has higher priority */ + return slaac1 & IFA_F_MANAGETEMPADDR ? -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; + + /* sort addresses lexically */ + return memcmp (&a1->address, &a2->address, sizeof (a2->address)); +} + +static void +_addresses_sort (NMIP6Config *self, NMSettingIP6ConfigPrivacy use_temporary, gboolean *out_changed) +{ + NMIP6ConfigPrivate *priv; + size_t data_len = 0; + char *data_pre = NULL; + + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + if (priv->addresses->len <= 1) { + if (out_changed) + *out_changed = FALSE; + return; + } + + if (out_changed) { + 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, &use_temporary); + + if (out_changed) { + *out_changed = memcmp (data_pre, priv->addresses->data, data_len) != 0; + g_free (data_pre); + } +} + +gboolean +nm_ip6_config_addresses_sort (NMIP6Config *self, NMSettingIP6ConfigPrivacy use_temporary) +{ + gboolean changed; + + _addresses_sort (self, use_temporary, &changed); + + if (changed) + _NOTIFY (self, PROP_ADDRESSES); + return changed; +} + 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 +310,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 +361,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); + + _addresses_sort (config, use_temporary, NULL); /* 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 8d69f4f486..6755b53db4 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -231,6 +231,73 @@ test_add_route_with_source (void) g_object_unref (a); } +static void +test_nm_ip6_config_addresses_sort_test (NMIP6Config *config, NMSettingIP6ConfigPrivacy use_tempaddr, int repeat) +{ + int addr_count = nm_ip6_config_get_num_addresses (config); + int i; + NMIP6Config *copy = nm_ip6_config_new (); + NMIP6Config *copy2 = nm_ip6_config_new (); + int *idx = NULL; + + while (repeat-- > 0) { + nm_ip6_config_replace (copy, config, NULL); + + nm_ip6_config_reset_addresses (copy); + + if (addr_count > 0) { + idx = g_new0 (int, addr_count); + + for (i = 0; i < addr_count; i++) + idx[i] = i; + for (i = 0; i < addr_count; i++) { + int j = g_rand_int_range (nmtst_get_rand0 (), i, addr_count); + int tmp; + + tmp = idx[i]; + idx[i] = idx[j]; + idx[j] = tmp; + } + + for (i = 0; i < addr_count; i++) + nm_ip6_config_add_address (copy, nm_ip6_config_get_address (config, idx[i])); + + g_free (idx); + } + + nm_ip6_config_addresses_sort (copy, use_tempaddr); + + /* check equality using nm_ip6_config_equal() */ + g_assert (nm_ip6_config_equal (copy, config)); + + /* also check equality using nm_ip6_config_replace() */ + nm_ip6_config_replace (copy2, config, NULL); + g_assert (nm_ip6_config_replace (copy2, copy, NULL) == FALSE); + } + + 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::6", NULL, 64, 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); + test_nm_ip6_config_addresses_sort_test (config, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, 3); + +#undef ADDR_ADD + g_object_unref (config); +} + /*******************************************/ NMTST_INIT(); @@ -246,6 +313,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 (); } |