diff options
author | Thomas Haller <thaller@redhat.com> | 2018-02-09 17:40:09 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-02-09 17:40:09 +0100 |
commit | fa41e5852cebb3ba5f6dde880ef792d361cc7fe9 (patch) | |
tree | 66340a955600ddd74885d60698c2f64aebc32981 | |
parent | 86a18b2df2dafdd6cdb120e07509d473d498a591 (diff) | |
parent | 6d8a636563b0ddbf2b484473210bf3bcf41b47a7 (diff) | |
download | NetworkManager-fa41e5852cebb3ba5f6dde880ef792d361cc7fe9.tar.gz |
core: merge branch 'th/ip6-temp-addr-sync-rh1542609'
https://github.com/NetworkManager/NetworkManager/pull/65
https://bugzilla.redhat.com/show_bug.cgi?id=1542609
-rw-r--r-- | src/devices/nm-device.c | 58 | ||||
-rw-r--r-- | src/ndisc/nm-ndisc.c | 13 | ||||
-rw-r--r-- | src/nm-core-utils.c | 52 | ||||
-rw-r--r-- | src/nm-core-utils.h | 11 | ||||
-rw-r--r-- | src/nm-ip6-config.c | 2 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 7 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 293 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 8 | ||||
-rw-r--r-- | src/platform/nmp-object.c | 61 | ||||
-rw-r--r-- | src/platform/nmp-object.h | 10 |
10 files changed, 348 insertions, 167 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ea2c426e19..8b99fc0ba8 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2816,6 +2816,8 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) { const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter.current->obj); NMNDiscAddress *ndisc_addr; + guint32 lifetime, preferred; + gint32 base; if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) continue; @@ -2827,12 +2829,28 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) if (addr->plen != 64) continue; + /* resolve the timestamps relative to a new base. + * + * Note that for convenience, platform @addr might have timestamp and/or + * lifetime unset. We don't allow that flexibility for ndisc and require + * well defined timestamps. */ + if (addr->timestamp) { + nm_assert (addr->timestamp < G_MAXINT32); + base = addr->timestamp; + } else + base = now; + + lifetime = nm_utils_lifetime_get (addr->timestamp, addr->lifetime, addr->preferred, + base, &preferred); + if (!lifetime) + continue; + g_array_set_size (addresses, addresses->len+1); ndisc_addr = &g_array_index (addresses, NMNDiscAddress, addresses->len-1); ndisc_addr->address = addr->address; - ndisc_addr->timestamp = addr->timestamp; - ndisc_addr->lifetime = addr->lifetime; - ndisc_addr->preferred = addr->preferred; + ndisc_addr->timestamp = base; + ndisc_addr->lifetime = lifetime; + ndisc_addr->preferred = preferred; } len = nm_ip6_config_get_num_nameservers (priv->ip6_config); @@ -11511,6 +11529,7 @@ queued_ip6_config_change (gpointer user_data) NMDevicePrivate *priv; GSList *iter; gboolean need_ipv6ll = FALSE; + NMPlatform *platform; g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); @@ -11537,14 +11556,24 @@ queued_ip6_config_change (gpointer user_data) } else update_ip_config (self, AF_INET6, FALSE); - if (priv->state < NM_DEVICE_STATE_DEACTIVATING - && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) { + if ( priv->state < NM_DEVICE_STATE_DEACTIVATING + && (platform = nm_device_get_platform (self)) + && nm_platform_link_get (platform, priv->ifindex)) { /* Handle DAD failures */ - for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) { - const NMPlatformIP6Address *addr = iter->data; - - if (addr->addr_source >= NM_IP_CONFIG_SOURCE_USER) + for (iter = priv->dad6_failed_addrs; iter; iter = iter->next) { + const NMPObject *obj = iter->data; + const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (obj); + const NMPlatformIP6Address *addr2; + + addr2 = NMP_OBJECT_CAST_IP6_ADDRESS (nm_platform_lookup_obj (platform, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + obj)); + if ( addr2 + && ( NM_FLAGS_HAS (addr2->n_ifa_flags, IFA_F_SECONDARY) + || !NM_FLAGS_HAS (addr2->n_ifa_flags, IFA_F_DADFAILED))) { + /* the address still/again exists and is not in DADFAILED state. Skip it. */ continue; + } _LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address", nm_platform_ip6_address_to_string (addr, NULL, 0)); @@ -11567,7 +11596,7 @@ queued_ip6_config_change (gpointer user_data) check_and_add_ipv6ll_addr (self); } - g_slist_free_full (priv->dad6_failed_addrs, g_free); + g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); priv->dad6_failed_addrs = NULL; /* Check if DAD is still pending */ @@ -11623,12 +11652,13 @@ device_ipx_changed (NMPlatform *platform, case NMP_OBJECT_TYPE_IP6_ADDRESS: addr = platform_object; - if ( priv->state > NM_DEVICE_STATE_DISCONNECTED + if ( !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_SECONDARY) + && priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING && ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED) || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))) { - priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs, - g_memdup (addr, sizeof (NMPlatformIP6Address))); + priv->dad6_failed_addrs = g_slist_prepend (priv->dad6_failed_addrs, + (gpointer) nmp_object_ref (NMP_OBJECT_UP_CAST (addr))); } /* fall through */ case NMP_OBJECT_TYPE_IP6_ROUTE: @@ -14684,7 +14714,7 @@ finalize (GObject *object) g_free (priv->hw_addr_perm); g_free (priv->hw_addr_initial); g_slist_free (priv->pending_actions); - g_slist_free_full (priv->dad6_failed_addrs, g_free); + g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); g_clear_pointer (&priv->physical_port_id, g_free); g_free (priv->udi); g_free (priv->iface); diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index a02e4b7692..ede655dd95 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -89,7 +89,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( ); enum { - CONFIG_CHANGED, + CONFIG_RECEIVED, RA_TIMEOUT, LAST_SIGNAL }; @@ -235,7 +235,7 @@ static void _emit_config_change (NMNDisc *self, NMNDiscConfigMap changed) { _config_changed_log (self, changed); - g_signal_emit (self, signals[CONFIG_CHANGED], 0, + g_signal_emit (self, signals[CONFIG_RECEIVED], 0, _data_complete (&NM_NDISC_GET_PRIVATE (self)->rdata), (guint) changed); } @@ -349,6 +349,9 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) NMNDiscDataInternal *rdata = &priv->rdata; guint i; + nm_assert (new); + nm_assert (new->timestamp > 0 && new->timestamp < G_MAXINT32); + for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); @@ -936,7 +939,7 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) get_exp (str_exp, now_ns, gateway)); } for (i = 0; i < rdata->addresses->len; i++) { - NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i); + const NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i); inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr)); _LOGD (" address %s exp %s", addrstr, @@ -1003,7 +1006,7 @@ clean_addresses (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 * rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; for (i = 0; i < rdata->addresses->len; ) { - NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); + const NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); if (item->lifetime != NM_NDISC_INFINITY) { gint32 expiry = get_expiry (item); @@ -1378,7 +1381,7 @@ nm_ndisc_class_init (NMNDiscClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - signals[CONFIG_CHANGED] = + signals[CONFIG_RECEIVED] = g_signal_new (NM_NDISC_CONFIG_RECEIVED, G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST, diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 3a5118508b..f3e9e5f75e 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -3891,12 +3891,11 @@ nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, return t; } -gboolean +guint32 nm_utils_lifetime_get (guint32 timestamp, guint32 lifetime, guint32 preferred, gint32 now, - guint32 *out_lifetime, guint32 *out_preferred) { guint32 t_lifetime, t_preferred; @@ -3904,38 +3903,39 @@ nm_utils_lifetime_get (guint32 timestamp, nm_assert (now >= 0); if (timestamp == 0 && lifetime == 0) { - /* We treat lifetime==0 && timestamp == 0 addresses as permanent addresses to allow easy + /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy * creation of such addresses (without requiring to set the lifetime fields to * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us * to drop an address will have timestamp set. */ - NM_SET_OUT (out_lifetime, NM_PLATFORM_LIFETIME_PERMANENT); NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT); - g_return_val_if_fail (preferred == 0, TRUE); - } else { - if (now <= 0) - now = nm_utils_get_monotonic_timestamp_s (); - t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now); - if (!t_lifetime) { - NM_SET_OUT (out_lifetime, 0); - NM_SET_OUT (out_preferred, 0); - return FALSE; - } - t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now); + g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT); + return NM_PLATFORM_LIFETIME_PERMANENT; + } - NM_SET_OUT (out_lifetime, t_lifetime); - NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime)); + if (now <= 0) + now = nm_utils_get_monotonic_timestamp_s (); - /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now() - * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always - * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. - */ - g_return_val_if_fail ( timestamp != 0 - || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); - g_return_val_if_fail (t_preferred <= t_lifetime, TRUE); + t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now); + if (!t_lifetime) { + NM_SET_OUT (out_preferred, 0); + return 0; } - return TRUE; + + t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now); + + NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime)); + + /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now() + * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always + * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. + */ + g_return_val_if_fail ( timestamp != 0 + || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime); + g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime); + + return t_lifetime; } const char * diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 2f6585e717..38f20b0522 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -408,12 +408,11 @@ guint32 nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, gint32 now); -gboolean nm_utils_lifetime_get (guint32 timestamp, - guint32 lifetime, - guint32 preferred, - gint32 now, - guint32 *out_lifetime, - guint32 *out_preferred); +guint32 nm_utils_lifetime_get (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + gint32 now, + guint32 *out_preferred); gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 692182e183..3fb1c8675a 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -566,7 +566,7 @@ nm_ip6_config_commit (const NMIP6Config *self, ifindex, route_table_sync); - nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE); + nm_platform_ip6_address_sync (platform, ifindex, addresses, FALSE); if (!nm_platform_ip_route_sync (platform, AF_INET6, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 0128362864..1e141eeb90 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -4829,6 +4829,12 @@ link_refresh (NMPlatform *platform, int ifindex) return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } +static void +refresh_all (NMPlatform *platform, NMPObjectType obj_type) +{ + do_request_one_type (platform, obj_type); +} + static gboolean link_set_netns (NMPlatform *platform, int ifindex, @@ -7196,6 +7202,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_add = link_add; platform_class->link_delete = link_delete; + platform_class->refresh_all = refresh_all; platform_class->link_refresh = link_refresh; platform_class->link_set_netns = link_set_netns; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 67051f7769..9ac6a646e1 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1118,6 +1118,21 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) } /** + * nm_platform_refresh_all: + * @self: platform instance + * @obj_type: The object type to request. + * + * Resync and re-request all objects from kernel of a certain @obj_type. + */ +void +nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type) +{ + _CHECK_SELF_VOID (self, klass); + + klass->refresh_all (self, obj_type); +} + +/** * nm_platform_link_refresh: * @self: platform instance * @ifindex: Interface index @@ -3110,35 +3125,68 @@ nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr addr return NMP_OBJECT_CAST_IP6_ADDRESS (obj); } -static int -array_ip6_address_position (const GPtrArray *addresses, - const NMPlatformIP6Address *address, - gint32 now, - gboolean ignore_ll) -{ - guint len = addresses ? addresses->len : 0; - guint i, pos; - - nm_assert (!ignore_ll || !IN6_IS_ADDR_LINKLOCAL (&address->address)); - - for (i = 0, pos = 0; i < len; i++) { - NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]); - - if ( IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) - && candidate->plen == address->plen - && nm_utils_lifetime_get (candidate->timestamp, - candidate->lifetime, - candidate->preferred, - now, - NULL, - NULL)) - return pos; - - if (!ignore_ll || !IN6_IS_ADDR_LINKLOCAL (&candidate->address)) - pos++; +static gboolean +_addr_array_clean_expired (int addr_family, int ifindex, GPtrArray *array, guint32 now, GHashTable **idx) +{ + guint i; + gboolean any_addrs = FALSE; + + nm_assert_addr_family (addr_family); + nm_assert (ifindex > 0); + nm_assert (now > 0); + + if (!array) + return FALSE; + + /* remove all addresses that are already expired. */ + for (i = 0; i < array->len; i++) { + const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS (array->pdata[i]); + +#if NM_MORE_ASSERTS > 10 + nm_assert (a); + nm_assert (a->ifindex == ifindex); + { + const NMPObject *o = NMP_OBJECT_UP_CAST (a); + guint j; + + nm_assert (NMP_OBJECT_GET_CLASS (o)->addr_family == addr_family); + for (j = i + 1; j < array->len; j++) { + const NMPObject *o2 = array->pdata[j]; + + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_GET_TYPE (o2)); + nm_assert (!nmp_object_id_equal (o, o2)); + } + } +#endif + + if (NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_SECONDARY)) { + /* temporary addresses are never added explicitly by NetworkManager but + * kernel adds them via mngtempaddr flag. + * + * We drop them from this list. */ + goto clear_and_next; + } + + if (!nm_utils_lifetime_get (a->timestamp, a->lifetime, a->preferred, + now, NULL)) + goto clear_and_next; + + if (idx) { + if (G_UNLIKELY (!*idx)) { + *idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + if (!g_hash_table_add (*idx, (gpointer) NMP_OBJECT_UP_CAST (a))) + nm_assert_not_reached (); + } + any_addrs = TRUE; + continue; + +clear_and_next: + nmp_object_unref (g_steal_pointer (&array->pdata[i])); } - return -1; + return any_addrs; } static gboolean @@ -3322,39 +3370,8 @@ nm_platform_ip4_address_sync (NMPlatform *self, _CHECK_SELF (self, klass, FALSE); - if (known_addresses) { - /* remove all addresses that are already expired. */ - for (i = 0; i < known_addresses->len; i++) { - const NMPObject *o; - - o = known_addresses->pdata[i]; - nm_assert (o); - - known_address = NMP_OBJECT_CAST_IP4_ADDRESS (known_addresses->pdata[i]); - - if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, NULL, NULL)) - goto delete_and_next; - - if (G_UNLIKELY (!known_addresses_idx)) { - known_addresses_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - if (!g_hash_table_insert (known_addresses_idx, (gpointer) o, (gpointer) o)) { - /* duplicate? Keep only the first instance. */ - goto delete_and_next; - } - - continue; -delete_and_next: - nmp_object_unref (o); - known_addresses->pdata[i] = NULL; - } - - if ( !known_addresses_idx - || g_hash_table_size (known_addresses_idx) == 0) - known_addresses = NULL; - } + if (!_addr_array_clean_expired (AF_INET, ifindex, known_addresses, now, &known_addresses_idx)) + known_addresses = NULL; plat_addresses = nm_platform_lookup_clone (self, nmp_lookup_init_object (&lookup, @@ -3451,8 +3468,9 @@ delete_and_next: known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o); - if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &lifetime, &preferred)) + lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, &preferred); + if (!lifetime) goto delete_and_next2; if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen, @@ -3474,9 +3492,15 @@ delete_and_next2: * nm_platform_ip6_address_sync: * @self: platform instance * @ifindex: Interface index - * @known_addresses: List of IPv6 addresses, as NMPObject. The list - * is not modified. - * @keep_link_local: Don't remove link-local address + * @known_addresses: List of addresses. The list will be modified and only + * addresses that were successfully added will be kept in the list. + * That means, expired addresses and addresses that could not be added + * will be dropped. + * Hence, the input argument @known_addresses is also an output argument + * telling which addresses were succesfully added. + * Addresses are removed by unrefing the instance via nmp_object_unref() + * and leaving a NULL tombstone. + * @full_sync: Also remove link-local and temporary addresses. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3487,60 +3511,114 @@ delete_and_next2: gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, - const GPtrArray *known_addresses, - gboolean keep_link_local) + GPtrArray *known_addresses, + gboolean full_sync) { gs_unref_ptrarray GPtrArray *plat_addresses = NULL; - NMPlatformIP6Address *address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - int i, position; + guint i_plat, i_know; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; NMPLookup lookup; guint32 ifa_flags; - gboolean remove = FALSE; - /* @plat_addresses and @known_addresses are in increasing priority order */ + if (!_addr_array_clean_expired (AF_INET6, ifindex, known_addresses, now, &known_addresses_idx)) + known_addresses = NULL; + + /* @plat_addresses is in decreasing priority order (highest priority addresses first), contrary to + * @known_addresses which is in increasing priority order (lowest priority addresses first). */ plat_addresses = nm_platform_lookup_clone (self, nmp_lookup_init_object (&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), NULL, NULL); - if (plat_addresses) { - /* First, remove unknown addresses from platform */ - for (i = 0; i < plat_addresses->len; i++) { - address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); - if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) - continue; - - position = array_ip6_address_position (known_addresses, address, now, FALSE); - if (position < 0) { - nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); - g_ptr_array_remove_index (plat_addresses, i); - i--; + if (plat_addresses) { + gboolean delete_remaining; + guint known_addresses_len; + + known_addresses_len = known_addresses ? known_addresses->len : 0; + + /* First, compare every address whether it is still a "known address", that is, whether + * to keep it or to delete it. + * + * If we don't find a matching valid address in @known_addresses, we will delete + * plat_addr. + * + * Certain addresses, like link-local or temporary addresses, are ignored by this function + * if not run with full_sync. + * + * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ + for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { + const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; + const NMPObject *know_obj; + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj); + + if ( NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_SECONDARY) + || IN6_IS_ADDR_LINKLOCAL (&plat_addr->address)) { + if (!full_sync) { + /* just mark as handled, without actually deleting the address. */ + goto clear_and_next; + } + } else if (known_addresses_idx) { + know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj); + if ( know_obj + && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) { + /* technically, plen is not part of the ID for IPv6 addresses and thus + * @plat_addr is essentially the same address as @know_addr (regrading + * its identity, not its other attributes). + * However, we cannot modify an existing addresses' plen without + * removing and readding it. Thus, only keep plat_addr, if the plen + * matches. + * + * keep this one, and continue */ + continue; + } } - } - /* Start from addresses with lower priority and remove them if they are - * in a wrong position. Removing an address also removes all addresses - * with higher priority. We ignore link-local addresses when determining - * positions. - */ - for (i = 0, position = 0; i < plat_addresses->len; i++) { - address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); + nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); +clear_and_next: + nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat])); + } - if (IN6_IS_ADDR_LINKLOCAL (&address->address)) + /* Next, we must preserve the priority of the routes. That is, source address + * selection will choose addresses in the order as they are reported by kernel. + * Note that the order in @plat_addresses of the remaining matches is highest + * priority first. + * We need to compare this to the order in @known_addresses (which has lowest + * priority first). + * + * If we find a first discrepancy, we need to delete all remaining addresses + * from that point on, because below we must re-add all the addresses in the + * right order to get their priority right. */ + i_plat = plat_addresses->len; + i_know = 0; + delete_remaining = FALSE; + while (i_plat > 0) { + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]); + + if (!plat_addr) continue; - if ( remove - || position != array_ip6_address_position (known_addresses, - address, - now, - TRUE)) { - nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); - remove = TRUE; + if (!delete_remaining) { + for (; i_know < known_addresses_len; i_know++) { + const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); + + if (!know_addr) + continue; + + if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) { + /* we have a match. Mark address as handled. */ + i_know++; + goto next_plat; + } + break; + } + delete_remaining = TRUE; } - position++; + nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); +next_plat: + ; } } @@ -3554,18 +3632,15 @@ nm_platform_ip6_address_sync (NMPlatform *self, /* Add missing addresses. New addresses are added by kernel with top * priority. */ - for (i = 0; i < known_addresses->len; i++) { - const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]); + for (i_know = 0; i_know < known_addresses->len; i_know++) { + const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); guint32 lifetime, preferred; - if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_SECONDARY)) { - /* Kernel manages these */ + if (!known_address) continue; - } - if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &lifetime, &preferred)) - continue; + lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, &preferred); if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, @@ -3593,7 +3668,7 @@ nm_platform_ip_address_flush (NMPlatform *self, if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) success &= nm_platform_ip4_address_sync (self, ifindex, NULL); if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) - success &= nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + success &= nm_platform_ip6_address_sync (self, ifindex, NULL, TRUE); return success; } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 1f4230c6c4..3cbf8c9990 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -719,6 +719,8 @@ typedef struct { gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); + void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); + gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, @@ -1033,6 +1035,8 @@ gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); +void nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type); + const NMPObject *nm_platform_link_get_obj (NMPlatform *self, int ifindex, gboolean visible_only); @@ -1256,8 +1260,8 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self, guint32 flags); gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresse); -gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GPtrArray *known_addresses, gboolean keep_link_local); +gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses); +gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync); gboolean nm_platform_ip_address_flush (NMPlatform *self, int addr_family, int ifindex); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index dd5d1a229c..4df64e608c 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -1768,6 +1768,54 @@ nmp_cache_lookup_link_full (const NMPCache *cache, /*****************************************************************************/ +static NMDedupMultiIdxMode +_obj_get_add_mode (const NMPObject *obj) +{ + /* new objects are usually appended to the list. Except for + * addresses, which are prepended during `ip address add`. + * + * Actually, for routes it is more complicated, because depending on + * `ip route append`, `ip route replace`, `ip route prepend`, the object + * will be added at the tail, at the front, or even replace an element + * in the list. However, that is handled separately by nmp_cache_update_netlink_route() + * and of no concern here. */ + if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS)) + return NM_DEDUP_MULTI_IDX_MODE_PREPEND; + return NM_DEDUP_MULTI_IDX_MODE_APPEND; +} + +static void +_idxcache_update_order_for_dump (NMPCache *cache, + const NMDedupMultiEntry *entry) +{ + const NMPClass *klass; + const guint8 *i_idx_type; + const NMDedupMultiEntry *entry2; + + nm_dedup_multi_entry_reorder (entry, NULL, TRUE); + + klass = NMP_OBJECT_GET_CLASS (entry->obj); + for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) { + NMPCacheIdType id_type = *i_idx_type; + + if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) + continue; + + entry2 = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + _idx_type_get (cache, id_type), + entry->obj); + if (!entry2) + continue; + + nm_assert (entry2 != entry); + nm_assert (entry2->obj == entry->obj); + + nm_dedup_multi_entry_reorder (entry2, NULL, TRUE); + } +} + static void _idxcache_update_other_cache_ids (NMPCache *cache, NMPCacheIdType cache_id_type, @@ -1827,7 +1875,7 @@ _idxcache_update_other_cache_ids (NMPCache *cache, obj_new, is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE - : NM_DEDUP_MULTI_IDX_MODE_APPEND, + : _obj_get_add_mode (obj_new), is_dump ? NULL : entry_order, @@ -1905,7 +1953,7 @@ _idxcache_update (NMPCache *cache, obj_new, is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE - : NM_DEDUP_MULTI_IDX_MODE_APPEND, + : _obj_get_add_mode (obj_new), NULL, entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, NULL, @@ -2162,6 +2210,8 @@ nmp_cache_update_netlink (NMPCache *cache, } if (nmp_object_equal (obj_old, obj_hand_over)) { + if (is_dump) + _idxcache_update_order_for_dump (cache, entry_old); nm_dedup_multi_entry_set_dirty (entry_old, FALSE); NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; @@ -2235,6 +2285,8 @@ nmp_cache_update_netlink_route (NMPCache *cache, } if (nmp_object_equal (entry_old->obj, obj_hand_over)) { + if (is_dump) + _idxcache_update_order_for_dump (cache, entry_old); nm_dedup_multi_entry_set_dirty (entry_old, FALSE); goto update_done; } @@ -2258,9 +2310,8 @@ update_done: * properly find @obj_replaced. */ resync_required = FALSE; entry_replace = NULL; - if (is_dump) { + if (is_dump) goto out; - } if (!entry_new) { if ( NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE) @@ -2275,6 +2326,8 @@ update_done: goto out; } + /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID + * index. For all other indexes their order becomes messed up. */ entry_cur = _lookup_entry_with_idx_type (cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj); diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 8414c1a57f..8c36e2e3d4 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -730,6 +730,16 @@ const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj); +static inline const NMPObject * +nm_platform_lookup_obj (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + return nm_dedup_multi_entry_get_obj (nm_platform_lookup_entry (platform, + cache_id_type, + obj)); +} + static inline const NMDedupMultiHeadEntry * nm_platform_lookup_obj_type (NMPlatform *platform, NMPObjectType obj_type) |