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
This commit is contained in:
Thomas Haller 2018-02-09 17:40:09 +01:00
commit fa41e5852c
10 changed files with 341 additions and 160 deletions

View file

@ -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;
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;
if (addr->addr_source >= NM_IP_CONFIG_SOURCE_USER)
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);

View file

@ -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,

View file

@ -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);
NM_SET_OUT (out_lifetime, t_lifetime);
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), TRUE);
g_return_val_if_fail (t_preferred <= t_lifetime, TRUE);
g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT);
return NM_PLATFORM_LIFETIME_PERMANENT;
}
return TRUE;
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_preferred, 0);
return 0;
}
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 *

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -1117,6 +1117,21 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex)
return (nm_platform_link_get_type (self, ifindex) & 0x20000);
}
/**
* 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
@ -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)
static gboolean
_addr_array_clean_expired (int addr_family, int ifindex, GPtrArray *array, guint32 now, GHashTable **idx)
{
guint len = addresses ? addresses->len : 0;
guint i, pos;
guint i;
gboolean any_addrs = FALSE;
nm_assert (!ignore_ll || !IN6_IS_ADDR_LINKLOCAL (&address->address));
nm_assert_addr_family (addr_family);
nm_assert (ifindex > 0);
nm_assert (now > 0);
for (i = 0, pos = 0; i < len; i++) {
NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]);
if (!array)
return FALSE;
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;
/* 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 (!ignore_ll || !IN6_IS_ADDR_LINKLOCAL (&candidate->address))
pos++;
#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]);
gboolean delete_remaining;
guint known_addresses_len;
if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address))
continue;
known_addresses_len = known_addresses ? known_addresses->len : 0;
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--;
/* 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;
}
}
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]));
}
/* 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]);
/* 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 (IN6_IS_ADDR_LINKLOCAL (&address->address))
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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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)