diff options
| author | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
| commit | 90e8691111889a7b5f3c812f5a41f15a8a058913 (patch) | |
| tree | f101a879eca27c34a9bfa5f3da52266b22539a36 /src/platform | |
| parent | bdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff) | |
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/nm-fake-platform.c | 1437 | ||||
| -rw-r--r-- | src/platform/nm-linux-platform.c | 2379 | ||||
| -rw-r--r-- | src/platform/nm-linux-platform.h | 6 | ||||
| -rw-r--r-- | src/platform/nm-platform-private.h | 42 | ||||
| -rw-r--r-- | src/platform/nm-platform.c | 2926 | ||||
| -rw-r--r-- | src/platform/nm-platform.h | 469 | ||||
| -rw-r--r-- | src/platform/nmp-netns.c | 16 | ||||
| -rw-r--r-- | src/platform/nmp-netns.h | 3 | ||||
| -rw-r--r-- | src/platform/nmp-object.c | 2461 | ||||
| -rw-r--r-- | src/platform/nmp-object.h | 559 | ||||
| -rw-r--r-- | src/platform/tests/monitor.c | 2 | ||||
| -rw-r--r-- | src/platform/tests/test-address.c | 87 | ||||
| -rw-r--r-- | src/platform/tests/test-cleanup.c | 32 | ||||
| -rw-r--r-- | src/platform/tests/test-common.c | 445 | ||||
| -rw-r--r-- | src/platform/tests/test-common.h | 174 | ||||
| -rw-r--r-- | src/platform/tests/test-general.c | 2 | ||||
| -rw-r--r-- | src/platform/tests/test-link.c | 31 | ||||
| -rw-r--r-- | src/platform/tests/test-nmp-object.c | 494 | ||||
| -rw-r--r-- | src/platform/tests/test-route.c | 541 | ||||
| -rw-r--r-- | src/platform/wifi/wifi-utils-nl80211.c | 38 | ||||
| -rw-r--r-- | src/platform/wifi/wifi-utils-wext.c | 5 |
21 files changed, 7625 insertions, 4524 deletions
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 38706f37..c199c5ed 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -32,6 +32,7 @@ #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "nmp-object.h" #include "nm-test-utils-core.h" @@ -39,20 +40,14 @@ /*****************************************************************************/ typedef struct { - NMPlatformLink link; - + const NMPObject *obj; char *udi; - NMPObject *lnk; struct in6_addr ip6_lladdr; } NMFakePlatformLink; typedef struct { GHashTable *options; GArray *links; - GArray *ip4_addresses; - GArray *ip6_addresses; - GArray *ip4_routes; - GArray *ip6_routes; } NMFakePlatformPrivate; struct _NMFakePlatform { @@ -96,7 +91,22 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) /*****************************************************************************/ -static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal); +static void link_changed (NMPlatform *platform, + NMFakePlatformLink *device, + NMPCacheOpsType cache_op, + const NMPObject *obj_old); + +static gboolean ipx_address_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer addr, + const guint8 *plen, + gconstpointer peer_addr); + +static gboolean ipx_route_delete (NMPlatform *platform, + int addr_family, + int ifindex, + const NMPObject *obj); static gboolean ip6_address_add (NMPlatform *platform, int ifindex, @@ -110,14 +120,6 @@ static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in /*****************************************************************************/ -static gboolean -_ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, guint8 plen) -{ - return ((peer1 ^ peer2) & nm_utils_ip4_prefix_to_netmask (plen)) == 0; -} - -/*****************************************************************************/ - #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ G_STMT_START { \ const char *const _pathid = (pathid); \ @@ -158,288 +160,338 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat return g_strdup (g_hash_table_lookup (priv->options, path)); } -static const char * -type_to_type_name (NMLinkType type) -{ - switch (type) { - case NM_LINK_TYPE_UNKNOWN: - return "unknown"; - case NM_LINK_TYPE_LOOPBACK: - return "loopback"; - case NM_LINK_TYPE_ETHERNET: - return "ethernet"; - case NM_LINK_TYPE_DUMMY: - return "dummy"; - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bond"; - case NM_LINK_TYPE_TEAM: - return "team"; - case NM_LINK_TYPE_VLAN: - return "vlan"; - case NM_LINK_TYPE_NONE: - default: - return NULL; - } -} - -static void -link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) -{ - gs_free char *ip6_lladdr = NULL; - - g_assert (!name || strlen (name) < sizeof(device->link.name)); - - memset (device, 0, sizeof (*device)); - - ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - - device->link.ifindex = name ? ifindex : 0; - device->link.type = type; - device->link.kind = type_to_type_name (type); - device->link.driver = type_to_type_name (type); - device->udi = g_strdup_printf ("fake:%d", ifindex); - device->link.initialized = TRUE; - device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); - if (name) - strcpy (device->link.name, name); - switch (device->link.type) { - case NM_LINK_TYPE_DUMMY: - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_NOARP); - break; - default: - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_NOARP); - break; - } -} - static NMFakePlatformLink * link_get (NMPlatform *platform, int ifindex) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMFakePlatformLink *device; + int idx; + + if (ifindex <= 0) + g_return_val_if_reached (NULL); - if (ifindex >= priv->links->len) + idx = ifindex - 1; + if (idx >= priv->links->len) goto not_found; - device = &g_array_index (priv->links, NMFakePlatformLink, ifindex); - if (!device->link.ifindex) + + device = &g_array_index (priv->links, NMFakePlatformLink, idx); + if (!device->obj) goto not_found; + g_assert (ifindex == NMP_OBJECT_CAST_LINK (device->obj)->ifindex); + g_assert (device->obj == nm_platform_link_get_obj (platform, ifindex, FALSE)); + return device; not_found: _LOGD ("link not found: %d", ifindex); return NULL; } -static GArray * -link_get_all (NMPlatform *platform) +static void +link_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + NMPObject *obj_tmp) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *links = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), priv->links->len); - int i; + gboolean connected; - for (i = 0; i < priv->links->len; i++) - if (g_array_index (priv->links, NMFakePlatformLink, i).link.ifindex) - g_array_append_val (links, g_array_index (priv->links, NMFakePlatformLink, i).link); + /* we must clear the driver, because platform cache want's to set it */ + g_assert (obj_tmp->link.driver == g_intern_string (obj_tmp->link.driver)); + obj_tmp->link.driver = NULL; - return links; -} - -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); + if (NM_IN_SET (obj_tmp->link.type, NM_LINK_TYPE_BRIDGE, + NM_LINK_TYPE_BOND)) { + connected = FALSE; + if (NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP)) { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject *slave_candidate = NULL; + + nmp_cache_iter_for_each (&iter, + nmp_cache_lookup (nm_platform_get_cache (platform), + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_LINK)), + &slave_candidate) { + if (nmp_cache_link_connected_for_slave (obj_tmp->link.ifindex, slave_candidate)) { + connected = TRUE; + break; + } + } + } + } else + connected = NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP); - return device ? &device->link : NULL; + obj_tmp->link.n_ifi_flags = NM_FLAGS_ASSIGN (obj_tmp->link.n_ifi_flags, IFF_LOWER_UP, connected); + obj_tmp->link.connected = connected; } -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, const char *ifname) +static NMFakePlatformLink * +link_add_pre (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - guint i; + NMFakePlatformLink *device; + int ifindex; + NMPObject *o; + NMPlatformLink *link; + gs_free char *ip6_lladdr = NULL; - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + g_assert (!name || strlen (name) < IFNAMSIZ); - if (!strcmp (device->link.name, ifname)) - return &device->link; - } - return NULL; -} + g_array_set_size (priv->links, priv->links->len + 1); + device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); + ifindex = priv->links->len; -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - guint i; + memset (device, 0, sizeof (*device)); - if ( length == 0 - || length > NM_UTILS_HWADDR_LEN_MAX - || !address) - g_return_val_if_reached (NULL); + o = nmp_object_new_link (ifindex); + link = NMP_OBJECT_CAST_LINK (o); - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - if ( device->link.addr.len == length - && memcmp (device->link.addr.data, address, length) == 0) { - return &device->link; - } + link->ifindex = name ? ifindex : 0; + link->type = type; + link->kind = g_intern_string (nm_link_type_to_string (type)); + link->initialized = TRUE; + if (name) + strcpy (link->name, name); + switch (link->type) { + case NM_LINK_TYPE_DUMMY: + link->n_ifi_flags = NM_FLAGS_SET (link->n_ifi_flags, IFF_NOARP); + break; + default: + link->n_ifi_flags = NM_FLAGS_UNSET (link->n_ifi_flags, IFF_NOARP); + break; } - return NULL; -} -static const NMPObject * -link_get_lnk (NMPlatform *platform, - int ifindex, - NMLinkType link_type, - const NMPlatformLink **out_link) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); + o->_link.netlink.is_in_netlink = TRUE; - if (!device) - return NULL; - - NM_SET_OUT (out_link, &device->link); - - if (!device->lnk) - return NULL; - - if (link_type == NM_LINK_TYPE_NONE) - return device->lnk; + if (address) { + g_assert (address_len > 0 && address_len <= sizeof (link->addr.data)); + memcpy (link->addr.data, address, address_len); + link->addr.len = address_len; + } else + g_assert (address_len == 0); - if ( link_type != device->link.type - || link_type != NMP_OBJECT_GET_CLASS (device->lnk)->lnk_link_type) - return NULL; + device->obj = o; + device->udi = g_strdup_printf ("fake:%d", ifindex); + device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); - return device->lnk; + return device; } static gboolean link_add (NMPlatform *platform, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMFakePlatformLink device; - NMFakePlatformLink *new_device; + NMFakePlatformLink *device; + NMFakePlatformLink *device_veth = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old_veth = NULL; + nm_auto_nmpobj const NMPObject *obj_new_veth = NULL; + NMPCacheOpsType cache_op; + NMPCacheOpsType cache_op_veth = NMP_CACHE_OPS_UNCHANGED; + + device = link_add_pre (platform, name, type, address, address_len); + + if (veth_peer) { + g_assert (type == NM_LINK_TYPE_VETH); + device_veth = link_add_pre (platform, veth_peer, type, NULL, 0); + } else + g_assert (type != NM_LINK_TYPE_VETH); + + link_add_prepare (platform, device, (NMPObject *) device->obj); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device->obj, + FALSE, + &obj_old, &obj_new); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + if (veth_peer) { + link_add_prepare (platform, device_veth, (NMPObject *) device_veth->obj); + cache_op_veth = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device_veth->obj, + FALSE, + &obj_old_veth, &obj_new_veth); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + } - link_init (&device, priv->links->len, type, name); + if (out_link) + *out_link = NMP_OBJECT_CAST_LINK (device->obj); - if (address) { - g_return_val_if_fail (address_len > 0 && address_len <= sizeof (device.link.addr.data), FALSE); - memcpy (device.link.addr.data, address, address_len); - device.link.addr.len = address_len; - } + link_changed (platform, device, cache_op, NULL); + if (veth_peer) + link_changed (platform, device_veth, cache_op_veth, NULL); + + return TRUE; +} + +static NMFakePlatformLink * +link_add_one (NMPlatform *platform, + const char *name, + NMLinkType link_type, + void (*prepare_fcn) (NMPlatform *platform, NMFakePlatformLink *device, gconstpointer user_data), + gconstpointer user_data, + const NMPlatformLink **out_link) +{ + NMFakePlatformLink *device; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCacheOpsType cache_op; + int ifindex; - g_array_append_val (priv->links, device); - new_device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); + device = link_add_pre (platform, name, NM_LINK_TYPE_VLAN, NULL, 0); - if (device.link.ifindex) { - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, (int) NM_PLATFORM_SIGNAL_ADDED); + ifindex = NMP_OBJECT_CAST_LINK (device->obj)->ifindex; - link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE); - } + if (prepare_fcn) + prepare_fcn (platform, device, user_data); - if (out_link) - *out_link = &new_device->link; - return TRUE; + link_add_prepare (platform, device, (NMPObject *) device->obj); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device->obj, + FALSE, + &obj_old, &obj_new); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + + link_changed (platform, device, cache_op, obj_old); + + device = link_get (platform, ifindex); + if (!device) + g_assert_not_reached (); + + NM_SET_OUT (out_link, NMP_OBJECT_CAST_LINK (device->obj)); + return device; } static gboolean link_delete (NMPlatform *platform, int ifindex) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMFakePlatformLink *device = link_get (platform, ifindex); - NMPlatformLink deleted_device; - int i; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_old2 = NULL; + NMPCacheOpsType cache_op; - if (!device || !device->link.ifindex) + if (!device) return FALSE; - memcpy (&deleted_device, &device->link, sizeof (deleted_device)); - memset (&device->link, 0, sizeof (device->link)); - g_clear_pointer (&device->lnk, nmp_object_unref); + obj_old = g_steal_pointer (&device->obj); g_clear_pointer (&device->udi, g_free); + cache_op = nmp_cache_remove (nm_platform_get_cache (platform), + obj_old, + FALSE, + FALSE, + &obj_old2); + g_assert (cache_op == NMP_CACHE_OPS_REMOVED); + g_assert (obj_old2); + g_assert (obj_old == obj_old2); + /* Remove addresses and routes which belong to the deleted interface */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + ipx_address_delete (platform, AF_INET, ifindex, NULL, NULL, NULL); + ipx_address_delete (platform, AF_INET6, ifindex, NULL, NULL, NULL); + ipx_route_delete (platform, AF_INET, ifindex, NULL); + ipx_route_delete (platform, AF_INET6, ifindex, NULL); + + nm_platform_cache_update_emit_signal (platform, + cache_op, + obj_old2, + NULL); + return TRUE; +} - if (address->ifindex == ifindex) - memset (address, 0, sizeof (*address)); - } - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); +static void +link_set_obj (NMPlatform *platform, + NMFakePlatformLink *device, + NMPObject *obj_tmp) +{ + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj NMPObject *obj_tmp_tmp = NULL; + NMPCacheOpsType cache_op; - if (address->ifindex == ifindex) - memset (address, 0, sizeof (*address)); - } - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + g_assert (device); + g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); - if (route->ifindex == ifindex) - memset (route, 0, sizeof (*route)); + if (!obj_tmp) { + obj_tmp_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp = obj_tmp_tmp; } - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (route->ifindex == ifindex) - memset (route, 0, sizeof (*route)); - } + g_assert (NMP_OBJECT_GET_TYPE (obj_tmp) == NMP_OBJECT_TYPE_LINK); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, (int) NM_PLATFORM_SIGNAL_REMOVED); + link_add_prepare (platform, device, obj_tmp); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + obj_tmp, + FALSE, + &obj_old, &obj_new); + g_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_UNCHANGED, + NMP_CACHE_OPS_UPDATED)); + g_assert (obj_old == device->obj); + g_assert (obj_new); - return TRUE; -} + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) -{ - return type_to_type_name (nm_platform_link_get_type (platform, ifindex)); + link_changed (platform, device, cache_op, obj_old); } static void -link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal) +link_set_flags (NMPlatform *platform, + NMFakePlatformLink *device, + guint n_ifi_flags) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (raise_signal) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, (int) NM_PLATFORM_SIGNAL_CHANGED); + g_assert (device); + g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); - if (device->link.ifindex && !IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { - if (device->link.connected) - ip6_address_add (platform, device->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); - else - ip6_address_delete (platform, device->link.ifindex, device->ip6_lladdr, 64); - } + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.n_ifi_flags = n_ifi_flags; + link_set_obj (platform, device, obj_tmp); +} - if (device->link.master) { - gboolean connected = FALSE; +static void +link_changed (NMPlatform *platform, + NMFakePlatformLink *device, + NMPCacheOpsType cache_op, + const NMPObject *obj_old) +{ + g_assert (device->obj); - NMFakePlatformLink *master = link_get (platform, device->link.master); + g_assert (!nmp_cache_link_connected_needs_toggle (nm_platform_get_cache (platform), + device->obj, NULL, NULL)); - g_return_if_fail (master && master != device); + nm_platform_cache_update_emit_signal (platform, + cache_op, + obj_old, + device->obj); - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *slave = &g_array_index (priv->links, NMFakePlatformLink, i); + if (!IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { + if (device->obj->link.connected) + ip6_address_add (platform, device->obj->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); + else + ip6_address_delete (platform, device->obj->link.ifindex, device->ip6_lladdr, 64); + } - if (slave && slave->link.master == master->link.ifindex && slave->link.connected) - connected = TRUE; - } + if (device->obj->link.master) { + NMFakePlatformLink *master; - if (master->link.connected != connected) { - master->link.connected = connected; - link_changed (platform, master, TRUE); - } + master = link_get (platform, device->obj->link.master); + link_set_obj (platform, master, NULL); } } @@ -447,7 +499,6 @@ static gboolean link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { NMFakePlatformLink *device = link_get (platform, ifindex); - gboolean up, connected; if (out_no_firmware) *out_no_firmware = FALSE; @@ -457,29 +508,9 @@ link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) return FALSE; } - up = TRUE; - connected = TRUE; - switch (device->link.type) { - case NM_LINK_TYPE_DUMMY: - case NM_LINK_TYPE_VLAN: - break; - case NM_LINK_TYPE_BRIDGE: - case NM_LINK_TYPE_BOND: - case NM_LINK_TYPE_TEAM: - connected = FALSE; - break; - default: - connected = FALSE; - g_error ("Unexpected device type: %d", device->link.type); - } - - if ( NM_FLAGS_HAS (device->link.n_ifi_flags, IFF_UP) != !!up - || device->link.connected != connected) { - device->link.n_ifi_flags = NM_FLAGS_ASSIGN (device->link.n_ifi_flags, IFF_UP, up); - device->link.connected = connected; - link_changed (platform, device, TRUE); - } - + link_set_flags (platform, + device, + NM_FLAGS_ASSIGN (device->obj->link.n_ifi_flags, IFF_UP, TRUE)); return TRUE; } @@ -493,13 +524,9 @@ link_set_down (NMPlatform *platform, int ifindex) return FALSE; } - if (NM_FLAGS_HAS (device->link.n_ifi_flags, IFF_UP) || device->link.connected) { - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_UP); - device->link.connected = FALSE; - - link_changed (platform, device, TRUE); - } - + link_set_flags (platform, + device, + NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_UP)); return TRUE; } @@ -513,10 +540,9 @@ link_set_arp (NMPlatform *platform, int ifindex) return FALSE; } - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_NOARP); - - link_changed (platform, device, TRUE); - + link_set_flags (platform, + device, + NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } @@ -530,10 +556,9 @@ link_set_noarp (NMPlatform *platform, int ifindex) return FALSE; } - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_NOARP); - - link_changed (platform, device, TRUE); - + link_set_flags (platform, + device, + NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } @@ -541,36 +566,40 @@ static NMPlatformError link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if ( !device - || len == 0 + if ( len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - if ( device->link.addr.len != len - || ( len > 0 - && memcmp (device->link.addr.data, addr, len) != 0)) { - memcpy (device->link.addr.data, addr, len); - device->link.addr.len = len; - link_changed (platform, link_get (platform, ifindex), TRUE); - } + if (!device) + return NM_PLATFORM_ERROR_EXISTS; + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.addr.len = len; + memset (obj_tmp->link.addr.data, 0, sizeof (obj_tmp->link.addr.data)); + memcpy (obj_tmp->link.addr.data, addr, len); + + link_set_obj (platform, device, obj_tmp); return NM_PLATFORM_ERROR_SUCCESS; } -static gboolean +static NMPlatformError link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { NMFakePlatformLink *device = link_get (platform, ifindex); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (device) { - device->link.mtu = mtu; - link_changed (platform, device, TRUE); - } else + if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); + return NM_PLATFORM_ERROR_EXISTS; + } - return !!device; + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.mtu = mtu; + link_set_obj (platform, device, obj_tmp); + return NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -614,7 +643,7 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_DUMMY: return FALSE; default: @@ -630,7 +659,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: @@ -646,7 +675,7 @@ link_supports_sriov (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: @@ -663,15 +692,14 @@ link_enslave (NMPlatform *platform, int master, int slave) g_return_val_if_fail (device, FALSE); g_return_val_if_fail (master_device, FALSE); - if (device->link.master != master) { - device->link.master = master; - - if (NM_IN_SET (master_device->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) { - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_UP); - device->link.connected = TRUE; - } + if (device->obj->link.master != master) { + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - link_changed (platform, device, TRUE); + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.master = master; + if (NM_IN_SET (master_device->obj->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) + obj_tmp->link.n_ifi_flags = NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_UP); + link_set_obj (platform, device, obj_tmp); } return TRUE; @@ -682,40 +710,56 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) { NMFakePlatformLink *master = link_get (platform, master_idx); NMFakePlatformLink *slave = link_get (platform, slave_idx); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; g_return_val_if_fail (master, FALSE); g_return_val_if_fail (slave, FALSE); - if (slave->link.master != master->link.ifindex) + if (slave->obj->link.master != master->obj->link.ifindex) return FALSE; - slave->link.master = 0; - - link_changed (platform, slave, TRUE); - link_changed (platform, master, TRUE); - + obj_tmp = nmp_object_clone (slave->obj, FALSE); + obj_tmp->link.master = 0; + link_set_obj (platform, slave, obj_tmp); return TRUE; } -static gboolean -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, const NMPlatformLink **out_link) +struct vlan_add_data { + guint32 vlan_flags; + int parent; + int vlan_id; +}; + +static void +_vlan_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) { - NMFakePlatformLink *device; + const struct vlan_add_data *d = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0, out_link)) - return FALSE; + obj_tmp = (NMPObject *) device->obj; - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + lnk->lnk_vlan.id = d->vlan_id; + lnk->lnk_vlan.flags = d->vlan_flags; - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); + obj_tmp->link.parent = d->parent; + obj_tmp->_link.netlink.lnk = lnk; +} - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - device->lnk->lnk_vlan.id = vlan_id; - device->link.parent = parent; +static gboolean +vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, const NMPlatformLink **out_link) +{ + const struct vlan_add_data d = { + .parent = parent, + .vlan_id = vlan_id, + .vlan_flags = vlan_flags, + }; - if (out_link) - *out_link = &device->link; + link_add_one (platform, name, NM_LINK_TYPE_VLAN, + _vlan_add_prepare, &d, out_link); return TRUE; } @@ -734,53 +778,76 @@ link_vlan_change (NMPlatform *platform, return FALSE; } +static void +_vxlan_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const NMPlatformLnkVxlan *props = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; + + obj_tmp = (NMPObject *) device->obj; + + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + lnk->lnk_vxlan = *props; + + obj_tmp->link.parent = props->parent_ifindex; + obj_tmp->_link.netlink.lnk = lnk; +} + static gboolean link_vxlan_add (NMPlatform *platform, const char *name, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link) { - NMFakePlatformLink *device; + link_add_one (platform, name, NM_LINK_TYPE_VXLAN, + _vxlan_add_prepare, props, out_link); + return TRUE; +} - if (!link_add (platform, name, NM_LINK_TYPE_VXLAN, NULL, 0, out_link)) - return FALSE; +struct infiniband_add_data { + int parent; + int p_key; +}; - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); +static void +_infiniband_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const struct infiniband_add_data *d = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); + obj_tmp = (NMPObject *) device->obj; - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); - device->lnk->lnk_vxlan = *props; - device->link.parent = props->parent_ifindex; + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); + lnk->lnk_infiniband.p_key = d->p_key; + lnk->lnk_infiniband.mode = "datagram"; - if (out_link) - *out_link = &device->link; - return TRUE; + obj_tmp->link.parent = d->parent; + obj_tmp->_link.netlink.lnk = lnk; } static gboolean infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) { - NMFakePlatformLink *device, *parent_device; + NMFakePlatformLink *parent_device; char name[IFNAMSIZ]; + const struct infiniband_add_data d = { + .parent = parent, + .p_key = p_key, + }; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); - nm_utils_new_infiniband_name (name, parent_device->link.name, p_key); - - if (!link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0, out_link)) - return FALSE; - - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); + nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - device->lnk->lnk_infiniband.p_key = p_key; - device->lnk->lnk_infiniband.mode = "datagram"; - device->link.parent = parent; + link_add_one (platform, name, NM_LINK_TYPE_INFINIBAND, + _infiniband_add_prepare, &d, out_link); return TRUE; } @@ -793,7 +860,7 @@ infiniband_partition_delete (NMPlatform *platform, int parent, int p_key) parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); - nm_utils_new_infiniband_name (name, parent_device->link.name, p_key); + nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); return link_delete (platform, nm_platform_link_get_ifindex (platform, name)); } @@ -804,7 +871,7 @@ wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabiliti g_return_val_if_fail (device, FALSE); - if (device->link.type != NM_LINK_TYPE_WIFI) + if (device->obj->link.type != NM_LINK_TYPE_WIFI) return FALSE; if (caps) { @@ -894,59 +961,25 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) /*****************************************************************************/ -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *addresses; - NMPlatformIP4Address *address; - int count = 0, i; - - /* Count addresses */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address && address->ifindex == ifindex) - count++; - } - - addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count); - - /* Fill addresses */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address && address->ifindex == ifindex) - g_array_append_val (addresses, *address); - } - - return addresses; -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) +static gboolean +ipx_address_add (NMPlatform *platform, int addr_family, const NMPlatformObject *address) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *addresses; - NMPlatformIP6Address *address; - int count = 0, i; - - /* Count addresses */ - for (i = 0; i < priv->ip6_addresses->len; i++) { - address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address && address->ifindex == ifindex) - count++; - } + nm_auto_nmpobj NMPObject *obj = NULL; + NMPCacheOpsType cache_op; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCache *cache = nm_platform_get_cache (platform); - addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count); + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - /* Fill addresses */ - count = 0; - for (i = 0; i < priv->ip6_addresses->len; i++) { - address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address && address->ifindex == ifindex) - g_array_append_val (addresses, *address); - } + obj = nmp_object_new (addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + address); - return addresses; + cache_op = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + return TRUE; } static gboolean @@ -960,9 +993,7 @@ ip4_address_add (NMPlatform *platform, guint32 flags, const char *label) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMPlatformIP4Address address; - int i; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -977,28 +1008,7 @@ ip4_address_add (NMPlatform *platform, if (label) g_strlcpy (address.label, label, sizeof (address.label)); - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - gboolean changed; - - if ( item->ifindex != address.ifindex - || item->address != address.address - || item->plen != address.plen - || !_ip4_address_equal_peer_net (item->peer_address, address.peer_address, address.plen)) - continue; - - changed = !nm_platform_ip4_address_cmp (item, &address); - - memcpy (item, &address, sizeof (address)); - if (changed) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; - } - - g_array_append_val (priv->ip4_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; + return ipx_address_add (platform, AF_INET, (const NMPlatformObject *) &address); } static gboolean @@ -1011,9 +1021,7 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint32 flags) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMPlatformIP6Address address; - int i; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -1026,380 +1034,321 @@ ip6_address_add (NMPlatform *platform, address.preferred = preferred; address.n_ifa_flags = flags; - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - gboolean changed; - - if ( item->ifindex != address.ifindex - || !IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) - continue; + return ipx_address_add (platform, AF_INET6, (const NMPlatformObject *) &address); +} - changed = !nm_platform_ip6_address_cmp (item, &address); +static gboolean +ipx_address_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer addr, + const guint8 *plen, + gconstpointer peer_addr) +{ + gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + NMDedupMultiIter iter; + const NMPObject *o = NULL; + guint i; + guint32 peer_addr_i; + + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + peer_addr_i = peer_addr ? *((guint32 *) peer_addr) : 0; + + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + 0), + &o) { + const NMPObject *obj_old = NULL; + + if (addr_family == AF_INET) { + const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (o); + + if ( address->ifindex != ifindex + || (addr && address->address != *((guint32 *) addr)) + || (plen && address->plen != *plen) + || ( peer_addr + && (((peer_addr_i ^ address->peer_address) & _nm_utils_ip4_prefix_to_netmask (address->plen)) != 0))) + continue; + } else { + const NMPlatformIP6Address *address = NMP_OBJECT_CAST_IP6_ADDRESS (o); + + g_assert (!peer_addr); + if ( address->ifindex != ifindex + || (addr && !IN6_ARE_ADDR_EQUAL (&address->address, addr)) + || (plen && address->plen != *plen)) + continue; + } - memcpy (item, &address, sizeof (address)); - if (changed) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + if (nmp_cache_remove (nm_platform_get_cache (platform), + o, + TRUE, + FALSE, + &obj_old) != NMP_CACHE_OPS_REMOVED) + g_assert_not_reached (); + g_assert (obj_old); + g_ptr_array_add (objs, (gpointer) obj_old); } - g_array_append_val (priv->ip6_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_ADDED); - + for (i = 0; i < objs->len; i++) { + nm_platform_cache_update_emit_signal (platform, + NMP_CACHE_OPS_REMOVED, + objs->pdata[i], + NULL); + } return TRUE; } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && address->address == addr - && ((peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (plen)) == 0) { - NMPlatformIP4Address deleted_address; - - memcpy (&deleted_address, address, sizeof (deleted_address)); - memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, (int) NM_PLATFORM_SIGNAL_REMOVED); - return TRUE; - } - } - - return TRUE; + return ipx_address_delete (platform, AF_INET, ifindex, &addr, &plen, &peer_address); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { - NMPlatformIP6Address deleted_address; - - memcpy (&deleted_address, address, sizeof (deleted_address)); - memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, (int) NM_PLATFORM_SIGNAL_REMOVED); - return TRUE; - } - } - - return TRUE; -} - -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && address->address == addr - && _ip4_address_equal_peer_net (address->peer_address, peer_address, plen)) - return address; - } - - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) - return address; - } - - return NULL; + return ipx_address_delete (platform, AF_INET6, ifindex, &addr, &plen, NULL); } /*****************************************************************************/ -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) +static gboolean +ipx_route_delete (NMPlatform *platform, + int addr_family, + int ifindex, + const NMPObject *obj) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *routes; - NMPlatformIP4Route *route; + gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + NMDedupMultiIter iter; + const NMPObject *o = NULL; guint i; - - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - /* Fill routes */ - for (i = 0; i < priv->ip4_routes->len; i++) { - route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - if (route && (!ifindex || route->ifindex == ifindex)) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) - g_array_append_val (routes, *route); - } else { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - g_array_append_val (routes, *route); - } - } + NMPObjectType obj_type; + + if (addr_family == AF_UNSPEC) { + g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + g_assert (ifindex == -1); + ifindex = obj->object.ifindex; + obj_type = NMP_OBJECT_GET_TYPE (obj); + } else { + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + g_assert (!obj); + g_assert (ifindex > 0); + obj_type = addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE; } - return routes; -} - -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *routes; - NMPlatformIP6Route *route; - guint i; - - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - /* Fill routes */ - for (i = 0; i < priv->ip6_routes->len; i++) { - route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (route && (!ifindex || route->ifindex == ifindex)) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) - g_array_append_val (routes, *route); + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + obj_type, + ifindex), + &o) { + const NMPObject *obj_old = NULL; + + if (obj) { + if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { + const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE (o); + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (obj); + + if ( route->network != r->network + || route->plen != r->plen + || route->metric != r->metric) + continue; } else { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - g_array_append_val (routes, *route); + const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE (o); + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (obj); + + if ( !IN6_ARE_ADDR_EQUAL (&route->network, &r->network) + || route->plen != r->plen + || route->metric != r->metric) + continue; } } - } - - return routes; -} -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - NMPlatformIP4Route deleted_route; - - if ( route->ifindex != ifindex - || route->network != network - || route->plen != plen - || route->metric != metric) - continue; - - memcpy (&deleted_route, route, sizeof (deleted_route)); - g_array_remove_index (priv->ip4_routes, i); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, (int) NM_PLATFORM_SIGNAL_REMOVED); + if (nmp_cache_remove (nm_platform_get_cache (platform), + o, + TRUE, + FALSE, + &obj_old) != NMP_CACHE_OPS_REMOVED) + g_assert_not_reached (); + g_assert (obj_old); + g_ptr_array_add (objs, (gpointer) obj_old); } + for (i = 0; i < objs->len; i++) { + nm_platform_cache_update_emit_signal (platform, + NMP_CACHE_OPS_REMOVED, + objs->pdata[i], + NULL); + } return TRUE; } static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +ip_route_delete (NMPlatform *platform, const NMPObject *obj) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - metric = nm_utils_ip6_route_metric_normalize (metric); - - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - NMPlatformIP6Route deleted_route; - - if ( route->ifindex != ifindex - || !IN6_ARE_ADDR_EQUAL (&route->network, &network) - || route->plen != plen - || route->metric != metric) - continue; - - memcpy (&deleted_route, route, sizeof (deleted_route)); - g_array_remove_index (priv->ip6_routes, i); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, (int) NM_PLATFORM_SIGNAL_REMOVED); - } + g_assert (NM_IS_FAKE_PLATFORM (platform)); + g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - return TRUE; + return ipx_route_delete (platform, AF_UNSPEC, -1, obj); } -static gboolean -ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP4Route rt = *route; - guint i; - - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - rt.network = nm_utils_ip4_address_clear_host_address (rt.network, rt.plen); - rt.scope_inv = nm_platform_route_scope_inv (rt.gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK); +static NMPlatformError +ip_route_add (NMPlatform *platform, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route) +{ + NMDedupMultiIter iter; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPCacheOpsType cache_op; + const NMPObject *o = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + NMPCache *cache = nm_platform_get_cache (platform); + gboolean has_gateway = FALSE; + NMPlatformIPRoute *r = NULL; + NMPlatformIP4Route *r4 = NULL; + NMPlatformIP6Route *r6 = NULL; + gboolean has_same_weak_id; + gboolean only_dirty; + guint16 nlmsgflags; + + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + flags = NM_FLAGS_UNSET (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); + + /* currently, only replace is implemented. */ + g_assert (flags == NMP_NLM_FLAG_REPLACE); + + obj = nmp_object_new (addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + (const NMPlatformObject *) route); + r = NMP_OBJECT_CAST_IP_ROUTE (obj); + nm_platform_ip_route_normalize (addr_family, r); + + switch (addr_family) { + case AF_INET: + r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); + if (r4->gateway) + has_gateway = TRUE; + break; + case AF_INET6: + r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); + if (!IN6_IS_ADDR_UNSPECIFIED (&r6->gateway)) + has_gateway = TRUE; + break; + default: + nm_assert_not_reached (); + } - if (rt.gateway) { - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, - NMPlatformIP4Route, i); - guint32 gate = ntohl (item->network) >> (32 - item->plen); - guint32 host = ntohl (rt.gateway) >> (32 - item->plen); + if (has_gateway) { + gboolean has_route_to_gw = FALSE; + + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_GET_TYPE (obj), + 0), + &o) { + if (addr_family == AF_INET) { + const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE (o); + guint32 n = nm_utils_ip4_address_clear_host_address (item->network, item->plen); + guint32 g = nm_utils_ip4_address_clear_host_address (r4->gateway, item->plen); + + if ( r->ifindex == item->ifindex + && n == g) { + has_route_to_gw = TRUE; + break; + } + } else { + const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE (o); - if (rt.ifindex == item->ifindex && gate == host) - break; + if ( r->ifindex == item->ifindex + && nm_utils_ip6_address_same_prefix (&r6->gateway, &item->network, item->plen)) { + has_route_to_gw = TRUE; + break; + } + } } - if (i == priv->ip4_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - rt.ifindex, nm_utils_inet4_ntop (rt.network, NULL), rt.plen, rt.metric); - return FALSE; + if (!has_route_to_gw) { + if (addr_family == AF_INET) { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", + r->ifindex, nm_utils_inet4_ntop (r4->network, NULL), r->plen, r->metric); + } else { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", + r->ifindex, nm_utils_inet6_ntop (&r6->network, NULL), r->plen, r->metric); + } + return NM_PLATFORM_ERROR_UNSPECIFIED; } } - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (item->network != rt.network) - continue; - if (item->plen != rt.plen) - continue; - if (item->metric != rt.metric) - continue; - - if (item->ifindex != rt.ifindex) { - ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); - i--; - continue; + has_same_weak_id = FALSE; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_all (platform, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + obj), + &o) { + if (addr_family == AF_INET) { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (o), r4, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + continue; + } else { + if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (o), r6, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + continue; } - - memcpy (item, &rt, sizeof (rt)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + has_same_weak_id = TRUE; } - g_array_append_val (priv->ip4_routes, rt); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; -} - -static gboolean -ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP6Route rt = *route; - guint i; - - rt.metric = nm_utils_ip6_route_metric_normalize (rt.metric); - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - nm_utils_ip6_address_clear_host_address (&rt.network, &rt.network, rt.plen); - - if (!IN6_IS_ADDR_UNSPECIFIED (&rt.gateway)) { - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, - NMPlatformIP6Route, i); - guint8 gate_bits = rt.gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - - if ( rt.ifindex == item->ifindex - && memcmp (&rt.gateway, &item->network, item->plen / 8) == 0 - && gate_bits == host_bits) - break; - } - if (i == priv->ip6_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - rt.ifindex, nm_utils_inet6_ntop (&rt.network, NULL), rt.plen, rt.metric); - return FALSE; + nlmsgflags = 0; + if (has_same_weak_id) { + switch (flags) { + case NMP_NLM_FLAG_REPLACE: + nlmsgflags = NLM_F_REPLACE; + break; + default: + g_assert_not_reached (); + break; } } - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (!IN6_ARE_ADDR_EQUAL (&item->network, &rt.network)) - continue; - if (item->plen != rt.plen) - continue; - if (item->metric != rt.metric) - continue; - - if (item->ifindex != rt.ifindex) { - ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); - i--; - continue; + /* we manipulate the cache the same was as NMLinuxPlatform does it. */ + cache_op = nmp_cache_update_netlink_route (cache, + obj, + FALSE, + nlmsgflags, + &obj_old, + &obj_new, + &obj_replace, + NULL); + only_dirty = FALSE; + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; } - - memcpy (item, &rt, sizeof (rt)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } - g_array_append_val (priv->ip6_routes, rt); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; -} - -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (route->ifindex == ifindex - && route->network == network - && route->plen == plen - && route->metric == metric) - return route; - } - - return NULL; -} - -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - metric = nm_utils_ip6_route_metric_normalize (metric); - - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (route->ifindex == ifindex - && IN6_ARE_ADDR_EQUAL (&route->network, &network) - && route->plen == plen - && route->metric == metric) - return route; + if (obj_replace) { + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } } - return NULL; + return NM_PLATFORM_ERROR_SUCCESS; } /*****************************************************************************/ @@ -1409,12 +1358,8 @@ nm_fake_platform_init (NMFakePlatform *fake_platform) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (fake_platform); - priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); priv->links = g_array_new (TRUE, TRUE, sizeof (NMFakePlatformLink)); - priv->ip4_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Address)); - priv->ip6_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Address)); - priv->ip4_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); - priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); } void @@ -1428,16 +1373,13 @@ nm_fake_platform_setup (void) nm_platform_setup (platform); - /* skip zero element */ - link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0, NULL); - /* add loopback interface */ - link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0, NULL); + link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, NULL, 0, NULL); /* add some ethernets */ - link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); - link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); - link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); + link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, NULL, 0, NULL); + link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, NULL, 0, NULL); + link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, NULL, 0, NULL); } static void @@ -1451,13 +1393,9 @@ finalize (GObject *object) NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_free (device->udi); - g_clear_pointer (&device->lnk, nmp_object_unref); + g_clear_pointer (&device->obj, nmp_object_unref); } g_array_unref (priv->links); - g_array_unref (priv->ip4_addresses); - g_array_unref (priv->ip6_addresses); - g_array_unref (priv->ip4_routes); - g_array_unref (priv->ip6_routes); G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object); } @@ -1473,15 +1411,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_get_udi = link_get_udi; @@ -1525,21 +1456,11 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; - platform_class->ip4_route_add = ip4_route_add; - platform_class->ip6_route_add = ip6_route_add; - platform_class->ip4_route_delete = ip4_route_delete; - platform_class->ip6_route_delete = ip6_route_delete; + platform_class->ip_route_add = ip_route_add; + platform_class->ip_route_delete = ip_route_delete; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6b84c185..c4c93ed3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -48,6 +48,7 @@ #include "nmp-object.h" #include "nmp-netns.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" #include "nm-utils/unaligned.h" @@ -103,6 +104,14 @@ #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) #endif + +static const gboolean RTA_PREF_SUPPORTED_AT_COMPILETIME = (RTA_MAX >= 20 /* RTA_PREF */); + +G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); +#define RTA_PREF 20 +#undef RTA_MAX +#define RTA_MAX (MAX ((__RTA_MAX - 1), RTA_PREF)) + #ifndef MACVLAN_FLAG_NOPROMISC #define MACVLAN_FLAG_NOPROMISC 1 #endif @@ -197,6 +206,21 @@ typedef enum { INFINIBAND_ACTION_DELETE_CHILD, } InfinibandAction; +typedef enum { + CHANGE_LINK_TYPE_UNSPEC, + CHANGE_LINK_TYPE_SET_MTU, + CHANGE_LINK_TYPE_SET_ADDRESS, +} ChangeLinkType; + +typedef struct { + union { + struct { + gconstpointer address; + gsize length; + } set_address; + }; +} ChangeLinkData; + enum { DELAYED_ACTION_IDX_REFRESH_ALL_LINKS, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES, @@ -255,13 +279,25 @@ static void delayed_action_schedule (NMPlatform *platform, DelayedActionType act static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name); static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type); -static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); -static void cache_prune_candidates_prune (NMPlatform *platform); +static void cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); +static void cache_prune_all (NMPlatform *platform); static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); -static void ASSERT_NETNS_CURRENT (NMPlatform *platform); /*****************************************************************************/ +static NMPlatformError +wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result) +{ + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return NM_PLATFORM_ERROR_SUCCESS; + if (seq_result < 0) + return (NMPlatformError) seq_result; + return NM_PLATFORM_ERROR_NETLINK; +} + static const char * wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size) { @@ -287,35 +323,117 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, g return buf0; } -/****************************************************************** +/***************************************************************************** * Support IFLA_INET6_ADDR_GEN_MODE - ******************************************************************/ + *****************************************************************************/ static int _support_user_ipv6ll = 0; #define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) +static void +_support_user_ipv6ll_detect (struct nlattr **tb) +{ + gboolean supported; + + nm_assert (_support_user_ipv6ll_still_undecided ()); + + /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ + supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + _support_user_ipv6ll = supported ? 1 : -1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", + supported ? "detected" : "not detected"); +} + static gboolean _support_user_ipv6ll_get (void) { if (_support_user_ipv6ll_still_undecided ()) { - _support_user_ipv6ll = -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume no support"); - return FALSE; + _support_user_ipv6ll = 1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume support"); } - return _support_user_ipv6ll > 0; + return _support_user_ipv6ll >= 0; +} + +/***************************************************************************** + * extended IFA_FLAGS support + *****************************************************************************/ +static int _support_kernel_extended_ifa_flags = 0; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) + +static void +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr; + gboolean support; + + nm_assert (_support_kernel_extended_ifa_flags_still_undecided ()); + nm_assert (msg); + + msg_hdr = nlmsg_hdr (msg); + + nm_assert (msg_hdr && msg_hdr->nlmsg_type == RTM_NEWADDR); + + /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, + * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ + if (NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6)) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, + * dated 30 March, 2014. + * + * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy + * way to detect kernel support. */ + support = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); + _support_kernel_extended_ifa_flags = support ? 1 : -1; + _LOG2D ("kernel-support: extended-ifa-flags: %s", support ? "detected" : "not detected"); } +static gboolean +_support_kernel_extended_ifa_flags_get (void) +{ + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); + _support_kernel_extended_ifa_flags = 1; + } + return _support_kernel_extended_ifa_flags >= 0; +} + +/***************************************************************************** + * Support RTA_PREF + *****************************************************************************/ + +static int _support_rta_pref = 0; +#define _support_rta_pref_still_undecided() (G_UNLIKELY (_support_rta_pref == 0)) + static void -_support_user_ipv6ll_detect (struct nlattr **tb) +_support_rta_pref_detect (struct nlattr **tb) { - if (_support_user_ipv6ll_still_undecided ()) { - gboolean supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + gboolean supported; + + nm_assert (_support_rta_pref_still_undecided ()); - _support_user_ipv6ll = supported ? 1 : -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", - supported ? "detected" : "not detected"); + /* RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ + supported = !!tb[RTA_PREF]; + _support_rta_pref = supported ? 1 : -1; + _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", + supported ? "detected" : "not detected"); +} + +static gboolean +_support_rta_pref_get (void) +{ + if (_support_rta_pref_still_undecided ()) { + /* if we couldn't detect support, we fallback on compile-time check, whether + * RTA_PREF is present in the kernel headers. */ + _support_rta_pref = RTA_PREF_SUPPORTED_AT_COMPILETIME ? 1 : -1; + _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", + RTA_PREF_SUPPORTED_AT_COMPILETIME ? "assume support" : "assume no support"); } + return _support_rta_pref >= 0; } /****************************************************************** @@ -377,6 +495,7 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_WWAN_NET, "wwan", NULL, "wwan" }, { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, + { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, @@ -388,13 +507,13 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", NULL }, + { NM_LINK_TYPE_PPP, "ppp", NULL, "ppp" }, { NM_LINK_TYPE_SIT, "sit", "sit", NULL }, { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, { NM_LINK_TYPE_VXLAN, "vxlan", "vxlan", "vxlan" }, - { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, { NM_LINK_TYPE_BRIDGE, "bridge", "bridge", "bridge" }, { NM_LINK_TYPE_BOND, "bond", "bond", "bond" }, @@ -628,7 +747,7 @@ _linktype_get_type (NMPlatform *platform, { guint i; - ASSERT_NETNS_CURRENT (platform); + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (platform); nm_assert (ifname); if (completed_from_cache) { @@ -693,6 +812,8 @@ _linktype_get_type (NMPlatform *platform, return NM_LINK_TYPE_SIT; else if (arptype == ARPHRD_TUNNEL6) return NM_LINK_TYPE_IP6TNL; + else if (arptype == ARPHRD_PPP) + return NM_LINK_TYPE_PPP; { NMPUtilsEthtoolDriverInfo driver_info; @@ -762,6 +883,10 @@ _linktype_get_type (NMPlatform *platform, * aside from the DEVTYPE. */ if (!g_strcmp0 (devtype, "gadget")) return NM_LINK_TYPE_ETHERNET; + + /* Distributed Switch Architecture switch chips */ + if (!g_strcmp0 (devtype, "dsa")) + return NM_LINK_TYPE_ETHERNET; } } @@ -772,32 +897,149 @@ _linktype_get_type (NMPlatform *platform, * libnl unility functions and wrappers ******************************************************************/ -#define nm_auto_nlmsg __attribute__((cleanup(_nm_auto_nl_msg_cleanup))) +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +/* copied from iproute2's addattr_l(). */ +static gboolean +_nl_addattr_l (struct nlmsghdr *n, + int maxlen, + int type, + const void *data, + int alen) +{ + int len = RTA_LENGTH (alen); + struct rtattr *rta; + + if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) + return FALSE; + + rta = NLMSG_TAIL (n); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA (rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); + return TRUE; +} + static void _nm_auto_nl_msg_cleanup (void *ptr) { nlmsg_free (*((struct nl_msg **) ptr)); } +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) static const char * -_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +_nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) { - const char *str_type = NULL; + const char *b; + const char *s; + guint flags, flags_before; + const char *prefix; - switch (type) { - case RTM_NEWLINK: str_type = "NEWLINK"; break; - case RTM_DELLINK: str_type = "DELLINK"; break; - case RTM_NEWADDR: str_type = "NEWADDR"; break; - case RTM_DELADDR: str_type = "DELADDR"; break; - case RTM_NEWROUTE: str_type = "NEWROUTE"; break; - case RTM_DELROUTE: str_type = "DELROUTE"; break; + nm_utils_to_string_buffer_init (&buf, &len); + b = buf; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: s = "NEWLINK"; break; + case RTM_DELLINK: s = "DELLINK"; break; + case RTM_NEWADDR: s = "NEWADDR"; break; + case RTM_DELADDR: s = "DELADDR"; break; + case RTM_NEWROUTE: s = "NEWROUTE"; break; + case RTM_DELROUTE: s = "DELROUTE"; break; + default: s = NULL; break; } - if (str_type) - g_strlcpy (buf, str_type, len); + + if (s) + nm_utils_strbuf_append (&buf, &len, "RTM_%s", s); else - g_snprintf (buf, len, "(%d)", type); - return buf; + nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); + + flags = hdr->nlmsg_flags; + + if (!flags) { + nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); + goto flags_done; + } + +#define _F(f, n) \ + G_STMT_START { \ + if (NM_FLAGS_ALL (flags, f)) { \ + flags &= ~(f); \ + nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ + if (!flags) \ + goto flags_done; \ + prefix = ","; \ + } \ + } G_STMT_END + + prefix = ", flags "; + flags_before = flags; + _F (NLM_F_REQUEST, "request"); + _F (NLM_F_MULTI, "multi"); + _F (NLM_F_ACK, "ack"); + _F (NLM_F_ECHO, "echo"); + _F (NLM_F_DUMP_INTR, "dump_intr"); + _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); + + if (flags_before != flags) + prefix = ";"; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + _F (NLM_F_REPLACE, "replace"); + _F (NLM_F_EXCL, "excl"); + _F (NLM_F_CREATE, "create"); + _F (NLM_F_APPEND, "append"); + break; + case RTM_GETLINK: + case RTM_GETADDR: + case RTM_GETROUTE: + _F (NLM_F_DUMP, "dump"); + _F (NLM_F_ROOT, "root"); + _F (NLM_F_MATCH, "match"); + _F (NLM_F_ATOMIC, "atomic"); + break; + } + +#undef _F + + if (flags_before != flags) + prefix = ";"; + nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); + +flags_done: + + nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); + + return b; +} + +static int +_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); +} +#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) + +static int +_nl_nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + return nlmsg_parse (nlh, hdrlen, tb, maxtype, (struct nla_policy *) policy); } +#define nlmsg_parse(...) _nl_nlmsg_parse(__VA_ARGS__) + +static int +_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); +} +#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) /****************************************************************** * NMPObject/netlink functions @@ -826,7 +1068,7 @@ _parse_af_inet6 (NMPlatform *platform, guint8 *out_addr_gen_mode_inv, gboolean *out_addr_gen_mode_valid) { - static struct nla_policy policy[IFLA_INET6_MAX+1] = { + static const struct nla_policy policy[IFLA_INET6_MAX+1] = { [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, [IFLA_INET6_CACHEINFO] = { .minlen = nm_offsetofend (struct ifla_cacheinfo, retrans_time) }, [IFLA_INET6_CONF] = { .minlen = 4 }, @@ -862,7 +1104,8 @@ _parse_af_inet6 (NMPlatform *platform, /* Hack to detect support addrgenmode of the kernel. We only parse * netlink messages that we receive from kernel, hence this check * is valid. */ - _support_user_ipv6ll_detect (tb); + if (_support_user_ipv6ll_still_undecided ()) + _support_user_ipv6ll_detect (tb); if (tb[IFLA_INET6_ADDR_GEN_MODE]) { i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); @@ -892,7 +1135,7 @@ errout: static NMPObject * _parse_lnk_gre (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_GRE_MAX + 1] = { + static const struct nla_policy policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_LINK] = { .type = NLA_U32 }, [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, @@ -952,7 +1195,7 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_infiniband (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, @@ -998,7 +1241,7 @@ _parse_lnk_infiniband (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_UNSPEC, .minlen = sizeof (struct in6_addr)}, @@ -1051,7 +1294,7 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_ipip (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, @@ -1089,7 +1332,7 @@ _parse_lnk_ipip (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_macvlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { + static const struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, }; @@ -1132,7 +1375,7 @@ _parse_lnk_macvlan (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_macsec (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[__IFLA_MACSEC_MAX] = { + static const struct nla_policy policy[__IFLA_MACSEC_MAX] = { [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, @@ -1182,7 +1425,7 @@ _parse_lnk_macsec (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_sit (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, @@ -1284,7 +1527,7 @@ _vlan_qos_mapping_from_nla (struct nlattr *nlattr, static NMPObject * _parse_lnk_vlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_VLAN_MAX+1] = { + static const struct nla_policy policy[IFLA_VLAN_MAX+1] = { [IFLA_VLAN_ID] = { .type = NLA_U16 }, [IFLA_VLAN_FLAGS] = { .minlen = nm_offsetofend (struct ifla_vlan_flags, flags) }, [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, @@ -1370,7 +1613,7 @@ struct nm_ifla_vxlan_port_range { static NMPObject * _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { + static const struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_ID] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, @@ -1460,7 +1703,7 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) static NMPObject * _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[IFLA_MAX+1] = { + static const struct nla_policy policy[IFLA_MAX+1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MTU] = { .type = NLA_U32 }, @@ -1488,7 +1731,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr [IFLA_NET_NS_PID] = { .type = NLA_U32 }, [IFLA_NET_NS_FD] = { .type = NLA_U32 }, }; - static struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { + static const struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING }, [IFLA_INFO_DATA] = { .type = NLA_NESTED }, [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, @@ -1514,6 +1757,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr return NULL; ifi = nlmsg_data(nlh); + if (ifi->ifi_family != AF_UNSPEC) + return NULL; + obj = nmp_object_new_link (ifi->ifi_index); if (id_only) @@ -1680,7 +1926,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr * Also, sometimes the info-data is missing for updates. In this case * we want to keep the previously received lnk_data. */ nmp_object_unref (lnk_data); - lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); + lnk_data = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk); } if (address_complete_from_cache) obj->link.addr = link_cached->link.addr; @@ -1711,7 +1957,7 @@ errout: static NMPObject * _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[IFA_MAX+1] = { + static const struct nla_policy policy[IFA_MAX+1] = { [IFA_LABEL] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFA_CACHEINFO] = { .minlen = nm_offsetofend (struct ifa_cacheinfo, tstamp) }, @@ -1733,7 +1979,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) goto errout; is_v4 = ifa->ifa_family == AF_INET; - err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, policy); + err = nlmsg_parse (nlh, sizeof(*ifa), tb, IFA_MAX, policy); if (err < 0) goto errout; @@ -1826,10 +2072,12 @@ errout: static NMPObject * _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[RTA_MAX+1] = { + static const struct nla_policy policy[RTA_MAX+1] = { + [RTA_TABLE] = { .type = NLA_U32 }, [RTA_IIF] = { .type = NLA_U32 }, [RTA_OIF] = { .type = NLA_U32 }, [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_PREF] = { .type = NLA_U8 }, [RTA_FLOW] = { .type = NLA_U32 }, [RTA_CACHEINFO] = { .minlen = nm_offsetofend (struct rta_cacheinfo, rta_tsage) }, [RTA_METRICS] = { .type = NLA_NESTED }, @@ -1849,7 +2097,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) } nh; guint32 mss; guint32 window = 0, cwnd = 0, initcwnd = 0, initrwnd = 0, mtu = 0, lock = 0; - guint32 table; if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) return NULL; @@ -1869,12 +2116,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (err < 0) goto errout; - table = tb[RTA_TABLE] - ? nla_get_u32 (tb[RTA_TABLE]) - : (guint32) rtm->rtm_table; - if (table != RT_TABLE_MAIN) - goto errout; - /*****************************************************************/ is_v4 = rtm->rtm_family == AF_INET; @@ -1928,7 +2169,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) || tb[RTA_GATEWAY] || tb[RTA_FLOW]) { int ifindex = 0; - NMIPAddr gateway = NMIPAddrInit; + NMIPAddr gateway = { }; if (tb[RTA_OIF]) ifindex = nla_get_u32 (tb[RTA_OIF]); @@ -1956,7 +2197,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) mss = 0; if (tb[RTA_METRICS]) { struct nlattr *mtb[RTAX_MAX + 1]; - static struct nla_policy rtax_policy[RTAX_MAX + 1] = { + static const struct nla_policy rtax_policy[RTAX_MAX + 1] = { [RTAX_LOCK] = { .type = NLA_U32 }, [RTAX_ADVMSS] = { .type = NLA_U32 }, [RTAX_WINDOW] = { .type = NLA_U32 }, @@ -1990,6 +2231,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip_route.table_coerced = nm_platform_route_table_coerce ( tb[RTA_TABLE] + ? nla_get_u32 (tb[RTA_TABLE]) + : (guint32) rtm->rtm_table); + obj->ip_route.ifindex = nh.ifindex; if (_check_addr_or_errout (tb, RTA_DST, addr_len)) @@ -2015,9 +2260,13 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) memcpy (&obj->ip6_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); } - if (!is_v4 && tb[RTA_SRC]) { - _check_addr_or_errout (tb, RTA_SRC, addr_len); - memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + if (is_v4) + obj->ip4_route.tos = rtm->rtm_tos; + else { + if (tb[RTA_SRC]) { + _check_addr_or_errout (tb, RTA_SRC, addr_len); + memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + } obj->ip6_route.src_plen = rtm->rtm_src_len; } @@ -2027,12 +2276,20 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj->ip_route.initcwnd = initcwnd; obj->ip_route.initrwnd = initrwnd; obj->ip_route.mtu = mtu; - obj->ip_route.tos = rtm->rtm_tos; - obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW); - obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND); + obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW); + obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND); obj->ip_route.lock_initcwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITCWND); obj->ip_route.lock_initrwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITRWND); - obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); + obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); + + if (!is_v4) { + /* Detect support for RTA_PREF by inspecting the netlink message. */ + if (_support_rta_pref_still_undecided ()) + _support_rta_pref_detect (tb); + + if (tb[RTA_PREF]) + obj->ip6_route.rt_pref = nla_get_u8 (tb[RTA_PREF]); + } if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) { /* we must not straight way reject cloned routes, because we might have cached @@ -2135,12 +2392,14 @@ nla_put_failure: static gboolean _nl_msg_new_link_set_linkinfo (struct nl_msg *msg, - NMLinkType link_type) + NMLinkType link_type, + const char *veth_peer) { struct nlattr *info; const char *kind; nm_assert (msg); + nm_assert (!!veth_peer == (link_type == NM_LINK_TYPE_VETH)); kind = nm_link_type_to_rtnl_type_string (link_type); if (!kind) @@ -2151,11 +2410,26 @@ _nl_msg_new_link_set_linkinfo (struct nl_msg *msg, NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind); + if (veth_peer) { + struct ifinfomsg ifi = { }; + struct nlattr *data, *info_peer; + + if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) + goto nla_put_failure; + if (!(info_peer = nla_nest_start (msg, 1 /*VETH_INFO_PEER*/))) + goto nla_put_failure; + if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + NLA_PUT_STRING (msg, IFLA_IFNAME, veth_peer); + nla_nest_end (msg, info_peer); + nla_nest_end (msg, data); + } + nla_nest_end (msg, info); return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -2363,7 +2637,7 @@ _nl_msg_new_address (int nlmsg_type, && *((in_addr_t *) address) != 0) { in_addr_t broadcast; - broadcast = *((in_addr_t *) address) | ~nm_utils_ip4_prefix_to_netmask (plen); + broadcast = *((in_addr_t *) address) | ~_nm_utils_ip4_prefix_to_netmask (plen); NLA_PUT (msg, IFA_BROADCAST, addr_len, &broadcast); } @@ -2394,87 +2668,109 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static guint32 +ip_route_get_lock_flag (const NMPlatformIPRoute *route) +{ + return (((guint32) route->lock_window) << RTAX_WINDOW) + | (((guint32) route->lock_cwnd) << RTAX_CWND) + | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) + | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) + | (((guint32) route->lock_mtu) << RTAX_MTU); +} + /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ static struct nl_msg * _nl_msg_new_route (int nlmsg_type, - int nlmsg_flags, - int family, - int ifindex, - NMIPConfigSource source, - unsigned char scope, - gconstpointer network, - guint8 plen, - gconstpointer gateway, - guint32 metric, - guint32 mss, - gconstpointer pref_src, - gconstpointer src, - guint8 src_plen, - guint8 tos, - guint32 window, - guint32 cwnd, - guint32 initcwnd, - guint32 initrwnd, - guint32 mtu, - guint32 lock) + guint16 nlmsgflags, + const NMPObject *obj) { struct nl_msg *msg; + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + gboolean is_v4 = klass->addr_family == AF_INET; + const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj)); + const guint32 table = nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE); struct rtmsg rtmsg = { - .rtm_family = family, - .rtm_tos = tos, - .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ - .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source), - .rtm_scope = scope, + .rtm_family = klass->addr_family, + .rtm_tos = is_v4 + ? obj->ip4_route.tos + : 0, + .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, + .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (obj->ip_route.rt_source), + .rtm_scope = is_v4 + ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv) + : RT_SCOPE_NOWHERE, .rtm_type = RTN_UNICAST, .rtm_flags = 0, - .rtm_dst_len = plen, - .rtm_src_len = src ? src_plen : 0, + .rtm_dst_len = obj->ip_route.plen, + .rtm_src_len = is_v4 + ? 0 + : NMP_OBJECT_CAST_IP6_ROUTE (obj)->src_plen, }; gsize addr_len; - nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); - nm_assert (network); - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); if (!msg) g_return_val_if_reached (NULL); if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; - addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); - NLA_PUT (msg, RTA_DST, addr_len, network); + NLA_PUT (msg, RTA_DST, addr_len, + is_v4 + ? (gconstpointer) &obj->ip4_route.network + : (gconstpointer) &obj->ip6_route.network); - if (src) - NLA_PUT (msg, RTA_SRC, addr_len, src); + if (!is_v4) { + if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->src)) + NLA_PUT (msg, RTA_SRC, addr_len, &obj->ip6_route.src); + } - NLA_PUT_U32 (msg, RTA_PRIORITY, metric); + NLA_PUT_U32 (msg, RTA_PRIORITY, obj->ip_route.metric); - if (pref_src) - NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src); + if (table > 0xFF) + NLA_PUT_U32 (msg, RTA_TABLE, table); + + if (is_v4) { + if (NMP_OBJECT_CAST_IP4_ROUTE (obj)->pref_src) + NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->pref_src)) + NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); + } - if (mss || window || cwnd || initcwnd || initrwnd || mtu || lock) { + if ( obj->ip_route.mss + || obj->ip_route.window + || obj->ip_route.cwnd + || obj->ip_route.initcwnd + || obj->ip_route.initrwnd + || obj->ip_route.mtu + || lock) { struct nlattr *metrics; metrics = nla_nest_start (msg, RTA_METRICS); if (!metrics) goto nla_put_failure; - if (mss) - NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); - if (window) - NLA_PUT_U32 (msg, RTAX_WINDOW, window); - if (cwnd) - NLA_PUT_U32 (msg, RTAX_CWND, cwnd); - if (initcwnd) - NLA_PUT_U32 (msg, RTAX_INITCWND, initcwnd); - if (initrwnd) - NLA_PUT_U32 (msg, RTAX_INITRWND, initrwnd); - if (mtu) - NLA_PUT_U32 (msg, RTAX_MTU, mtu); + if (obj->ip_route.mss) + NLA_PUT_U32 (msg, RTAX_ADVMSS, obj->ip_route.mss); + if (obj->ip_route.window) + NLA_PUT_U32 (msg, RTAX_WINDOW, obj->ip_route.window); + if (obj->ip_route.cwnd) + NLA_PUT_U32 (msg, RTAX_CWND, obj->ip_route.cwnd); + if (obj->ip_route.initcwnd) + NLA_PUT_U32 (msg, RTAX_INITCWND, obj->ip_route.initcwnd); + if (obj->ip_route.initrwnd) + NLA_PUT_U32 (msg, RTAX_INITRWND, obj->ip_route.initrwnd); + if (obj->ip_route.mtu) + NLA_PUT_U32 (msg, RTAX_MTU, obj->ip_route.mtu); if (lock) NLA_PUT_U32 (msg, RTAX_LOCK, lock); @@ -2482,10 +2778,17 @@ _nl_msg_new_route (int nlmsg_type, } /* We currently don't have need for multi-hop routes... */ - if ( gateway - && memcmp (gateway, &nm_ip_addr_zero, addr_len) != 0) - NLA_PUT (msg, RTA_GATEWAY, addr_len, gateway); - NLA_PUT_U32 (msg, RTA_OIF, ifindex); + if (is_v4) { + NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED (&obj->ip6_route.gateway)) + NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); + } + NLA_PUT_U32 (msg, RTA_OIF, obj->ip_route.ifindex); + + if ( !is_v4 + && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) + NLA_PUT_U8 (msg, RTA_PREF, obj->ip6_route.rt_pref); return msg; @@ -2494,56 +2797,27 @@ nla_put_failure: g_return_val_if_reached (NULL); } -/*****************************************************************************/ - -static int _support_kernel_extended_ifa_flags = -1; - -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) - -static void -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; - - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; - - msg_hdr = nlmsg_hdr (msg); - if (msg_hdr->nlmsg_type != RTM_NEWADDR) - return; - - /* the extended address flags are only set for AF_INET6 */ - if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) - return; - - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE (they were added together). - **/ - _support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); - _LOG2D ("kernel-support: extended-ifa-flags: %s", _support_kernel_extended_ifa_flags ? "detected" : "not detected"); -} - -static gboolean -_support_kernel_extended_ifa_flags_get (void) -{ - if (_support_kernel_extended_ifa_flags_still_undecided ()) { - _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); - _support_kernel_extended_ifa_flags = 1; - } - return _support_kernel_extended_ifa_flags; -} - /****************************************************************** * NMPlatform types and functions ******************************************************************/ +typedef enum { + DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, + DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, + DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, +} DelayedActionWaitForNlResponseType; + typedef struct { guint32 seq_number; WaitForNlResponseResult seq_result; + DelayedActionWaitForNlResponseType response_type; gint64 timeout_abs_ns; WaitForNlResponseResult *out_seq_result; - gint *out_refresh_all_in_progess; + union { + gint *out_refresh_all_in_progess; + NMPObject **out_route_get; + gpointer out_data; + } response; } DelayedActionWaitForNlResponseData; typedef struct { @@ -2553,11 +2827,12 @@ typedef struct { guint32 nlh_seq_last_handled; #endif guint32 nlh_seq_last_seen; - NMPCache *cache; GIOChannel *event_channel; guint event_id; - gboolean sysctl_get_warned; + bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + + bool sysctl_get_warned; GHashTable *sysctl_get_prev_values; NMUdevClient *udev_client; @@ -2578,8 +2853,6 @@ typedef struct { gint is_handling; } delayed_action; - GHashTable *prune_candidates; - GHashTable *wifi_data; } NMLinuxPlatformPrivate; @@ -2599,8 +2872,15 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) NMPlatform * nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) { + gboolean use_udev = FALSE; + + if ( nmp_netns_is_initial () + && access ("/sys", W_OK) == 0) + use_udev = TRUE; + return g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, + NM_PLATFORM_USE_UDEV, use_udev, NM_PLATFORM_NETNS_SUPPORT, netns_support, NULL); } @@ -2611,13 +2891,6 @@ nm_linux_platform_setup (void) nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); } -static void -ASSERT_NETNS_CURRENT (NMPlatform *platform) -{ - nm_assert (NM_IS_LINUX_PLATFORM (platform)); - nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ())); -} - /*****************************************************************************/ #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ @@ -2676,7 +2949,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat nm_auto_pop_netns NMPNetns *netns = NULL; int fd, tries; gssize nwrote; - gsize len; + gssize len; char *actual; gs_free char *actual_free = NULL; int errsv; @@ -2731,6 +3004,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat * about to write. */ len = strlen (value) + 1; + nm_assert (len > 0); if (len > 512) actual = actual_free = g_malloc (len + 1); else @@ -2752,16 +3026,27 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat break; } } - if (nwrote == -1 && errsv != EEXIST) { - _LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s", - path, value, errsv, strerror (errsv)); + if (nwrote == -1) { + NMLogLevel level = LOGL_ERR; + + if (errsv == EEXIST) { + level = LOGL_DEBUG; + } else if ( errsv == EINVAL + && nm_utils_sysctl_ip_conf_is_path (AF_INET6, path, NULL, "mtu")) { + /* setting the MTU can fail under regular conditions. Suppress + * logging a warning. */ + level = LOGL_DEBUG; + } + + _NMLOG (level, "sysctl: failed to set '%s' to '%s': (%d) %s", + path, value, errsv, strerror (errsv)); } else if (nwrote < len - 1) { _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts", path, value); } if (nwrote < len - 1) { - if (close (fd) != 0) { + if (nm_close (fd) != 0) { if (errsv != 0) errno = errsv; } else if (errsv != 0) @@ -2770,7 +3055,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat errno = EIO; return FALSE; } - if (close (fd) != 0) { + if (nm_close (fd) != 0) { /* errno is already properly set. */ return FALSE; } @@ -2804,7 +3089,7 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char * if (!priv->sysctl_get_prev_values) { _nm_logging_clear_platform_logging_cache = _nm_logging_clear_platform_logging_cache_impl; sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform); - priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->sysctl_get_prev_values = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); } else prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, pathid); @@ -2872,20 +3157,30 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat /*****************************************************************************/ -static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) +static NMPlatformKernelSupportFlags +check_kernel_support (NMPlatform *platform, + NMPlatformKernelSupportFlags request_flags) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + NMPlatformKernelSupportFlags response = 0; - return _support_kernel_extended_ifa_flags_get (); -} + nm_assert (NM_IS_LINUX_PLATFORM (platform)); -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + if (_support_kernel_extended_ifa_flags_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS; + } - return _support_user_ipv6ll_get (); + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { + if (_support_user_ipv6ll_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL; + } + + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)) { + if (_support_rta_pref_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF; + } + + return response; } static void @@ -2896,86 +3191,6 @@ process_events (NMPlatform *platform) /*****************************************************************************/ -#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ - ({ \ - NMPCacheId _cache_id; \ - \ - ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ - nmp_cache_id_init_object_type (&_cache_id, (obj_type), (visible_only)), \ - NULL)); \ - }) - -/*****************************************************************************/ - -static void -do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible) -{ - gboolean is_visible; - NMPObject obj_clone; - const NMPClass *klass; - - nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); - - nm_assert (obj || cache_op == NMP_CACHE_OPS_UNCHANGED); - nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - - ASSERT_NETNS_CURRENT (platform); - - switch (cache_op) { - case NMP_CACHE_OPS_ADDED: - if (!nmp_object_is_visible (obj)) - return; - break; - case NMP_CACHE_OPS_UPDATED: - is_visible = nmp_object_is_visible (obj); - if (!was_visible && is_visible) - cache_op = NMP_CACHE_OPS_ADDED; - else if (was_visible && !is_visible) { - /* This is a bit ugly. The object was visible and changed in a way that it became invisible. - * We raise a removed signal, but contrary to a real 'remove', @obj is already changed to be - * different from what it was when the user saw it the last time. - * - * The more correct solution would be to have cache_pre_hook() create a clone of the original - * value before it was changed to become invisible. - * - * But, don't bother. Probably nobody depends on the original values and only cares about the - * id properties (which are still correct). - */ - cache_op = NMP_CACHE_OPS_REMOVED; - } else if (!is_visible) - return; - break; - case NMP_CACHE_OPS_REMOVED: - if (!was_visible) - return; - break; - default: - g_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); - return; - } - - klass = NMP_OBJECT_GET_CLASS (obj); - - _LOGt ("emit signal %s %s: %s", - klass->signal_type, - nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - /* don't expose @obj directly, but clone the public fields. A signal handler might - * call back into NMPlatform which could invalidate (or modify) @obj. */ - memcpy (&obj_clone.object, &obj->object, klass->sizeof_public); - g_signal_emit (platform, - _nm_platform_signal_id_get (klass->signal_type_id), - 0, - (int) klass->obj_type, - obj_clone.object.ifindex, - &obj_clone.object, - (int) cache_op); -} - -/*****************************************************************************/ - _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObjectType, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_LINK, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS), @@ -3043,11 +3258,12 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_ns (); char b[255]; - nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT"%s%s)", + nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT", response-type %d%s%s)", data->seq_number, timeout < 0 ? "-" : "", (timeout < 0 ? -timeout : timeout) / NM_UTILS_NS_PER_SECOND, (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND, + (int) data->response_type, data->seq_result ? ", " : "", data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : ""); } else @@ -3109,9 +3325,22 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; if (data->out_seq_result) *data->out_seq_result = seq_result; - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; + switch (data->response_type) { + case DELAYED_ACTION_RESPONSE_TYPE_VOID: + break; + case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: + if (data->response.out_refresh_all_in_progess) { + nm_assert (*data->response.out_refresh_all_in_progess > 0); + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + } + break; + case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: + if (data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + data->response.out_route_get = NULL; + } + break; } g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); @@ -3146,13 +3375,15 @@ delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, static void delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + cache_op = nmp_cache_update_link_master_connected (nm_platform_get_cache (platform), master_ifindex, &obj_old, &obj_new); + if (cache_op == NMP_CACHE_OPS_UNCHANGED) + return; + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } static void @@ -3273,7 +3504,7 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) any = TRUE; priv->delayed_action.is_handling--; - cache_prune_candidates_prune (platform); + cache_prune_all (platform); return any; } @@ -3319,13 +3550,15 @@ static void delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { DelayedActionWaitForNlResponseData data = { .seq_number = seq_number, .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)), .out_seq_result = out_seq_result, - .out_refresh_all_in_progess = out_refresh_all_in_progess, + .response_type = response_type, + .response.out_data = response_out_data, }; delayed_action_schedule (platform, @@ -3336,146 +3569,111 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, /*****************************************************************************/ static void -cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, - nmp_cache_id_init_object_type (&cache_id, obj_type, FALSE), - priv->prune_candidates); - _LOGt ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name, - priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0); -} - -static void -cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj) -{ - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - if (!priv->prune_candidates) - priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - if (_LOGt_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj)); -} - -static void -cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj) +cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) { - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - if (priv->prune_candidates) { - if (_LOGt_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_remove (priv->prune_candidates, obj); + NMDedupMultiIter iter; + const NMPObject *obj; + NMPCacheOpsType cache_op; + NMPLookup lookup; + NMPCache *cache = nm_platform_get_cache (platform); + + nmp_lookup_init_obj_type (&lookup, + obj_type); + nm_dedup_multi_iter_init (&iter, + nmp_cache_lookup (cache, + &lookup)); + while (nm_dedup_multi_iter_next (&iter)) { + if (iter.current->dirty) { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + obj = iter.current->obj; + _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + cache_op = nmp_cache_remove (cache, obj, TRUE, TRUE, &obj_old); + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_old, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, NULL); + } } } static void -cache_prune_candidates_prune (NMPlatform *platform) +cache_prune_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GHashTable *prune_candidates; - GHashTableIter iter; - const NMPObject *obj; - gboolean was_visible; - NMPCacheOpsType cache_op; - - if (!priv->prune_candidates) - return; - - prune_candidates = priv->prune_candidates; - priv->prune_candidates = NULL; + DelayedActionType iflags, action_type; - g_hash_table_iter_init (&iter, prune_candidates); - while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) { - nm_auto_nmpobj NMPObject *obj_cache = NULL; + action_type = DELAYED_ACTION_TYPE_REFRESH_ALL; + FOR_EACH_DELAYED_ACTION (iflags, action_type) { + bool *p = &priv->pruning[delayed_action_refresh_all_to_idx (iflags)]; - _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + if (*p) { + *p = FALSE; + cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags)); + } } - - g_hash_table_unref (prune_candidates); } static void -cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) { - NMPlatform *platform = user_data; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPClass *klass; char str_buf[sizeof (_nm_utils_to_string_buffer)]; char str_buf2[sizeof (_nm_utils_to_string_buffer)]; + NMPCache *cache = nm_platform_get_cache (platform); - nm_assert (old || new); - nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); - nm_assert (ops_type != NMP_CACHE_OPS_ADDED || (old == NULL && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old))); - nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old)); - nm_assert (!old || !new || NMP_OBJECT_GET_CLASS (old) == NMP_OBJECT_GET_CLASS (new)); + ASSERT_nmp_cache_ops (cache, cache_op, obj_old, obj_new); + nm_assert (cache_op != NMP_CACHE_OPS_UNCHANGED); - klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new); - - nm_assert (klass == (new ? NMP_OBJECT_GET_CLASS (new) : NMP_OBJECT_GET_CLASS (old))); + klass = obj_old ? NMP_OBJECT_GET_CLASS (obj_old) : NMP_OBJECT_GET_CLASS (obj_new); _LOGt ("update-cache-%s: %s: %s%s%s", klass->obj_type_name, - (ops_type == NMP_CACHE_OPS_UPDATED + (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE" - : (ops_type == NMP_CACHE_OPS_REMOVED + : (cache_op == NMP_CACHE_OPS_REMOVED ? "REMOVE" - : (ops_type == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), - (ops_type != NMP_CACHE_OPS_ADDED - ? nmp_object_to_string (old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) - : nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), - (ops_type == NMP_CACHE_OPS_UPDATED) ? " -> " : "", - (ops_type == NMP_CACHE_OPS_UPDATED - ? nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) + : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), + (cache_op != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string (obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) + : nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), + (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (cache_op == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) : "")); switch (klass->obj_type) { case NMP_OBJECT_TYPE_LINK: { /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ - if ( old - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, old->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (old->link.master)); - if ( new - && (!old || old->link.master != new->link.master) - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, new->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.master)); + if ( obj_old + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_old->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_old->link.master)); + if ( obj_new + && (!obj_old || obj_old->link.master != obj_new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_new->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.master)); } { /* check whether we are about to change a master link that needs toggling connected state. */ - if ( new /* <-- nonsensical, make coverity happy */ - && nmp_cache_link_connected_needs_toggle (cache, new, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.ifindex)); + if ( obj_new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle (cache, obj_new, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.ifindex)); } { int ifindex = 0; /* if we remove a link (from netlink), we must refresh the addresses and routes */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && !new->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && !obj_new->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink != obj_old->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { delayed_action_schedule (platform, @@ -3494,40 +3692,40 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP * Currently, kernel misses to sent us a notification in this case * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && !new->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && !obj_new->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { - const NMPlatformLink *const *links; - - links = cache_lookup_all_objects (NMPlatformLink, platform, NMP_OBJECT_TYPE_LINK, FALSE); - if (links) { - for (; *links; links++) { - const NMPlatformLink *l = (*links); - - if (l->parent == ifindex) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); - } + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *l; + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, &lookup), + &l) { + if (l->parent == ifindex) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); } } } { /* if a link goes down, we must refresh routes */ - if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink - && ( ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_UP)) - || ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP)))) { + if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink + && ( ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_UP)) + || ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I * think kernel does send RTM_DELROUTE events for IPv6 routes, so * we might not need to refresh IPv6 routes. */ @@ -3537,17 +3735,17 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP NULL); } } - if ( NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) - && (new && new->_link.netlink.is_in_netlink) - && (!old || !old->_link.netlink.is_in_netlink)) + if ( NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) + && (obj_new && obj_new->_link.netlink.is_in_netlink) + && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { - if (!new->_link.netlink.lnk) { + if (!obj_new->_link.netlink.lnk) { /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that * kernel didn't send this notification, thus when we first learn about a link * that lacks an lnk_data we re-request it again. * * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - switch (new->link.type) { + switch (obj_new->link.type) { case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_IP6TNL: case NM_LINK_TYPE_INFINIBAND: @@ -3558,23 +3756,23 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP case NM_LINK_TYPE_VXLAN: delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); break; default: break; } } - if ( new->link.type == NM_LINK_TYPE_VETH - && new->link.parent == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_VETH + && obj_new->link.parent == 0) { /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). * Request it again. */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } - if ( new->link.type == NM_LINK_TYPE_ETHERNET - && new->link.addr.len == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET + && obj_new->link.addr.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -3584,7 +3782,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } } { @@ -3592,14 +3790,14 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP int ifindex1 = 0, ifindex2 = 0; gboolean changed_master, changed_connected; - changed_master = (new && new->_link.netlink.is_in_netlink && new->link.master > 0 ? new->link.master : 0) - != (old && old->_link.netlink.is_in_netlink && old->link.master > 0 ? old->link.master : 0); - changed_connected = (new && new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP) : 2) - != (old && old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) : 2); + changed_master = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 ? obj_new->link.master : 0) + != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 ? obj_old->link.master : 0); + changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP) : 2) + != (obj_old && obj_old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) : 2); if (changed_master || changed_connected) { - ifindex1 = (old && old->_link.netlink.is_in_netlink && old->link.master > 0) ? old->link.master : 0; - ifindex2 = (new && new->_link.netlink.is_in_netlink && new->link.master > 0) ? new->link.master : 0; + ifindex1 = (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) ? obj_old->link.master : 0; + ifindex2 = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) ? obj_new->link.master : 0; if (ifindex1 > 0) delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); @@ -3607,29 +3805,13 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2)); } } - { - if ( ( (ops_type == NMP_CACHE_OPS_REMOVED) - || ( (ops_type == NMP_CACHE_OPS_UPDATED) - && new - && !new->_link.netlink.is_in_netlink)) - && old - && old->_link.netlink.is_in_netlink - && old->link.master) { - /* sometimes we receive a wrong RTM_DELLINK message when unslaving - * a device. Refetch the link again to check whether the device - * is really gone. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=1285719#c2 */ - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (old->link.ifindex)); - } - } break; case NMP_OBJECT_TYPE_IP4_ADDRESS: case NMP_OBJECT_TYPE_IP6_ADDRESS: { /* Address deletion is sometimes accompanied by route deletion. We need to * check all routes belonging to the same interface. */ - if (ops_type == NMP_CACHE_OPS_REMOVED) { + if (cache_op == NMP_CACHE_OPS_REMOVED) { delayed_action_schedule (platform, (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES @@ -3643,69 +3825,113 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP } } -static void -cache_post (NMPlatform *platform, - struct nlmsghdr *msghdr, - NMPCacheOpsType cache_op, - NMPObject *obj, - NMPObject *obj_cache) +/*****************************************************************************/ + +static guint32 +_nlh_seq_next_get (NMLinuxPlatformPrivate *priv) +{ + /* generate a new sequence number, but skip zero. */ + return priv->nlh_seq_next++ ?: priv->nlh_seq_next++; +} + +/** + * _nl_send_nlmsghdr: + * @platform: + * @nlhdr: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror. + */ +static int +_nl_send_nlmsghdr (NMPlatform *platform, + struct nlmsghdr *nlhdr, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint32 seq; + int nle; - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!obj_cache || nmp_object_id_equal (obj, obj_cache)); - - if (msghdr->nlmsg_type == RTM_NEWROUTE) { - DelayedActionType action_type; - - action_type = NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; - if ( !delayed_action_refresh_all_in_progress (platform, action_type) - && nmp_cache_find_other_route_for_same_destination (priv->cache, obj)) { - /* via `iproute route change` the user can update an existing route which effectively - * means that a new object (with a different ID) comes into existance, replacing the - * old on. In other words, as the ID of the object changes, we really see a new - * object with the old one deleted. - * However, kernel decides not to send a RTM_DELROUTE event for that. - * - * To hack around that, check if the update leaves us with multiple routes for the - * same network/plen,metric part. In that case, we cannot do better then requesting - * all routes anew, which sucks. - * - * One mitigation to avoid a dump is only to request a new dump, if we are not in - * the middle of an ongoing dump (delayed_action_refresh_all_in_progress). */ - delayed_action_schedule (platform, action_type, NULL); + nm_assert (nlhdr); + + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; + + { + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK, + }; + struct iovec iov = { + .iov_base = nlhdr, + .iov_len = nlhdr->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int try_count; + + if (!nlhdr->nlmsg_pid) + nlhdr->nlmsg_pid = nl_socket_get_local_port (priv->nlh); + nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + + try_count = 0; +again: + nle = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0); + if (nle < 0) { + nle = errno; + if (nle == EINTR && try_count++ < 100) + goto again; + _LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", g_strerror (nle), nle); + return -nle; } } -} -/*****************************************************************************/ + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; +} +/** + * _nl_send_nlmsg: + * @platform: + * @nlmsg: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno). + */ static int -_nl_send_auto_with_seq (NMPlatform *platform, - struct nl_msg *nlmsg, - WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) +_nl_send_nlmsg (NMPlatform *platform, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nlmsghdr *nlhdr; guint32 seq; int nle; - /* complete the message with a sequence number (ensuring it's not zero). */ - seq = priv->nlh_seq_next++ ?: priv->nlh_seq_next++; - - nlmsg_hdr (nlmsg)->nlmsg_seq = seq; + nlhdr = nlmsg_hdr (nlmsg); + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; nle = nl_send_auto (priv->nlh, nlmsg); + if (nle < 0) { + _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nl_geterror (nle), nle); + return nle; + } - if (nle >= 0) { - nle = 0; - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_refresh_all_in_progess); - } else - _LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle); - - return nle; + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; } static void @@ -3713,17 +3939,23 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int nle; if (name && !name[0]) name = NULL; g_return_if_fail (ifindex > 0 || name); - _LOGD ("do-request-link: %d %s", ifindex, name ? name : ""); + _LOGD ("do-request-link: %d %s", ifindex, name ?: ""); if (ifindex > 0) { - cache_prune_candidates_record_one (platform, - (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); + const NMDedupMultiEntry *entry; + + entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); + if (entry) { + priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE; + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } } event_handler_read_netlink (platform, FALSE); @@ -3734,8 +3966,15 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha name, 0, 0); - if (nlmsg) - _nl_send_auto_with_seq (platform, nlmsg, NULL, NULL); + if (nlmsg) { + nle = _nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", + ifindex, name ?: "", + nl_geterror (nle), -nle); + return; + } + } } static void @@ -3755,7 +3994,9 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; FOR_EACH_DELAYED_ACTION (iflags, action_type) { - cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags)); + priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE; + nmp_cache_dirty_set_all (nm_platform_get_cache (platform), + delayed_action_refresh_to_object_type (iflags)); } FOR_EACH_DELAYED_ACTION (iflags, action_type) { @@ -3795,7 +4036,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (nle < 0) continue; - if (_nl_send_auto_with_seq (platform, nlmsg, NULL, out_refresh_all_in_progess) < 0) { + if (_nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { nm_assert (*out_refresh_all_in_progess > 0); *out_refresh_all_in_progess -= 1; } @@ -3825,13 +4066,12 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - if (data->seq_number == priv->nlh_seq_last_seen) { - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; - data->out_refresh_all_in_progess = NULL; - break; - } + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS + && data->response.out_refresh_all_in_progess + && data->seq_number == priv->nlh_seq_last_seen) { + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + break; } } } @@ -3879,18 +4119,19 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu static void event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMLinuxPlatformPrivate *priv; nm_auto_nmpobj NMPObject *obj = NULL; - nm_auto_nmpobj NMPObject *obj_cache = NULL; NMPCacheOpsType cache_op; struct nlmsghdr *msghdr; - char buf_nlmsg_type[16]; + char buf_nlmsghdr[400]; gboolean id_only = FALSE; - gboolean was_visible; + NMPCache *cache = nm_platform_get_cache (platform); + gboolean is_dump; msghdr = nlmsg_hdr (msg); - if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) + if ( _support_kernel_extended_ifa_flags_still_undecided () + && msghdr->nlmsg_type == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); if (!handle_events) @@ -3902,156 +4143,144 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event id_only = TRUE; } - obj = nmp_object_new_from_nl (platform, priv->cache, msg, id_only); + obj = nmp_object_new_from_nl (platform, cache, msg, id_only); if (!obj) { - _LOGT ("event-notification: %s, seq %u: ignore", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq); + _LOGT ("event-notification: %s: ignore", + _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); return; } - _LOGT ("event-notification: %s, seq %u: %s", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, - id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - switch (msghdr->nlmsg_type) { - - case RTM_NEWLINK: case RTM_NEWADDR: + case RTM_NEWLINK: case RTM_NEWROUTE: - case RTM_GETLINK: - cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - - cache_post (platform, msghdr, cache_op, obj, obj_cache); - - do_emit_signal (platform, obj_cache, cache_op, was_visible); - break; - - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + is_dump = delayed_action_refresh_all_in_progress (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); break; - default: - break; + is_dump = FALSE; } - cache_prune_candidates_drop (platform, obj_cache); -} - -/*****************************************************************************/ - -static const NMPObject * -cache_lookup_link (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if (!nmp_object_is_visible (obj_cache)) - return NULL; - - return obj_cache; -} - -const NMPlatformObject *const* -nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL); - g_return_val_if_fail (cache_id, NULL); + _LOGT ("event-notification: %s%s: %s", + _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), + is_dump ? ", in-dump" : "", + nmp_object_to_string (obj, + id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, + NULL, 0)); - return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - cache_id, out_len); -} + { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + + switch (msghdr->nlmsg_type) { + + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_GETLINK: + cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; -static GArray * -link_get_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; + case RTM_NEWROUTE: { + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + gboolean resync_required = FALSE; + gboolean only_dirty = FALSE; + + if (obj->ip_route.rt_cloned) { + /* a cloned route might be a response for RTM_GETROUTE. Check, whether it is. */ + nm_assert (!nmp_object_is_alive (obj)); + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + guint i; + + nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET + && data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + if (data->seq_number == nlmsg_hdr (msg)->nlmsg_seq) { + *data->response.out_route_get = nmp_object_clone (obj, FALSE); + data->response.out_route_get = NULL; + break; + } + } + } + } + } - return nmp_cache_lookup_multi_to_array (priv->cache, - NMP_OBJECT_TYPE_LINK, - nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, TRUE)); -} + cache_op = nmp_cache_update_netlink_route (cache, + obj, + is_dump, + msghdr->nlmsg_flags, + &obj_old, + &obj_new, + &obj_replace, + &resync_required); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + /* we found an object that is to be replaced by the RTM_NEWROUTE message. + * While we invoke the signal, the platform cache might change and invalidate + * the findings. Mitigate that (for the most part), by marking the entry as + * dirty and only delete @obj_replace if it is still dirty afterwards. + * + * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, + * the signal handlers do not cause to call the platform again, so the cache + * is not really changing. -- if they would, it would anyway be dangerous to overflow + * the stack and it's not ensured that the processing of netlink messages is + * reentrant (maybe it is). + */ + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; + } + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj; + if (obj_replace) { + /* the RTM_NEWROUTE message indicates that another route was replaced. + * Remove it now. */ + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_replace, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } + } - obj = cache_lookup_link (platform, ifindex); - return obj ? &obj->link : NULL; -} + if (resync_required) { + /* we'd like to avoid such resyncs as they are expensive and we should only rely on the + * netlink events. This needs investigation. */ + _LOGT ("schedule resync of routes after RTM_NEWROUTE"); + delayed_action_schedule (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)), + NULL); + } + break; + } -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, - const char *ifname) -{ - const NMPObject *obj = NULL; + case RTM_DELLINK: + case RTM_DELADDR: + case RTM_DELROUTE: + cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; - if (ifname && *ifname) { - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + default: + break; + } } - return obj ? &obj->link : NULL; -} - -struct _nm_platform_link_get_by_address_data { - gconstpointer address; - guint8 length; -}; - -static gboolean -_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) -{ - return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); -} - -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - const NMPObject *obj; - struct _nm_platform_link_get_by_address_data d = { - .address = address, - .length = length, - }; - - if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) - return NULL; - if (!address) - return NULL; - - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, NULL, TRUE, NM_LINK_TYPE_NONE, - (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); - return obj ? &obj->link : NULL; -} - -/*****************************************************************************/ - -static const NMPObject * -link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - NM_SET_OUT (out_link, &obj->link); - - if (!obj->_link.netlink.lnk) - return NULL; - if ( link_type != NM_LINK_TYPE_NONE - && ( link_type != obj->link.type - || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) - return NULL; - - return obj->_link.netlink.lnk; } /*****************************************************************************/ @@ -4063,34 +4292,21 @@ do_add_link_with_lookup (NMPlatform *platform, struct nl_msg *nlmsg, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj = NULL; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; + NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); - if (nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { - /* hm, a link with such a name already exists. Try reloading first. */ - do_request_link (platform, 0, name); - - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); - if (obj) { - _LOGE ("do-add-link[%s/%s]: link already exists: %s", - name, - nm_link_type_to_string (link_type), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - return FALSE; - } - } - - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, nm_link_type_to_string (link_type), nl_geterror (nle), -nle); + NM_SET_OUT (out_link, NULL); return FALSE; } @@ -4100,35 +4316,29 @@ do_add_link_with_lookup (NMPlatform *platform, _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-link[%s/%s]: %s", name, nm_link_type_to_string (link_type), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); - - if (!obj) { - /* either kernel signaled failure, or it signaled success and the link object - * is not (yet) in the cache. Try to reload it... */ - do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); + if (out_link) { + obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); + *out_link = NMP_OBJECT_CAST_LINK (obj); } - if (out_link) - *out_link = obj ? &obj->link : NULL; - return !!obj; + return seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } -static gboolean -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) +static NMPlatformError +do_add_addrroute (NMPlatform *platform, + const NMPObject *obj_id, + struct nl_msg *nlmsg, + gboolean suppress_netlink_failure) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - const NMPObject *obj; nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, @@ -4136,72 +4346,69 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - return FALSE; + return NM_PLATFORM_ERROR_NETLINK; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + _NMLOG (( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + || ( suppress_netlink_failure + && seq_result < 0)) ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-%s[%s]: %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - /* In rare cases, the object is not yet ready as we received the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * FIXME: if the object already existed previously, we might not notice a - * missing update. It's not clear how to fix that reliably without refechting - * all the time. */ - obj = nmp_cache_lookup_obj (priv->cache, obj_id); - if (!obj) { - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - obj = nmp_cache_lookup_obj (priv->cache, obj_id); + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is not yet ready as we received the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); } - /* Adding is only successful, if kernel reported success *and* we have the - * expected object in cache afterwards. */ - return obj && seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + return wait_for_nl_response_to_plerr (seq_result); } static gboolean do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - gboolean success = TRUE; + gboolean success; const char *log_detail = ""; event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - goto out; + return FALSE; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); + success = TRUE; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { /* ok */ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) @@ -4216,42 +4423,60 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * else success = FALSE; - _NMLOG (success ? LOGL_DEBUG : LOGL_ERR, + _NMLOG (success ? LOGL_DEBUG : LOGL_WARN, "do-delete-%s[%s]: %s%s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); -out: - if (!nmp_cache_lookup_obj (priv->cache, obj_id)) - return TRUE; + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is still there after we receive the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + } - /* such an object still exists in the cache. To be sure, refetch it (and - * hope it's gone) */ - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - return !nmp_cache_lookup_obj (priv->cache, obj_id); + return success; } -static WaitForNlResponseResult -do_change_link_request (NMPlatform *platform, - int ifindex, - struct nl_msg *nlmsg) +static NMPlatformError +do_change_link (NMPlatform *platform, + ChangeLinkType change_link_type, + int ifindex, + struct nl_msg *nlmsg, + const ChangeLinkData *data) { nm_auto_pop_netns NMPNetns *netns = NULL; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + char s_buf[256]; + NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; + NMLogLevel log_level = LOGL_DEBUG; + const char *log_result = "failure"; + const char *log_detail = ""; + gs_free char *log_detail_free = NULL; + const NMPObject *obj_cache; - if (!nm_platform_netns_push (platform, &netns)) - return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + if (!nm_platform_netns_push (platform, &netns)) { + log_level = LOGL_ERR; + log_detail = ", failure to change network namespace"; + goto out; + } retry: - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { - _LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)", - ifindex, - nl_geterror (nle), -nle); - return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + log_level = LOGL_ERR; + log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)", + nl_geterror (nle), -nle); + log_detail = log_detail_free; + goto out; } /* always refetch the link after changing it. There seems to be issues @@ -4267,18 +4492,6 @@ retry: nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK; goto retry; } - return seq_result; -} - -static NMPlatformError -do_change_link_result (NMPlatform *platform, - int ifindex, - WaitForNlResponseResult seq_result) -{ - char s_buf[256]; - NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; - NMLogLevel log_level = LOGL_DEBUG; - const char *log_result = "failure", *log_detail = ""; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { log_result = "success"; @@ -4287,38 +4500,43 @@ do_change_link_result (NMPlatform *platform, } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) { log_detail = ", firmware not found"; result = NM_PLATFORM_ERROR_NO_FIRMWARE; + } else if ( NM_IN_SET (-((int) seq_result), ERANGE) + && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { + log_detail = ", setting MTU to requested size is not possible"; + result = NM_PLATFORM_ERROR_CANT_SET_MTU; + } else if ( NM_IN_SET (-((int) seq_result), ENFILE) + && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS + && (obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex)) + && obj_cache->link.addr.len == data->set_address.length + && memcmp (obj_cache->link.addr.data, data->set_address.address, data->set_address.length) == 0) { + /* workaround ENFILE which may be wrongly returned (bgo #770456). + * If the MAC address is as expected, assume success? */ + log_result = "success"; + log_detail = " (assume success changing address)"; + result = NM_PLATFORM_ERROR_SUCCESS; } else if (NM_IN_SET (-((int) seq_result), ENODEV)) { log_level = LOGL_DEBUG; result = NM_PLATFORM_ERROR_NOT_FOUND; } else { - log_level = LOGL_ERR; + log_level = LOGL_WARN; result = NM_PLATFORM_ERROR_UNSPECIFIED; } + +out: _NMLOG (log_level, "do-change-link[%d]: %s changing link: %s%s", ifindex, log_result, wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); - return result; } -static NMPlatformError -do_change_link (NMPlatform *platform, - int ifindex, - struct nl_msg *nlmsg) -{ - WaitForNlResponseResult seq_result; - - seq_result = do_change_link_request (platform, ifindex, nlmsg); - return do_change_link_result (platform, ifindex, seq_result); -} - static gboolean link_add (NMPlatform *platform, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) @@ -4337,9 +4555,6 @@ link_add (NMPlatform *platform, (void) nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); } - _LOGD ("link: add link '%s' of type '%s' (%d)", - name, nm_link_type_to_string (type), (int) type); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -4352,7 +4567,7 @@ link_add (NMPlatform *platform, if (address && address_len) NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); - if (!_nl_msg_new_link_set_linkinfo (nlmsg, type)) + if (!_nl_msg_new_link_set_linkinfo (nlmsg, type, veth_peer)) return FALSE; return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); @@ -4364,11 +4579,10 @@ static gboolean link_delete (NMPlatform *platform, int ifindex) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); NMPObject obj_id; const NMPObject *obj; - obj = nmp_cache_lookup_link (priv->cache, ifindex); + obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; @@ -4383,56 +4597,11 @@ link_delete (NMPlatform *platform, int ifindex) return do_delete_object (platform, &obj_id, nlmsg); } -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { - /* We could detect the @link_type. In this case the function returns - * our internel module names, which differs from rtnl_link_get_type(): - * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") - * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). - * Note that this functions is only used by NMDeviceGeneric to - * set type_description. */ - return nm_link_type_to_string (obj->link.type); - } - /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ - return obj->link.kind ?: "unknown"; -} - -static gboolean -link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPObject *link; - struct udev_device *udevice = NULL; - const char *uproperty; - - link = nmp_cache_lookup_link (priv->cache, ifindex); - if (!link) - return FALSE; - - udevice = link->_link.udev.device; - if (!udevice) - return FALSE; - - uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); - if (!uproperty) - return FALSE; - - *unmanaged = nm_udev_utils_property_as_boolean (uproperty); - return TRUE; -} - static gboolean link_refresh (NMPlatform *platform, int ifindex) { do_request_link (platform, ifindex, NULL); - return !!cache_lookup_link (platform, ifindex); + return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } static gboolean @@ -4454,7 +4623,7 @@ link_set_netns (NMPlatform *platform, return FALSE; NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: g_return_val_if_reached (FALSE); @@ -4484,7 +4653,7 @@ link_change_flags (NMPlatform *platform, flags_set); if (!nlmsg) return NM_PLATFORM_ERROR_UNSPECIFIED; - return do_change_link (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean @@ -4519,7 +4688,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) static const char * link_get_udi (NMPlatform *platform, int ifindex) { - const NMPObject *obj = cache_lookup_link (platform, ifindex); + const NMPObject *obj = nm_platform_link_get_obj (platform, ifindex, TRUE); if ( !obj || !obj->_link.netlink.is_in_netlink @@ -4528,35 +4697,21 @@ link_get_udi (NMPlatform *platform, int ifindex) return udev_device_get_syspath (obj->_link.udev.device); } -static struct udev_device * -link_get_udev_device (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - /* we don't use cache_lookup_link() because this would return NULL - * if the link is not visible in libnl. For link_get_udev_device() - * we want to return whatever we have, even if the link itself - * appears invisible via other platform functions. */ - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - return obj_cache ? obj_cache->_link.udev.device : NULL; -} - static NMPlatformError link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; + _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + if (!_support_user_ipv6ll_get ()) { _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); return NM_PLATFORM_ERROR_OPNOTSUPP; } - _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", - ifindex, - nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -4567,7 +4722,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - return do_change_link (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean @@ -4582,7 +4737,7 @@ link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) g_return_val_if_reached (FALSE); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -4606,7 +4761,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) nm_auto_pop_netns NMPNetns *netns = NULL; const NMPObject *obj; - obj = cache_lookup_link (platform, ifindex); + obj = nm_platform_link_get_obj (platform, ifindex, TRUE); /* Only ARPHRD_ETHER links can possibly support VLANs. */ if (!obj || obj->link.arptype != ARPHRD_ETHER) @@ -4647,8 +4802,12 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; gs_free char *mac = NULL; - WaitForNlResponseResult seq_result; - char s_buf[256]; + const ChangeLinkData d = { + .set_address = { + .address = address, + .length = length, + }, + }; if (!address || !length) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); @@ -4668,32 +4827,32 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); - seq_result = do_change_link_request (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); +nla_put_failure: + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); +} - if (NM_IN_SET (-((int) seq_result), ENFILE)) { - const NMPObject *obj_cache; +static NMPlatformError +link_set_name (NMPlatform *platform, int ifindex, const char *name) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - /* workaround ENFILE which may be wrongly returned (bgo #770456). - * If the MAC address is as expected, assume success? */ + _LOGD ("link: change %d: name: %s", ifindex, name); - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if ( obj_cache - && obj_cache->link.addr.len == length - && memcmp (obj_cache->link.addr.data, address, length) == 0) { - _NMLOG (LOGL_DEBUG, - "do-change-link[%d]: %s changing link: %s%s", - ifindex, - "success", - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), - " (assume success changing address)"); - return NM_PLATFORM_ERROR_SUCCESS; - } - } + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); - return do_change_link_result (platform, ifindex, seq_result); + NLA_PUT (nlmsg, IFLA_IFNAME, strlen (name) + 1, name); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (FALSE); } static gboolean @@ -4710,7 +4869,7 @@ link_get_permanent_address (NMPlatform *platform, return nmp_utils_ethtool_get_permanent_address (ifindex, buf, length); } -static gboolean +static NMPlatformError link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -4728,7 +4887,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) NLA_PUT_U32 (nlmsg, IFLA_MTU, mtu); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5359,7 +5518,6 @@ link_vlan_change (NMPlatform *platform, const NMVlanQosMapping *egress_map, gsize n_egress_map) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj_cache; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const NMPObjectLnkVlan *lnk; @@ -5371,7 +5529,7 @@ link_vlan_change (NMPlatform *platform, char s_ingress[256]; char s_egress[256]; - obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if ( !obj_cache || !obj_cache->_link.netlink.is_in_netlink) { _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); @@ -5437,7 +5595,7 @@ link_vlan_change (NMPlatform *platform, new_n_egress_map)) g_return_val_if_reached (FALSE); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static int @@ -5449,9 +5607,6 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, struct ifreq ifr = { }; int fd; - _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, - tap ? "tap" : "tun", name, owner, group); - fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd < 0) return FALSE; @@ -5467,30 +5622,30 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, ifr.ifr_flags |= NM_IFF_MULTI_QUEUE; if (ioctl (fd, TUNSETIFF, &ifr)) { - close (fd); + nm_close (fd); return FALSE; } if (owner >= 0 && owner < G_MAXINT32) { if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) { - close (fd); + nm_close (fd); return FALSE; } } if (group >= 0 && group < G_MAXINT32) { if (ioctl (fd, TUNSETGROUP, (gid_t) group)) { - close (fd); + nm_close (fd); return FALSE; } } if (ioctl (fd, TUNSETPERSIST, 1)) { - close (fd); + nm_close (fd); return FALSE; } do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, NULL, NULL); @@ -5520,7 +5675,7 @@ link_enslave (NMPlatform *platform, int master, int slave) NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5540,7 +5695,6 @@ _infiniband_partition_action (NMPlatform *platform, int p_key, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_close int dirfd = -1; char ifname_parent[IFNAMSIZ]; const NMPObject *obj; @@ -5576,7 +5730,7 @@ _infiniband_partition_action (NMPlatform *platform, if (action == INFINIBAND_ACTION_DELETE_CHILD) return TRUE; - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL); if (out_link) *out_link = obj ? &obj->link : NULL; @@ -5714,16 +5868,15 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru static gboolean link_can_assume (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformObject *const *objs; - guint i, len; - const NMPObject *link; + NMPLookup lookup; + const NMPObject *link, *o; + NMDedupMultiIter iter; + NMPCache *cache = nm_platform_get_cache (platform); if (ifindex <= 0) return FALSE; - link = cache_lookup_link (platform, ifindex); + link = nm_platform_link_get_obj (platform, ifindex, TRUE); if (!link) return FALSE; @@ -5733,21 +5886,21 @@ link_can_assume (NMPlatform *platform, int ifindex) if (link->link.master > 0) return TRUE; - if (nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex), - NULL)) + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex); + if (nmp_cache_lookup (cache, &lookup)) return TRUE; - objs = nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), - &len); - if (objs) { - for (i = 0; i < len; i++) { - const NMPlatformIP6Address *a = (NMPlatformIP6Address *) objs[i]; - - if (!IN6_IS_ADDR_LINKLOCAL (&a->address)) - return TRUE; - } + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex); + nmp_cache_iter_for_each (&iter, + nmp_cache_lookup (cache, &lookup), + &o) { + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP6_ADDRESS); + if (!IN6_IS_ADDR_LINKLOCAL (&o->ip6_address.address)) + return TRUE; } return FALSE; } @@ -5822,33 +5975,6 @@ link_get_driver_info (NMPlatform *platform, /*****************************************************************************/ -static GArray * -ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - - return nmp_cache_lookup_multi_to_array (priv->cache, - obj_type, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, - obj_type, - ifindex)); -} - -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); -} - static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -5877,7 +6003,7 @@ ip4_address_add (NMPlatform *platform, label); nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); - return do_add_addrroute (platform, &obj_id, nlmsg); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5906,8 +6032,8 @@ ip6_address_add (NMPlatform *platform, preferred, NULL); - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); - return do_add_addrroute (platform, &obj_id, nlmsg); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5956,303 +6082,139 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui if (!nlmsg) g_return_val_if_reached (FALSE); - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); return do_delete_object (platform, &obj_id, nlmsg); } -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_address; - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_address; - return NULL; -} - /*****************************************************************************/ -static GArray * -ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags) +static NMPlatformError +ip_route_add (NMPlatform *platform, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformIPRoute *const* routes; - GArray *array; - const NMPClass *klass; - gboolean with_rtprot_kernel; - guint i, len; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - klass = nmp_class_from_type (obj_type); - - nmp_cache_id_init_routes_visible (&cache_id, - obj_type, - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT), - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT), - ifindex); - - routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - - with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj; - if ( with_rtprot_kernel - || routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - g_array_append_vals (array, routes[i], 1); + switch (addr_family) { + case AF_INET: + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + break; + case AF_INET6: + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + break; + default: + nm_assert_not_reached (); } - return array; -} -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ROUTE, flags); -} + nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); -} - -static guint32 -ip_route_get_lock_flag (NMPlatformIPRoute *route) -{ - return (((guint32) route->lock_window) << RTAX_WINDOW) - | (((guint32) route->lock_cwnd) << RTAX_CWND) - | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) - | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) - | (((guint32) route->lock_mtu) << RTAX_MTU); -} - -static gboolean -ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - in_addr_t network; - - network = nm_utils_ip4_address_clear_host_address (route->network, route->plen); - - /* FIXME: take the scope from route into account */ - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET, - route->ifindex, - route->rt_source, - route->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, - &network, - route->plen, - &route->gateway, - route->metric, - route->mss, - route->pref_src ? &route->pref_src : NULL, - NULL, - 0, - route->tos, - route->window, - route->cwnd, - route->initcwnd, - route->initrwnd, - route->mtu, - ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - - nmp_object_stackinit_id_ip4_route (&obj_id, route->ifindex, network, route->plen, route->metric); - return do_add_addrroute (platform, &obj_id, nlmsg); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + return do_add_addrroute (platform, + &obj, + nlmsg, + NM_FLAGS_HAS (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } static gboolean -ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) +ip_route_delete (NMPlatform *platform, + const NMPObject *obj) { - NMPObject obj_id; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct in6_addr network; - - nm_utils_ip6_address_clear_host_address (&network, &route->network, route->plen); - - /* FIXME: take the scope from route into account */ - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET6, - route->ifindex, - route->rt_source, - IN6_IS_ADDR_UNSPECIFIED (&route->gateway) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, - &network, - route->plen, - &route->gateway, - route->metric, - route->mss, - !IN6_IS_ADDR_UNSPECIFIED (&route->pref_src) ? &route->pref_src : NULL, - !IN6_IS_ADDR_UNSPECIFIED (&route->src) ? &route->src : NULL, - route->src_plen, - route->tos, - route->window, - route->cwnd, - route->initcwnd, - route->initrwnd, - route->mtu, - ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - - nmp_object_stackinit_id_ip6_route (&obj_id, route->ifindex, &network, route->plen, route->metric); - return do_add_addrroute (platform, &obj_id, nlmsg); -} -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - network = nm_utils_ip4_address_clear_host_address (network, plen); + if (!NMP_OBJECT_IS_STACKINIT (obj)) + obj_keep_alive = nmp_object_ref (obj); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj); + if (!nlmsg) + g_return_val_if_reached (FALSE); + return do_delete_object (platform, obj, nlmsg); +} - if (metric == 0) { - /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. - * If no route with metric 0 exists, it might delete another route to the same destination. - * For nm_platform_ip4_route_delete() we don't want this semantic. - * - * Instead, make sure that we have the most recent state and process all - * delayed actions (including re-reading data from netlink). */ - delayed_action_handle_all (platform, TRUE); - - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) { - /* hmm... we are about to delete an IP4 route with metric 0. We must only - * send the delete request if such a route really exists. Above we refreshed - * the platform cache, still no such route exists. - * - * Be extra careful and reload the routes. We must be sure that such a - * route doesn't exists, because when we add an IPv4 address, we immediately - * afterwards try to delete the kernel-added device route with metric 0. - * It might be, that we didn't yet get the notification about that route. - * - * FIXME: once our ip4_address_add() is sure that upon return we have - * the latest state from in the platform cache, we might save this - * additional expensive cache-resync. */ - do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE); +/*****************************************************************************/ - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) - return TRUE; - } - } +static NMPlatformError +ip_route_get (NMPlatform *platform, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route) +{ + const gboolean is_v4 = (addr_family == AF_INET); + const int addr_len = is_v4 ? 4 : 16; + int try_count = 0; + WaitForNlResponseResult seq_result; + int nle; + nm_auto_nlmsg NMPObject *route = NULL; - nlmsg = _nl_msg_new_route (RTM_DELROUTE, - 0, - AF_INET, - ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - RT_SCOPE_NOWHERE, - &network, - plen, - NULL, - metric, - 0, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); - if (!nlmsg) - return FALSE; + nm_assert (NM_IS_LINUX_PLATFORM (platform)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (address); + + do { + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = addr_family, + .r.rtm_tos = 0, + .r.rtm_dst_len = is_v4 ? 32 : 128, + .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, + }; - return do_delete_object (platform, &obj_id, nlmsg); -} + g_clear_pointer (&route, nmp_object_unref); -static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_DST, address, addr_len)) + nm_assert_not_reached (); - metric = nm_utils_ip6_route_metric_normalize (metric); - - nm_utils_ip6_address_clear_host_address (&network, &network, plen); - - nlmsg = _nl_msg_new_route (RTM_DELROUTE, - 0, - AF_INET6, - ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - RT_SCOPE_NOWHERE, - &network, - plen, - NULL, - metric, - 0, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); - if (!nlmsg) - return FALSE; + if (oif_ifindex > 0) { + gint32 ii = oif_ifindex; - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_OIF, &ii, sizeof (ii))) + nm_assert_not_reached (); + } - return do_delete_object (platform, &obj_id, nlmsg); -} + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); + if (nle < 0) { + _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", + g_strerror (-nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; + delayed_action_handle_all (platform, FALSE); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_route; - return NULL; -} + /* Retry, if we failed due to a cache resync. That can happen when the netlink + * socket fills up and we lost the response. */ + } while ( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC + && ++try_count < 10); -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; + if (seq_result < 0) { + /* negative seq_result is an errno from kernel. Map it to negative + * NMPlatformError (which are also errno). */ + return (NMPlatformError) seq_result; + } - metric = nm_utils_ip6_route_metric_normalize (metric); + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + if (route) { + NM_SET_OUT (out_route, g_steal_pointer (&route)); + return NM_PLATFORM_ERROR_SUCCESS; + } + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + } - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_route; - return NULL; + return NM_PLATFORM_ERROR_UNSPECIFIED; } /*****************************************************************************/ @@ -6351,6 +6313,7 @@ continue_reading: gboolean abort_parsing = FALSE; gboolean process_valid_msg = FALSE; guint32 seq_number; + char buf_nlmsghdr[400]; msg = nlmsg_convert (hdr); if (!msg) { @@ -6370,8 +6333,8 @@ continue_reading: goto stop; } - _LOGt ("netlink: recvmsg: new message type %d, seq %u", - hdr->nlmsg_type, hdr->nlmsg_seq); + _LOGt ("netlink: recvmsg: new message %s", + _nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); if (creds) nlmsg_set_creds (msg, creds); @@ -6512,7 +6475,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) nle = event_handler_recvmsgs (platform, TRUE); - if (nle < 0) + if (nle < 0) { switch (nle) { case -NLE_AGAIN: goto after_read; @@ -6543,6 +6506,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) default: _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; + } } any = TRUE; } @@ -6615,19 +6579,19 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, struct udev_device *udevice) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_cache, &was_visible, cache_pre_hook, platform); + cache_op = nmp_cache_update_link_udev (nm_platform_get_cache (platform), ifindex, udevice, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { nm_auto_pop_netns NMPNetns *netns = NULL; + cache_on_change (platform, cache_op, obj_old, obj_new); if (!nm_platform_netns_push (platform, &netns)) return; - do_emit_signal (platform, obj_cache, cache_op, was_visible); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } } @@ -6683,7 +6647,7 @@ udev_device_removed (NMPlatform *platform, if (ifindex <= 0) { const NMPObject *obj; - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udevice); if (obj) ifindex = obj->link.ifindex; @@ -6735,22 +6699,12 @@ static void nm_linux_platform_init (NMLinuxPlatform *self) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self); - gboolean use_udev; - - use_udev = nmp_netns_is_initial () - && access ("/sys", W_OK) == 0; priv->nlh_seq_next = 1; - priv->cache = nmp_cache_new (use_udev); priv->delayed_action.list_master_connected = g_ptr_array_new (); priv->delayed_action.list_refresh_link = g_ptr_array_new (); priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); - - if (use_udev) { - priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, - handle_udev_event, self); - } } static void @@ -6764,6 +6718,11 @@ constructed (GObject *_object) nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ()); + if (nm_platform_get_use_udev (platform)) { + priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, + handle_udev_event, platform); + } + _LOGD ("create (%s netns, %s, %s udev)", !platform->_netns ? "ignore" : "use", !platform->_netns && nmp_netns_is_initial () @@ -6773,7 +6732,7 @@ constructed (GObject *_object) : nm_sprintf_bufa (100, "in netns[%p]%s", nmp_netns_get_current (), nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")), - nmp_cache_use_udev_get (priv->cache) ? "use" : "no"); + nm_platform_get_use_udev (platform) ? "use" : "no"); priv->nlh = nl_socket_alloc (); g_assert (priv->nlh); @@ -6874,8 +6833,6 @@ dispose (GObject *object) g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - g_clear_pointer (&priv->prune_candidates, g_hash_table_unref); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } @@ -6884,8 +6841,6 @@ finalize (GObject *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); - nmp_cache_free (priv->cache); - g_ptr_array_unref (priv->delayed_action.list_master_connected); g_ptr_array_unref (priv->delayed_action.list_refresh_link); g_array_unref (priv->delayed_action.list_wait_for_nl_response); @@ -6919,16 +6874,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - platform_class->link_get_unmanaged = link_get_unmanaged; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_refresh = link_refresh; @@ -6940,7 +6887,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_noarp = link_set_noarp; platform_class->link_get_udi = link_get_udi; - platform_class->link_get_udev_device = link_get_udev_device; platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; platform_class->link_set_token = link_set_token; @@ -6948,6 +6894,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_address = link_set_address; platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; + platform_class->link_set_name = link_set_name; platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs; platform_class->link_get_physical_port_id = link_get_physical_port_id; @@ -6995,26 +6942,16 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_ipip_add = link_ipip_add; platform_class->link_sit_add = link_sit_add; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; - platform_class->ip4_route_add = ip4_route_add; - platform_class->ip6_route_add = ip6_route_add; - platform_class->ip4_route_delete = ip4_route_delete; - platform_class->ip6_route_delete = ip6_route_delete; - - platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; - platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; + platform_class->ip_route_add = ip_route_add; + platform_class->ip_route_delete = ip_route_delete; + platform_class->ip_route_get = ip_route_get; + + platform_class->check_kernel_support = check_kernel_support; platform_class->process_events = process_events; } diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index 6b66ea69..bff6c00c 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -39,10 +39,4 @@ NMPlatform *nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support void nm_linux_platform_setup (void); -struct _NMPCacheId; - -const NMPlatformObject *const *nm_linux_platform_lookup (NMPlatform *platform, - const struct _NMPCacheId *cache_id, - guint *out_len); - #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h new file mode 100644 index 00000000..b6c94baa --- /dev/null +++ b/src/platform/nm-platform-private.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#ifndef __NM_PLATFORM_PRIVATE_H__ +#define __NM_PLATFORM_PRIVATE_H__ + +#include "nm-platform.h" +#include "nmp-object.h" + +NMPCache *nm_platform_get_cache (NMPlatform *self); + +#define NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform) \ + G_STMT_START { \ + NMPlatform *_platform = (platform); \ + \ + nm_assert (NM_IS_PLATFORM (_platform)); \ + nm_assert (NM_IN_SET (nm_platform_netns_get (_platform), NULL, nmp_netns_get_current ())); \ + } G_STMT_END + +void nm_platform_cache_update_emit_signal (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); + +#endif /* __NM_PLATFORM_PRIVATE_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index a244ff39..ffc4b395 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -32,12 +32,16 @@ #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/rtnetlink.h> +#include <libudev.h> #include "nm-utils.h" #include "nm-core-internal.h" +#include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/nm-udev-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "nmp-object.h" #include "nmp-netns.h" @@ -79,12 +83,23 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 }; enum { PROP_0, PROP_NETNS_SUPPORT, + PROP_USE_UDEV, PROP_LOG_WITH_PTR, LAST_PROP, }; typedef struct _NMPlatformPrivate { + bool use_udev:1; bool log_with_ptr:1; + + NMPlatformKernelSupportFlags support_checked; + NMPlatformKernelSupportFlags support_present; + + guint ip4_dev_route_blacklist_check_id; + guint ip4_dev_route_blacklist_gc_timeout_id; + GHashTable *ip4_dev_route_blacklist_hash; + NMDedupMultiIndex *multi_idx; + NMPCache *cache; } NMPlatformPrivate; G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) @@ -93,6 +108,16 @@ G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) /*****************************************************************************/ +static void _ip4_dev_route_blacklist_schedule (NMPlatform *self); + +/*****************************************************************************/ + +gboolean +nm_platform_get_use_udev (NMPlatform *self) +{ + return NM_PLATFORM_GET_PRIVATE (self)->use_udev; +} + gboolean nm_platform_get_log_with_ptr (NMPlatform *self) { @@ -193,17 +218,18 @@ nm_platform_get () /*****************************************************************************/ -/** - * _nm_platform_error_to_string: - * @error_code: the error code to stringify. - * - * Returns: A string representation of the error. - * For negative numbers, this function interprets - * the code as -errno. - * For invalid (positive) numbers it returns NULL. - */ -NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, - NM_UTILS_LOOKUP_DEFAULT ( val < 0 ? g_strerror (- ((int) val)) : NULL ), +NMDedupMultiIndex * +nm_platform_get_multi_idx (NMPlatform *self) +{ + g_return_val_if_fail (NM_IS_PLATFORM (self), NULL); + + return NM_PLATFORM_GET_PRIVATE (self)->multi_idx; +} + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nm_platform_error_to_string, NMPlatformError, + NM_UTILS_LOOKUP_DEFAULT (NULL), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_SUCCESS, "success"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_BUG, "bug"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_UNSPECIFIED, "unspecified"), @@ -213,35 +239,101 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_SLAVE, "not-slave"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NO_FIRMWARE, "no-firmware"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_OPNOTSUPP, "not-supported"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NETLINK, "netlink"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_CANT_SET_MTU, "cant-set-mtu"), NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT), ); -/*****************************************************************************/ - -gboolean -nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) +/** + * nm_platform_error_to_string: + * @error_code: the error code to stringify. + * @buf: (allow-none): buffer + * @buf_len: size of buffer + * + * Returns: A string representation of the error. + * For negative numbers, this function interprets + * the code as -errno. + * For invalid (positive) numbers it returns NULL. + */ +const char * +nm_platform_error_to_string (NMPlatformError error_code, char *buf, gsize buf_len) { - _CHECK_SELF (self, klass, FALSE); + const char *s; - if (!klass->check_support_kernel_extended_ifa_flags) - return FALSE; + if (error_code < 0) { + int errsv = -((int) error_code); - return klass->check_support_kernel_extended_ifa_flags (self); + nm_utils_to_string_buffer_init (&buf, &buf_len); + g_snprintf (buf, buf_len, "%s (%d)", g_strerror (errsv), errsv); + } else { + s = _nm_platform_error_to_string (error_code); + if (s) { + if (!buf) + return s; + g_strlcpy (buf, s, buf_len); + } else { + nm_utils_to_string_buffer_init (&buf, &buf_len); + g_snprintf (buf, buf_len, "(%d)", (int) error_code); + } + } + + return buf; } -gboolean -nm_platform_check_support_user_ipv6ll (NMPlatform *self) +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, + NM_UTILS_LOOKUP_DEFAULT (NULL), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_CHANGE, "change"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_REPLACE, "replace"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_PREPEND, "prepend"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_APPEND, "append"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_TEST, "test"), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_FMASK), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE), +); + +#define _nmp_nlm_flag_to_string(flags) \ + ({ \ + NMPNlmFlags _flags = (flags); \ + \ + _nmp_nlm_flag_to_string_lookup (flags) ?: nm_sprintf_bufa (100, "new[0x%x]", (unsigned) _flags); \ + }) + +/*****************************************************************************/ + +NMPlatformKernelSupportFlags +nm_platform_check_kernel_support (NMPlatform *self, + NMPlatformKernelSupportFlags request_flags) { - static int supported = -1; + NMPlatformPrivate *priv; - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, TRUE); - if (!klass->check_support_user_ipv6ll) - return FALSE; + priv = NM_PLATFORM_GET_PRIVATE (self); + + /* we cache the response from subclasses and only request it once. + * This probably gives better performance, but more importantly, + * we are guaranteed that the answer for a certain request_flag + * is always the same. */ + if (G_UNLIKELY (!NM_FLAGS_ALL (priv->support_checked, request_flags))) { + NMPlatformKernelSupportFlags checked, response; - if (supported < 0) - supported = klass->check_support_user_ipv6ll (self) ? 1 : 0; - return !!supported; + checked = request_flags & ~priv->support_checked; + nm_assert (checked); + + if (klass->check_kernel_support) + response = klass->check_kernel_support (self, checked); + else { + /* fake platform. Pretend no support for anything. */ + response = 0; + } + + priv->support_checked |= checked; + priv->support_present = (priv->support_present & ~checked) | (response & checked); + } + + return priv->support_present & request_flags; } /** @@ -322,6 +414,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, { const char *path; gint64 cur; + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; _CHECK_SELF (self, klass, FALSE); @@ -333,7 +426,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, if (value < 10) return FALSE; - path = nm_utils_ip6_property_path (iface, "hop_limit"); + path = nm_utils_sysctl_ip_conf_path (AF_INET6, buf, iface, "hop_limit"); cur = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 1, G_MAXINT32, -1); /* only allow increasing the hop-limit to avoid DOS by an attacker @@ -440,8 +533,8 @@ _link_get_all_presort (gconstpointer p_a, gconstpointer p_b, gpointer sort_by_name) { - const NMPlatformLink *a = p_a; - const NMPlatformLink *b = p_b; + const NMPlatformLink *a = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_a)); + const NMPlatformLink *b = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_b)); /* Loopback always first */ if (a->ifindex == 1) @@ -463,43 +556,56 @@ _link_get_all_presort (gconstpointer p_a, /** * nm_platform_link_get_all: - * self: platform instance + * @self: platform instance + * @sort_by_name: whether to sort by name or ifindex. * * Retrieve a snapshot of configuration for all links at once. The result is - * owned by the caller and should be freed with g_array_unref(). + * owned by the caller and should be freed with g_ptr_array_unref(). */ -GArray * +GPtrArray * nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) { - GArray *links, *result; - guint i, j, nresult; - GHashTable *unseen; - NMPlatformLink *item; + gs_unref_ptrarray GPtrArray *links = NULL; + GPtrArray *result; + guint i, nresult; + gs_unref_hashtable GHashTable *unseen = NULL; + const NMPlatformLink *item; + NMPLookup lookup; _CHECK_SELF (self, klass, NULL); - links = klass->link_get_all (self); + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + links = nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, &lookup), + NULL, NULL); + if (!links) + return NULL; + + for (i = 0; i < links->len; ) { + if (!nmp_object_is_visible (links->pdata[i])) + g_ptr_array_remove_index_fast (links, i); + else + i++; + } - if (!links || links->len == 0) - return links; + if (links->len == 0) + return NULL; /* first sort the links by their ifindex or name. Below we will sort * further by moving children/slaves to the end. */ - g_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); + g_ptr_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); unseen = g_hash_table_new (g_direct_hash, g_direct_equal); for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); - + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); nm_assert (item->ifindex > 0); if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL)) nm_assert_not_reached (); } -#ifndef G_DISABLE_ASSERT +#if NM_MORE_ASSERTS /* Ensure that link_get_all returns a consistent and valid result. */ for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); if (!item->ifindex) continue; @@ -519,54 +625,75 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) #endif /* Re-order the links list such that children/slaves come after all ancestors */ - nresult = g_hash_table_size (unseen); - result = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nresult); - g_array_set_size (result, nresult); + nm_assert (g_hash_table_size (unseen) == links->len); + nresult = links->len; + result = g_ptr_array_new_full (nresult, (GDestroyNotify) nmp_object_unref); - j = 0; - do { + while (TRUE) { gboolean found_something = FALSE; guint first_idx = G_MAXUINT; for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); - if (!item->ifindex) + if (!item) continue; - if (first_idx == G_MAXUINT) - first_idx = i; - g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master))) - continue; + goto skip; if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) - continue; + goto skip; g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); - g_array_index (result, NMPlatformLink, j++) = *item; - item->ifindex = 0; + g_ptr_array_add (result, links->pdata[i]); + links->pdata[i] = NULL; found_something = TRUE; + continue; +skip: + if (first_idx == G_MAXUINT) + first_idx = i; } - if (!found_something) { + if (found_something) { + if (first_idx == G_MAXUINT) + break; + } else { + nm_assert (first_idx != G_MAXUINT); /* There is a loop, pop the first (remaining) element from the list. * This can happen for veth pairs where each peer is parent of the other end. */ - item = &g_array_index (links, NMPlatformLink, first_idx); - + item = NMP_OBJECT_CAST_LINK (links->pdata[first_idx]); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); - g_array_index (result, NMPlatformLink, j++) = *item; - item->ifindex = 0; + g_ptr_array_add (result, links->pdata[first_idx]); + links->pdata[first_idx] = NULL; } - } while (j < nresult); - - g_hash_table_destroy (unseen); - g_array_free (links, TRUE); + nm_assert (result->len < nresult); + } + nm_assert (result->len == nresult); return result; } +/*****************************************************************************/ + +const NMPObject * +nm_platform_link_get_obj (NMPlatform *self, + int ifindex, + gboolean visible_only) +{ + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); + if ( !obj_cache + || ( visible_only + && !nmp_object_is_visible (obj_cache))) + return NULL; + return obj_cache; +} + +/*****************************************************************************/ + /** * nm_platform_link_get: * @self: platform instance @@ -577,16 +704,20 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) * Returns: %NULL, if such a link exists or the internal * platform link object. Do not modify the returned value. * Also, be aware that any subsequent platform call might - * invalidated/modify the returned instance. + * invalidate/modify the returned instance. **/ const NMPlatformLink * nm_platform_link_get (NMPlatform *self, int ifindex) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - if (ifindex > 0) - return klass->link_get (self, ifindex); - return NULL; + if (ifindex <= 0) + return NULL; + + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + return NMP_OBJECT_CAST_LINK (obj); } /** @@ -599,11 +730,27 @@ nm_platform_link_get (NMPlatform *self, int ifindex) const NMPlatformLink * nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - if (ifname && *ifname) - return klass->link_get_by_ifname (self, ifname); - return NULL; + if (!ifname || !*ifname) + return NULL; + + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + return NMP_OBJECT_CAST_LINK (obj); +} + +struct _nm_platform_link_get_by_address_data { + gconstpointer address; + guint8 length; +}; + +static gboolean +_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) +{ + return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); } /** @@ -620,15 +767,26 @@ nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length) { + const NMPObject *obj; + struct _nm_platform_link_get_by_address_data d = { + .address = address, + .length = length, + }; + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (length == 0 || address, NULL); - if (length > 0) { - if (length > NM_UTILS_HWADDR_LEN_MAX) - g_return_val_if_reached (NULL); - return klass->link_get_by_address (self, address, length); - } - return NULL; + if (length == 0) + return NULL; + + if (length > NM_UTILS_HWADDR_LEN_MAX) + g_return_val_if_reached (NULL); + if (!address) + g_return_val_if_reached (NULL); + + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + 0, NULL, TRUE, NM_LINK_TYPE_NONE, + (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); + return NMP_OBJECT_CAST_LINK (obj); } static NMPlatformError @@ -662,6 +820,7 @@ _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, c * @self: platform instance * @name: Interface name * @type: Interface type + * @veth_peer: For veths, the peer name * @address: (allow-none): set the mac address of the link * @address_len: the length of the @address * @out_link: on success, the link object @@ -680,27 +839,51 @@ static NMPlatformError nm_platform_link_add (NMPlatform *self, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) { NMPlatformError plerr; + char addr_buf[NM_UTILS_HWADDR_LEN_MAX * 3]; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail ((address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (address_len <= NM_UTILS_HWADDR_LEN_MAX, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail ((!!veth_peer) == (type == NM_LINK_TYPE_VETH), NM_PLATFORM_ERROR_BUG); plerr = _link_add_check_existing (self, name, type, out_link); if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding %s '%s'", nm_link_type_to_string (type), name); - if (!klass->link_add (self, name, type, address, address_len, out_link)) + _LOGD ("link: adding link '%s' of type '%s' (%d)" + "%s%s" /* address */ + "%s%s" /* veth peer */ + "", + name, + nm_link_type_to_string (type), + (int) type, + address ? ", address: " : "", + address ? nm_utils_hwaddr_ntoa_buf (address, address_len, FALSE, addr_buf, sizeof (addr_buf)) : "", + veth_peer ? ", veth-peer: " : "", + veth_peer ?: ""); + + if (!klass->link_add (self, name, type, veth_peer, address, address_len, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; return NM_PLATFORM_ERROR_SUCCESS; } +NMPlatformError +nm_platform_link_veth_add (NMPlatform *self, + const char *name, + const char *peer, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add (self, name, NM_LINK_TYPE_VETH, peer, NULL, 0, out_link); +} + /** * nm_platform_link_dummy_add: * @self: platform instance @@ -714,7 +897,7 @@ nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, 0, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, NULL, 0, out_link); } /** @@ -846,9 +1029,26 @@ nm_platform_link_get_type (NMPlatform *self, int ifindex) const char * nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - return klass->link_get_type_name (self, ifindex); + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + + if (!obj) + return NULL; + + if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { + /* We could detect the @link_type. In this case the function returns + * our internel module names, which differs from rtnl_link_get_type(): + * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") + * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). + * Note that this functions is only used by NMDeviceGeneric to + * set type_description. */ + return nm_link_type_to_string (obj->link.type); + } + /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ + return obj->link.kind ?: "unknown"; } /** @@ -863,11 +1063,26 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged) { + const NMPObject *link; + struct udev_device *udevice = NULL; + const char *uproperty; + _CHECK_SELF (self, klass, FALSE); - if (klass->link_get_unmanaged) - return klass->link_get_unmanaged (self, ifindex, unmanaged); - return FALSE; + link = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); + if (!link) + return FALSE; + + udevice = link->_link.udev.device; + if (!udevice) + return FALSE; + + uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); + if (!uproperty) + return FALSE; + + *unmanaged = nm_udev_utils_property_as_boolean (uproperty); + return TRUE; } /** @@ -1013,13 +1228,14 @@ nm_platform_link_get_udi (NMPlatform *self, int ifindex) struct udev_device * nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) { + const NMPObject *obj_cache; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, NULL); - if (klass->link_get_udev_device) - return klass->link_get_udev_device (self, ifindex); - return NULL; + obj_cache = nm_platform_link_get_obj (self, ifindex, FALSE); + return obj_cache ? obj_cache->_link.udev.device : NULL; } /** @@ -1284,7 +1500,7 @@ nm_platform_link_set_noarp (NMPlatform *self, int ifindex) * * Set interface MTU. */ -gboolean +NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) { _CHECK_SELF (self, klass, FALSE); @@ -1315,6 +1531,30 @@ nm_platform_link_get_mtu (NMPlatform *self, int ifindex) } /** + * nm_platform_link_set_name: + * @self: platform instance + * @ifindex: Interface index + * @name: The new interface name + * + * Set interface name. + */ +gboolean +nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex >= 0, FALSE); + g_return_val_if_fail (name, FALSE); + + _LOGD ("link: setting '%s' (%d) name %s", nm_platform_link_get_name (self, ifindex), ifindex, name); + + if (strlen (name) + 1 > IFNAMSIZ) + return FALSE; + + return klass->link_set_name (self, ifindex, name); +} + +/** * nm_platform_link_get_physical_port_id: * @self: platform instance * @ifindex: Interface index @@ -1465,7 +1705,7 @@ nm_platform_link_release (NMPlatform *self, int master, int slave) * @self: platform instance * @slave: Interface index of the slave. * - * Returns: Interfase index of the slave's master. + * Returns: Interface index of the slave's master. */ int nm_platform_link_get_master (NMPlatform *self, int slave) @@ -1519,13 +1759,28 @@ nm_platform_link_can_assume (NMPlatform *self, int ifindex) const NMPObject * nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) { + const NMPObject *obj; + _CHECK_SELF (self, klass, FALSE); NM_SET_OUT (out_link, NULL); g_return_val_if_fail (ifindex > 0, NULL); - return klass->link_get_lnk (self, ifindex, link_type, out_link); + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + if (!obj) + return NULL; + + NM_SET_OUT (out_link, &obj->link); + + if (!obj->_link.netlink.lnk) + return NULL; + if ( link_type != NM_LINK_TYPE_NONE + && ( link_type != obj->link.type + || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) + return NULL; + + return obj->_link.netlink.lnk; } static gconstpointer @@ -1616,7 +1871,7 @@ nm_platform_link_bridge_add (NMPlatform *self, size_t address_len, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, address, address_len, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, NULL, address, address_len, out_link); } /** @@ -1632,7 +1887,7 @@ nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, 0, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, NULL, 0, out_link); } /** @@ -1648,7 +1903,7 @@ nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, 0, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, NULL, 0, out_link); } /** @@ -2617,6 +2872,82 @@ nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean * /*****************************************************************************/ +const NMDedupMultiHeadEntry * +nm_platform_lookup_all (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + return nmp_cache_lookup_all (nm_platform_get_cache (platform), + cache_id_type, + obj); +} + +const NMDedupMultiEntry * +nm_platform_lookup_entry (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform), + cache_id_type, + obj); +} + +const NMDedupMultiHeadEntry * +nm_platform_lookup (NMPlatform *self, + const NMPLookup *lookup) +{ + return nmp_cache_lookup (nm_platform_get_cache (self), + lookup); +} + +gboolean +nm_platform_lookup_predicate_routes_main (const NMPObject *obj, + gpointer user_data) +{ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + return nm_platform_route_table_is_main (obj->ip_route.table_coerced); +} + +gboolean +nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data) +{ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + return nm_platform_route_table_is_main (obj->ip_route.table_coerced) + && obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; +} + +/** + * nm_platform_lookup_clone: + * @self: + * @lookup: + * @predicate: if given, only objects for which @predicate returns %TRUE are included + * in the result. + * @user_data: user data for @predicate + * + * Returns the result of lookup in a GPtrArray. The result array contains + * references objects from the cache, it's destroy function will unref them. + * + * The user must unref the GPtrArray, which will also unref the NMPObject + * elements. + * + * The elements in the array *must* not be modified. + * + * Returns: the result of the lookup. + */ +GPtrArray * +nm_platform_lookup_clone (NMPlatform *self, + const NMPLookup *lookup, + NMPObjectPredicateFunc predicate, + gpointer user_data) +{ + return nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, lookup), + (NMDedupMultiFcnSelectPredicate) predicate, + user_data); +} + void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) { @@ -2636,26 +2967,6 @@ nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr) return &addr->peer_address; } -GArray * -nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - - return klass->ip4_address_get_all (self, ifindex); -} - -GArray * -nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - - return klass->ip6_address_get_all (self, ifindex); -} - gboolean nm_platform_ip4_address_add (NMPlatform *self, int ifindex, @@ -2768,54 +3079,41 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a const NMPlatformIP4Address * nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, guint32 peer_address) { + NMPObject obj_id; + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (plen <= 32, NULL); - return klass->ip4_address_get (self, ifindex, address, plen, peer_address); + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, address, plen, peer_address); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + nm_assert (!obj || nmp_object_is_visible (obj)); + return NMP_OBJECT_CAST_IP4_ADDRESS (obj); } const NMPlatformIP6Address * -nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen) +nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address) { - _CHECK_SELF (self, klass, NULL); + NMPObject obj_id; + const NMPObject *obj; - g_return_val_if_fail (plen <= 128, NULL); - - return klass->ip6_address_get (self, ifindex, address, plen); -} - -static const NMPlatformIP4Address * -array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address, gint32 now) -{ - guint len = addresses ? addresses->len : 0; - guint i; - - for (i = 0; i < len; i++) { - const NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i); - - if ( candidate->address == address->address - && candidate->plen == address->plen - && ((candidate->peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) { - guint32 lifetime, preferred; - - if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, - now, &lifetime, &preferred)) - return candidate; - } - } + _CHECK_SELF (self, klass, NULL); - return NULL; + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &address); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + nm_assert (!obj || nmp_object_is_visible (obj)); + return NMP_OBJECT_CAST_IP6_ADDRESS (obj); } static gboolean -array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address, gint32 now) +array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now) { guint len = addresses ? addresses->len : 0; guint i; for (i = 0; i < len; i++) { - NMPlatformIP6Address *candidate = &g_array_index (addresses, NMPlatformIP6Address, i); + NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]); if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) { guint32 lifetime, preferred; @@ -2830,69 +3128,100 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address } static gboolean -_ptr_inside_ip4_addr_array (const GArray *array, gconstpointer needle) +ip4_addr_subnets_is_plain_address (const GPtrArray *addresses, gconstpointer needle) { - return needle >= (gconstpointer) &g_array_index (array, const NMPlatformIP4Address, 0) - && needle < (gconstpointer) &g_array_index (array, const NMPlatformIP4Address, array->len); + return needle >= (gconstpointer) &addresses->pdata[0] + && needle < (gconstpointer) &addresses->pdata[addresses->len]; +} + +static const NMPObject ** +ip4_addr_subnets_addr_list_get (const GPtrArray *addr_list, guint idx) +{ + nm_assert (addr_list); + nm_assert (addr_list->len > 1); + nm_assert (idx < addr_list->len); + nm_assert (addr_list->pdata[idx]); + nm_assert ( !(*((gpointer *) addr_list->pdata[idx])) + || NMP_OBJECT_CAST_IP4_ADDRESS (*((gpointer *) addr_list->pdata[idx]))); + nm_assert (idx == 0 || ip4_addr_subnets_addr_list_get (addr_list, idx - 1)); + return addr_list->pdata[idx]; } static void -ip4_addr_subnets_destroy_index (GHashTable *ht, const GArray *addresses) +ip4_addr_subnets_destroy_index (GHashTable *subnets, const GPtrArray *addresses) { GHashTableIter iter; gpointer p; - g_hash_table_iter_init (&iter, ht); + if (!subnets) + return; + g_hash_table_iter_init (&iter, subnets); while (g_hash_table_iter_next (&iter, NULL, &p)) { - if (!_ptr_inside_ip4_addr_array (addresses, p)) { + if (!ip4_addr_subnets_is_plain_address (addresses, p)) g_ptr_array_free ((GPtrArray *) p, TRUE); - } } - g_hash_table_unref (ht); + g_hash_table_unref (subnets); } static GHashTable * -ip4_addr_subnets_build_index (const GArray *addresses, gboolean consider_flags) +ip4_addr_subnets_build_index (const GPtrArray *addresses, + gboolean consider_flags, + gboolean full_index) { - const NMPlatformIP4Address *address; - gpointer p; GHashTable *subnets; - GPtrArray *ptr; - guint32 net; guint i; - gint position; - if (!addresses) - return NULL; + nm_assert (addresses && addresses->len); - subnets = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - NULL); + subnets = g_hash_table_new (NULL, NULL); /* Build a hash table of all addresses per subnet */ for (i = 0; i < addresses->len; i++) { - address = &g_array_index (addresses, const NMPlatformIP4Address, i); - net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen); - if (!g_hash_table_lookup_extended (subnets, GUINT_TO_POINTER (net), NULL, &p)) { - g_hash_table_insert (subnets, GUINT_TO_POINTER (net), (gpointer) address); + const NMPlatformIP4Address *address; + gpointer p_address; + GPtrArray *addr_list; + guint32 net; + int position; + gpointer p; + + if (!addresses->pdata[i]) continue; - } - if (_ptr_inside_ip4_addr_array (addresses, p)) { - ptr = g_ptr_array_new (); - g_hash_table_insert (subnets, GUINT_TO_POINTER (net), ptr); - g_ptr_array_add (ptr, p); - } else - ptr = p; - if (!consider_flags || NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) - position = -1; /* append */ - else - position = 0; /* prepend */ + p_address = &addresses->pdata[i]; + address = NMP_OBJECT_CAST_IP4_ADDRESS (addresses->pdata[i]); - g_ptr_array_insert (ptr, position, (gpointer) address); + net = address->address & _nm_utils_ip4_prefix_to_netmask (address->plen); + if (!g_hash_table_lookup_extended (subnets, GUINT_TO_POINTER (net), NULL, &p)) { + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), p_address); + continue; + } + nm_assert (p); + + if (full_index) { + if (ip4_addr_subnets_is_plain_address (addresses, p)) { + addr_list = g_ptr_array_new (); + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), addr_list); + g_ptr_array_add (addr_list, p); + } else + addr_list = p; + + if ( !consider_flags + || NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) + position = -1; /* append */ + else + position = 0; /* prepend */ + g_ptr_array_insert (addr_list, position, p_address); + } else { + /* we only care about the primary. No need to track the secondaries + * as a GPtrArray. */ + nm_assert (ip4_addr_subnets_is_plain_address (addresses, p)); + if ( consider_flags + && !NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) { + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), p_address); + } + } } return subnets; @@ -2911,23 +3240,33 @@ ip4_addr_subnets_build_index (const GArray *addresses, gboolean consider_flags) * Returns: %TRUE if the address is secondary, %FALSE otherwise */ static gboolean -ip4_addr_subnets_is_secondary (const NMPlatformIP4Address *address, GHashTable *subnets, const GArray *addresses, GPtrArray **out_addr_list) -{ - GPtrArray *addr_list; - gpointer p; +ip4_addr_subnets_is_secondary (const NMPObject *address, + GHashTable *subnets, + const GPtrArray *addresses, + const GPtrArray **out_addr_list) +{ + const NMPlatformIP4Address *a; + const GPtrArray *addr_list; + gconstpointer p; guint32 net; + const NMPObject **o; + + a = NMP_OBJECT_CAST_IP4_ADDRESS (address); - net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen); + net = a->address & _nm_utils_ip4_prefix_to_netmask (a->plen); p = g_hash_table_lookup (subnets, GUINT_TO_POINTER (net)); nm_assert (p); - if (!_ptr_inside_ip4_addr_array (addresses, p)) { + if (!ip4_addr_subnets_is_plain_address (addresses, p)) { addr_list = p; + nm_assert (addr_list->len > 1); NM_SET_OUT (out_addr_list, addr_list); - if (addr_list->pdata[0] != address) + o = ip4_addr_subnets_addr_list_get (addr_list, 0); + nm_assert (o && *o); + if (*o != address) return TRUE; } else { - nm_assert ((gconstpointer) address == p); NM_SET_OUT (out_addr_list, NULL); + return address != *((gconstpointer *) p); } return FALSE; } @@ -2936,11 +3275,14 @@ ip4_addr_subnets_is_secondary (const NMPlatformIP4Address *address, GHashTable * * nm_platform_ip4_address_sync: * @self: platform instance * @ifindex: Interface index - * @known_addresses: List of addresses - * @out_added_addresses: (out): (allow-none): if not %NULL, return a #GPtrArray - * with the addresses added. The pointers point into @known_addresses. - * It possibly does not contain all addresses from @known_address because - * some addresses might be expired. + * @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. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -2949,102 +3291,168 @@ ip4_addr_subnets_is_secondary (const NMPlatformIP4Address *address, GHashTable * * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses) +nm_platform_ip4_address_sync (NMPlatform *self, + int ifindex, + GPtrArray *known_addresses) { - GArray *addresses; - NMPlatformIP4Address *address; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; const NMPlatformIP4Address *known_address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - GHashTable *plat_subnets; - GHashTable *known_subnets; - GPtrArray *ptr; - int i, j; + GHashTable *plat_subnets = NULL; + GHashTable *known_subnets = NULL; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; + guint i, j, len; + NMPLookup lookup; + guint32 lifetime, preferred; + guint32 ifa_flags; _CHECK_SELF (self, klass, FALSE); - addresses = nm_platform_ip4_address_get_all (self, ifindex); - plat_subnets = ip4_addr_subnets_build_index (addresses, TRUE); - known_subnets = ip4_addr_subnets_build_index (known_addresses, 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, &lifetime, &preferred)) + 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 (!nm_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; + } + + plat_addresses = nm_platform_lookup_clone (self, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex), + NULL, NULL); + if (plat_addresses) + plat_subnets = ip4_addr_subnets_build_index (plat_addresses, TRUE, TRUE); /* Delete unknown addresses */ - for (i = 0; i < addresses->len; i++) { - address = &g_array_index (addresses, NMPlatformIP4Address, i); + len = plat_addresses ? plat_addresses->len : 0; + for (i = 0; i < len; i++) { + const NMPObject *plat_obj; + const NMPlatformIP4Address *plat_address; + const GPtrArray *addr_list; - if (!address->ifindex) { + plat_obj = plat_addresses->pdata[i]; + if (!plat_obj) { /* Already deleted */ continue; } - known_address = array_contains_ip4_address (known_addresses, address, now); - if (known_address) { - gboolean secondary; + plat_address = NMP_OBJECT_CAST_IP4_ADDRESS (plat_obj); + + if (known_addresses) { + const NMPObject *o; + + o = g_hash_table_lookup (known_addresses_idx, plat_obj); + if (o) { + gboolean secondary; + + if (!known_subnets) + known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE, FALSE); - secondary = ip4_addr_subnets_is_secondary (known_address, known_subnets, known_addresses, NULL); - /* Ignore the matching address if it has a different primary/slave - * role. */ - if (secondary != NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) - known_address = NULL; + secondary = ip4_addr_subnets_is_secondary (o, known_subnets, known_addresses, NULL); + if (secondary == NM_FLAGS_HAS (plat_address->n_ifa_flags, IFA_F_SECONDARY)) { + /* if we have an existing known-address, with matching secondary role, + * do not delete the platform-address. */ + continue; + } + } } - if (!known_address) { - nm_platform_ip4_address_delete (self, ifindex, - address->address, - address->plen, - address->peer_address); - if ( !ip4_addr_subnets_is_secondary (address, plat_subnets, addresses, &ptr) - && ptr) { - /* If we just deleted a primary addresses and there were - * secondary ones the kernel can do two things, depending on - * version and sysctl setting: delete also secondary addresses - * or promote a secondary to primary. Ensure that secondary - * addresses are deleted, so that we can start with a clean - * slate and add addresses in the right order. */ - for (j = 1; j < ptr->len; j++) { - address = ptr->pdata[j]; + nm_platform_ip4_address_delete (self, ifindex, + plat_address->address, + plat_address->plen, + plat_address->peer_address); + + if ( !ip4_addr_subnets_is_secondary (plat_obj, plat_subnets, plat_addresses, &addr_list) + && addr_list) { + /* If we just deleted a primary addresses and there were + * secondary ones the kernel can do two things, depending on + * version and sysctl setting: delete also secondary addresses + * or promote a secondary to primary. Ensure that secondary + * addresses are deleted, so that we can start with a clean + * slate and add addresses in the right order. */ + for (j = 1; j < addr_list->len; j++) { + const NMPObject **o; + + o = ip4_addr_subnets_addr_list_get (addr_list, j); + nm_assert (o); + + if (*o) { + const NMPlatformIP4Address *a; + + a = NMP_OBJECT_CAST_IP4_ADDRESS (*o); nm_platform_ip4_address_delete (self, ifindex, - address->address, - address->plen, - address->peer_address); - address->ifindex = 0; + a->address, + a->plen, + a->peer_address); + nmp_object_unref (*o); + *o = NULL; } } } } - ip4_addr_subnets_destroy_index (plat_subnets, addresses); - g_array_free (addresses, TRUE); - - if (out_added_addresses) - *out_added_addresses = NULL; + ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses); + ip4_addr_subnets_destroy_index (known_subnets, known_addresses); if (!known_addresses) return TRUE; + ifa_flags = nm_platform_check_kernel_support (self, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS) + ? IFA_F_NOPREFIXROUTE + : 0; + /* Add missing addresses */ for (i = 0; i < known_addresses->len; i++) { - guint32 lifetime, preferred; + const NMPObject *o; - known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i); + o = known_addresses->pdata[i]; + if (!o) + continue; + + 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)) - continue; + goto delete_and_next2; if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, lifetime, preferred, - 0, known_address->label)) { - ip4_addr_subnets_destroy_index (known_subnets, known_addresses); - return FALSE; - } - - if (out_added_addresses) { - if (!*out_added_addresses) - *out_added_addresses = g_ptr_array_new (); - g_ptr_array_add (*out_added_addresses, (gpointer) known_address); - } + ifa_flags, + known_address->label)) + goto delete_and_next2; + + continue; +delete_and_next2: + nmp_object_unref (o); + known_addresses->pdata[i] = NULL; } - ip4_addr_subnets_destroy_index (known_subnets, known_addresses); - return TRUE; } @@ -3052,7 +3460,8 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known * nm_platform_ip6_address_sync: * @self: platform instance * @ifindex: Interface index - * @known_addresses: List of addresses + * @known_addresses: List of IPv6 addresses, as NMPObject. The list + * is not modified. * @keep_link_local: Don't remove link-local address * * A convenience function to synchronize addresses for a specific interface @@ -3062,33 +3471,47 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known * Returns: %TRUE on success. */ gboolean -nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local) +nm_platform_ip6_address_sync (NMPlatform *self, + int ifindex, + const GPtrArray *known_addresses, + gboolean keep_link_local) { - GArray *addresses; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; NMPlatformIP6Address *address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - int i; + guint i; + NMPLookup lookup; + guint32 ifa_flags; /* Delete unknown addresses */ - addresses = nm_platform_ip6_address_get_all (self, ifindex); - for (i = 0; i < addresses->len; i++) { - address = &g_array_index (addresses, NMPlatformIP6Address, i); - - /* Leave link local address management to the kernel */ - if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) - continue; + plat_addresses = nm_platform_lookup_clone (self, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex), + NULL, NULL); + if (plat_addresses) { + for (i = 0; i < plat_addresses->len; i++) { + address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); + + /* Leave link local address management to the kernel */ + if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) + continue; - if (!array_contains_ip6_address (known_addresses, address, now)) - nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + if (!array_contains_ip6_address (known_addresses, address, now)) + nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + } } - g_array_free (addresses, TRUE); if (!known_addresses) return TRUE; + ifa_flags = nm_platform_check_kernel_support (self, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS) + ? IFA_F_NOPREFIXROUTE + : 0; + /* Add missing addresses */ for (i = 0; i < known_addresses->len; i++) { - const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i); + const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]); guint32 lifetime, preferred; if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_TEMPORARY)) { @@ -3102,7 +3525,8 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, - lifetime, preferred, known_address->n_ifa_flags)) + lifetime, preferred, + ifa_flags | known_address->n_ifa_flags)) return FALSE; } @@ -3110,125 +3534,797 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known } gboolean -nm_platform_address_flush (NMPlatform *self, int ifindex) +nm_platform_ip_address_flush (NMPlatform *self, + int addr_family, + int ifindex) { + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - return nm_platform_ip4_address_sync (self, ifindex, NULL, NULL) - && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); + + 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); + return success; } /*****************************************************************************/ -GArray * -nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) +static gboolean +_err_inval_due_to_ipv6_tentative_pref_src (NMPlatform *self, const NMPObject *obj) { - _CHECK_SELF (self, klass, NULL); + const NMPlatformIP6Route *r; + const NMPlatformIP6Address *a; - g_return_val_if_fail (ifindex >= 0, NULL); + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NMP_OBJECT_IS_VALID (obj)); - return klass->ip4_route_get_all (self, ifindex, flags); + /* trying to add an IPv6 route with pref-src fails, if the address is + * still tentative (rh#1452684). We need to hack around that. + * + * Detect it, by guessing whether that's the case. */ + + if (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_IP6_ROUTE) + return FALSE; + + r = NMP_OBJECT_CAST_IP6_ROUTE (obj); + + /* we only allow this workaround for routes added manually by the user. */ + if (r->rt_source != NM_IP_CONFIG_SOURCE_USER) + return FALSE; + + if (IN6_IS_ADDR_UNSPECIFIED (&r->pref_src)) + return FALSE; + + a = nm_platform_ip6_address_get (self, r->ifindex, r->pref_src); + if (!a) + return FALSE; + if ( !NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_TENTATIVE) + || NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_DADFAILED)) + return FALSE; + + return TRUE; } -GArray * -nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) -{ - _CHECK_SELF (self, klass, NULL); +GPtrArray * +nm_platform_ip_route_get_prune_list (NMPlatform *self, + int addr_family, + int ifindex, + NMIPRouteTableSyncMode route_table_sync) +{ + NMPLookup lookup; + GPtrArray *routes_prune; + const NMDedupMultiHeadEntry *head_entry; + CList *iter; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (route_table_sync, NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + NM_IP_ROUTE_TABLE_SYNC_MODE_FULL, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL)); + + nmp_lookup_init_addrroute (&lookup, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex); + head_entry = nm_platform_lookup (self, &lookup); + if (!head_entry) + return NULL; - g_return_val_if_fail (ifindex >= 0, NULL); + routes_prune = g_ptr_array_new_full (head_entry->len, + (GDestroyNotify) nm_dedup_multi_obj_unref); + + c_list_for_each (iter, &head_entry->lst_entries_head) { + const NMPObject *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; + + if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_FULL) { + if (nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE) == RT_TABLE_LOCAL) + continue; + } else if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN) { + if (!nm_platform_route_table_is_main (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced)) + continue; + } else + nm_assert (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); + + g_ptr_array_add (routes_prune, (gpointer) nmp_object_ref (obj)); + } - return klass->ip6_route_get_all (self, ifindex, flags); + if (routes_prune->len == 0) { + g_ptr_array_unref (routes_prune); + return NULL; + } + return routes_prune; } /** - * nm_platform_ip4_route_add: - * @self: - * @route: - * - * For kernel, a gateway can be either explicitly set or left - * at zero (0.0.0.0). In addition, there is the scope of the IPv4 - * route. - * When adding a route with - * $ ip route add default dev $IFNAME - * the resulting route will have gateway 0.0.0.0 and scope "link". - * Contrary to - * $ ip route add default via 0.0.0.0 dev $IFNAME - * which adds the route with scope "global". - * - * NetworkManager's Platform can currently only add on-link-routes with scope - * "link" (and gateway 0.0.0.0) or gateway-routes with scope "global" (and - * gateway not 0.0.0.0). + * nm_platform_ip_route_sync: + * @self: the #NMPlatform instance. + * @addr_family: AF_INET or AF_INET6. + * @ifindex: the @ifindex for which the routes are to be added. + * @routes: (allow-none): a list of routes to configure. Must contain + * NMPObject instances of routes, according to @addr_family. + * @routes_prune: (allow-none): the list of routes to delete. + * If platform has such a route configured, it will be deleted + * at the end of the operation. Note that if @routes contains + * the same route, then it will not be deleted. @routes overrules + * @routes_prune list. + * @out_temporary_not_available: (allow-none): (out): routes that could + * currently not be synced. The caller shall keep them and try later again. * - * It does not support adding globally scoped routes via 0.0.0.0. - * - * Returns: %TRUE in case of success. + * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route) -{ - _CHECK_SELF (self, klass, FALSE); +nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + GPtrArray *routes_prune, + GPtrArray **out_temporary_not_available) +{ + const NMPlatformVTableRoute *vt; + gs_unref_hashtable GHashTable *routes_idx = NULL; + const NMPObject *conf_o; + const NMDedupMultiEntry *plat_entry; + guint i; + int i_type; + gboolean success = TRUE; + char sbuf1[sizeof (_nm_utils_to_string_buffer)]; + char sbuf2[sizeof (_nm_utils_to_string_buffer)]; + char sbuf_err[60]; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (ifindex > 0); + + vt = addr_family == AF_INET + ? &nm_platform_vtable_route_v4 + : &nm_platform_vtable_route_v6; + + for (i_type = 0; routes && i_type < 2; i_type++) { + for (i = 0; i < routes->len; i++) { + NMPlatformError plerr; + + conf_o = routes->pdata[i]; + +#define VTABLE_IS_DEVICE_ROUTE(vt, o) (vt->is_ip4 \ + ? (NMP_OBJECT_CAST_IP4_ROUTE (o)->gateway == 0) \ + : IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (o)->gateway) ) + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vt, conf_o)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vt, conf_o))) { + /* we add routes in two runs over @i_type. + * + * First device routes, then gateway routes. */ + continue; + } + + if (!routes_idx) { + routes_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + if (!nm_g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { + _LOGD ("route-sync: skip adding duplicate route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + continue; + } + + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (plat_entry) { + const NMPObject *plat_o; + + plat_o = plat_entry->obj; - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 32, FALSE); + if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_o), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) + continue; - _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (route, NULL, 0)); + /* we need to replace the existing route with a (slightly) differnt + * one. Delete it first. */ + if (!nm_platform_ip_route_delete (self, plat_o)) { + /* ignore error. */ + } + } - return klass->ip4_route_add (self, route); + plerr = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, + conf_o); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + if (-((int) plerr) == EEXIST) { + /* Don't fail for EEXIST. It's not clear that the existing route + * is identical to the one that we were about to add. However, + * above we should have deleted conflicting (non-identical) routes. */ + if (_LOGD_ENABLED ()) { + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (!plat_entry) { + _LOGD ("route-sync: adding route %s failed with EEXIST, however we cannot find such a route", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + } else if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_entry->obj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) != 0) { + _LOGD ("route-sync: adding route %s failed due to existing (different!) route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + } + } + } else if ( -((int) plerr) == EINVAL + && out_temporary_not_available + && _err_inval_due_to_ipv6_tentative_pref_src (self, conf_o)) { + _LOGD ("route-sync: ignore failure to add IPv6 route with tentative IPv6 pref-src: %s: %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + if (!*out_temporary_not_available) + *out_temporary_not_available = g_ptr_array_new_full (0, (GDestroyNotify) nmp_object_unref); + g_ptr_array_add (*out_temporary_not_available, (gpointer) nmp_object_ref (conf_o)); + } else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { + _LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + } else { + const char *reason = ""; + + if ( -((int) plerr) == ENETUNREACH + && ( vt->is_ip4 + ? !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway + : !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway))) + reason = "; is the gateway directly reachable?"; + + _LOGW ("route-sync: failure to add IPv%c route: %s: %s%s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)), + reason); + success = FALSE; + } + } + } + } + + if (routes_prune) { + for (i = 0; i < routes_prune->len; i++) { + const NMPObject *prune_o; + + prune_o = routes_prune->pdata[i]; + + nm_assert ( (addr_family == AF_INET && NMP_OBJECT_GET_TYPE (prune_o) == NMP_OBJECT_TYPE_IP4_ROUTE) + || (addr_family == AF_INET6 && NMP_OBJECT_GET_TYPE (prune_o) == NMP_OBJECT_TYPE_IP6_ROUTE)); + + if ( routes_idx + && g_hash_table_lookup (routes_idx, prune_o)) + continue; + + if (!nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + prune_o)) + continue; + + if (!nm_platform_ip_route_delete (self, prune_o)) { + /* ignore error... */ + } + } + } + + return success; } gboolean -nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route) +nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex) { + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 128, FALSE); + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); - _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (route, NULL, 0)); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + gs_unref_ptrarray GPtrArray *routes_prune = NULL; - return klass->ip6_route_add (self, route); + routes_prune = nm_platform_ip_route_get_prune_list (self, + AF_INET, + ifindex, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); + success &= nm_platform_ip_route_sync (self, AF_INET, ifindex, NULL, routes_prune, NULL); + } + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + gs_unref_ptrarray GPtrArray *routes_prune = NULL; + + routes_prune = nm_platform_ip_route_get_prune_list (self, + AF_INET6, + ifindex, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); + success &= nm_platform_ip_route_sync (self, AF_INET6, ifindex, NULL, routes_prune, NULL); + } + return success; } -gboolean -nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric) +/*****************************************************************************/ + +static guint8 +_ip_route_scope_inv_get_normalized (const NMPlatformIP4Route *route) +{ + /* in kernel, you cannot set scope to RT_SCOPE_NOWHERE (255). + * That means, in NM, we treat RT_SCOPE_NOWHERE as unset, and detect + * it based on the presence of the gateway. In other words, when adding + * a route with scope RT_SCOPE_NOWHERE (in NetworkManager) to kernel, + * the resulting scope will be either "link" or "universe" (depending + * on the gateway). + * + * Note that internally, we track @scope_inv is the inverse of scope, + * so that the default equals zero (~(RT_SCOPE_NOWHERE)). + **/ + if (route->scope_inv == 0) { + return nm_platform_route_scope_inv (!route->gateway + ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + } + return route->scope_inv; +} + +static guint8 +_route_pref_normalize (guint8 pref) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + /* for kernel (and ICMPv6) pref can only have one of 3 values. Normalize. */ + return NM_IN_SET (pref, NM_ICMPV6_ROUTER_PREF_LOW, + NM_ICMPV6_ROUTER_PREF_HIGH) + ? pref + : NM_ICMPV6_ROUTER_PREF_MEDIUM; +} + +/** + * nm_platform_ip_route_normalize: + * @addr_family: AF_INET or AF_INET6 + * @route: an NMPlatformIP4Route or NMPlatformIP6Route instance, depending on @addr_family. + * + * Adding a route to kernel via nm_platform_ip_route_add() will normalize/coerce some + * properties of the route. This function modifies (normalizes) the route like it + * would be done by adding the route in kernel. + * + * Note that this function is related to NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY + * in that if two routes compare semantically equal, after normalizing they also shall + * compare equal with NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL. + */ +void +nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route) +{ + NMPlatformIP4Route *r4; + NMPlatformIP6Route *r6; + + switch (addr_family) { + case AF_INET: + r4 = (NMPlatformIP4Route *) route; + r4->table_coerced = nm_platform_route_table_coerce (nm_platform_route_table_uncoerce (r4->table_coerced, TRUE)); + r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen); + r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source); + r4->scope_inv = _ip_route_scope_inv_get_normalized (r4); + break; + case AF_INET6: + r6 = (NMPlatformIP6Route *) route; + r6->table_coerced = nm_platform_route_table_coerce (nm_platform_route_table_uncoerce (r6->table_coerced, TRUE)); + nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen); + r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source), + r6->metric = nm_utils_ip6_route_metric_normalize (r6->metric); + nm_utils_ip6_address_clear_host_address (&r6->src, &r6->src, r6->src_plen); + break; + default: + nm_assert_not_reached (); + break; + } +} + +static NMPlatformError +_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + int addr_family, + gconstpointer route) +{ + char sbuf[sizeof (_nm_utils_to_string_buffer)]; _CHECK_SELF (self, klass, FALSE); - _LOGD ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", - nm_utils_inet4_ntop (network, NULL), plen, metric, ifindex, - _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); - return klass->ip4_route_delete (self, ifindex, network, plen, metric); + nm_assert (route); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + _LOGD ("route: %-10s IPv%c route: %s", + _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), + nm_utils_addr_family_to_char (addr_family), + addr_family == AF_INET + ? nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)) + : nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf))); + + return klass->ip_route_add (self, flags, addr_family, route); +} + +NMPlatformError +nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route) +{ + int addr_family; + + switch (NMP_OBJECT_GET_TYPE (route)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + addr_family = AF_INET; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + addr_family = AF_INET6; + break; + default: + g_return_val_if_reached (FALSE); + } + + return _ip_route_add (self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE (route)); +} + +NMPlatformError +nm_platform_ip4_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route) +{ + return _ip_route_add (self, flags, AF_INET, route); +} + +NMPlatformError +nm_platform_ip6_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP6Route *route) +{ + return _ip_route_add (self, flags, AF_INET6, route); } gboolean -nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +nm_platform_ip_route_delete (NMPlatform *self, + const NMPObject *obj) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + _CHECK_SELF (self, klass, FALSE); + + if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)) + g_return_val_if_reached (FALSE); + + _LOGD ("route: delete IPv%c route %s", + NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE ? '4' : '6', + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + return klass->ip_route_delete (self, obj); +} + +/*****************************************************************************/ + +NMPlatformError +nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address /* in_addr_t or struct in6_addr */, + int oif_ifindex, + NMPObject **out_route) +{ + nm_auto_nmpobj NMPObject *route = NULL; + NMPlatformError result; + char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf_err[200]; + char buf_oif[64]; _CHECK_SELF (self, klass, FALSE); - _LOGD ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", - nm_utils_inet6_ntop (&network, NULL), plen, metric, ifindex, - _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); - return klass->ip6_route_delete (self, ifindex, network, plen, metric); + g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, + AF_INET6), NM_PLATFORM_ERROR_BUG); + + _LOGT ("route: get IPv%c route for: %s%s", + nm_utils_addr_family_to_char (addr_family), + inet_ntop (addr_family, address, buf, sizeof (buf)), + oif_ifindex > 0 ? nm_sprintf_buf (buf_oif, " oif %d", oif_ifindex) : ""); + + if (!klass->ip_route_get) + result = NM_PLATFORM_ERROR_OPNOTSUPP; + else { + result = klass->ip_route_get (self, + addr_family, + address, + oif_ifindex, + &route); + } + + if (result != NM_PLATFORM_ERROR_SUCCESS) { + nm_assert (!route); + _LOGW ("route: get IPv%c route for: %s failed with %s", + nm_utils_addr_family_to_char (addr_family), + inet_ntop (addr_family, address, buf, sizeof (buf)), + nm_platform_error_to_string (result, buf_err, sizeof (buf_err))); + } else { + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (route), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (route)); + nm_assert (route->parent._ref_count == 1); + _LOGD ("route: get IPv%c route for: %s succeeded: %s", + nm_utils_addr_family_to_char (addr_family), + inet_ntop (addr_family, address, buf, sizeof (buf)), + nmp_object_to_string (route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + NM_SET_OUT (out_route, g_steal_pointer (&route)); + } + return result; } -const NMPlatformIP4Route * -nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric) +/*****************************************************************************/ + +#define IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS ((int) 1500) +#define IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S ((int) (((IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS + 999) * 3) / 1000)) + +static gint64 +_ip4_dev_route_blacklist_timeout_ms_get (gint64 timeout_ms) { - _CHECK_SELF (self, klass, FALSE); + return timeout_ms >> 1; +} - return klass->ip4_route_get (self ,ifindex, network, plen, metric); +static gint64 +_ip4_dev_route_blacklist_timeout_ms_marked (gint64 timeout_ms) +{ + return !!(timeout_ms & ((gint64) 1)); } -const NMPlatformIP6Route * -nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +static gboolean +_ip4_dev_route_blacklist_check_cb (gpointer user_data) { - _CHECK_SELF (self, klass, FALSE); + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + priv->ip4_dev_route_blacklist_check_id = 0; + +again: + if (!priv->ip4_dev_route_blacklist_hash) + goto out; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (!_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) + continue; + + /* unmark because we checked it. */ + *p_timeout_ms = *p_timeout_ms & ~((gint64) 1); + + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) + continue; + + if (!nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + p_obj)) + continue; + + _LOGT ("ip4-dev-route: delete %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nm_platform_ip_route_delete (self, p_obj); + goto again; + } + +out: + return G_SOURCE_REMOVE; +} + +static void +_ip4_dev_route_blacklist_check_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if (!priv->ip4_dev_route_blacklist_check_id) { + priv->ip4_dev_route_blacklist_check_id = g_idle_add_full (G_PRIORITY_HIGH, + _ip4_dev_route_blacklist_check_cb, + self, + NULL); + } +} + +static void +_ip4_dev_route_blacklist_notify_route (NMPlatform *self, + const NMPObject *obj) +{ + NMPlatformPrivate *priv; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + if (!g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + obj, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) + return; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + /* already expired. Wait for gc. */ + return; + } + + if (_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) { + nm_assert (priv->ip4_dev_route_blacklist_check_id); + return; + } + + /* We cannot delete it right away because we are in the process of receiving netlink messages. + * It may be possible to do so, but complicated and error prone. + * + * Instead, we mark the entry and schedule an idle action (with high priority). */ + *p_timeout_ms = (*p_timeout_ms) | ((gint64) 1); + _ip4_dev_route_blacklist_check_schedule (self); +} + +static gboolean +_ip4_dev_route_blacklist_gc_timeout_handle (gpointer user_data) +{ + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + _LOGT ("ip4-dev-route: cleanup %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_iter_remove (&iter); + } + } + + _ip4_dev_route_blacklist_schedule (self); + return G_SOURCE_CONTINUE; +} + +static void +_ip4_dev_route_blacklist_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if ( !priv->ip4_dev_route_blacklist_hash + || g_hash_table_size (priv->ip4_dev_route_blacklist_hash) == 0) { + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + } else { + if (!priv->ip4_dev_route_blacklist_gc_timeout_id) { + /* this timeout is only to garbage collect the expired entries from priv->ip4_dev_route_blacklist_hash. + * It can run infrequently, and it doesn't hurt if expired entries linger around a bit + * longer then necessary. */ + priv->ip4_dev_route_blacklist_gc_timeout_id = g_timeout_add_seconds (IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S, + _ip4_dev_route_blacklist_gc_timeout_handle, + self); + } + } +} + +/** + * nm_platform_ip4_dev_route_blacklist_set: + * @self: + * @ifindex: + * @ip4_dev_route_blacklist: + * + * When adding an IP address, kernel automatically adds a device route. + * This can be suppressed via the IFA_F_NOPREFIXROUTE address flag. For proper + * IPv6 support, we require kernel support for IFA_F_NOPREFIXROUTE and always + * add the device route manually. + * + * For IPv4, this flag is rather new and we don't rely on it yet. We want to use + * it (but currently still don't). So, for IPv4, kernel possibly adds a device + * route, however it has a wrong metric of zero. We add our own device route (with + * proper metric), but need to delete the route that kernel adds. + * + * The problem is, that kernel does not immidiately add the route, when adding + * the address. It only shows up some time later. So, we register here a list + * of blacklisted routes, and when they show up within a time out, we assume it's + * the kernel generated one, and we delete it. + * + * Eventually, we want to get rid of this and use IFA_F_NOPREFIXROUTE for IPv4 + * routes as well. + */ +void +nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist) +{ + NMPlatformPrivate *priv; + GHashTableIter iter; + const NMPObject *p_obj; + guint i; + gint64 timeout_ms; + gint64 timeout_ms_val; + gint64 *p_timeout_ms; + gboolean needs_check = FALSE; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (ifindex > 0); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + /* first, expire all for current ifindex... */ + if (priv->ip4_dev_route_blacklist_hash) { + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (NMP_OBJECT_CAST_IP4_ROUTE (p_obj)->ifindex == ifindex) { + /* we could g_hash_table_iter_remove(&iter) the current entry. + * Instead, just expire it and let _ip4_dev_route_blacklist_gc_timeout_handle() + * handle it. + * + * The assumption is, that ip4_dev_route_blacklist contains the very same entry + * again, with a new timeout. So, we can un-expire it below. */ + *p_timeout_ms = 0; + } + } + } + + if ( ip4_dev_route_blacklist + && ip4_dev_route_blacklist->len > 0) { + + if (!priv->ip4_dev_route_blacklist_hash) { + priv->ip4_dev_route_blacklist_hash = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + nm_g_slice_free_fcn_gint64); + } + + timeout_ms = nm_utils_get_monotonic_timestamp_ms () + IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS; + timeout_ms_val = (timeout_ms << 1) | ((gint64) 1); + for (i = 0; i < ip4_dev_route_blacklist->len; i++) { + const NMPObject *o; + + needs_check = TRUE; + o = ip4_dev_route_blacklist->pdata[i]; + if (g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + o, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) { + if (nmp_object_equal (p_obj, o)) { + /* un-expire and reuse the entry. */ + _LOGT ("ip4-dev-route: register %s (update)", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + *p_timeout_ms = timeout_ms_val; + continue; + } + } + + _LOGT ("ip4-dev-route: register %s", + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + p_timeout_ms = g_slice_new (gint64); + *p_timeout_ms = timeout_ms_val; + g_hash_table_replace (priv->ip4_dev_route_blacklist_hash, + (gpointer) nmp_object_ref (o), + p_timeout_ms); + } + } + + _ip4_dev_route_blacklist_schedule (self); - return klass->ip6_route_get (self, ifindex, network, plen, metric); + if (needs_check) + _ip4_dev_route_blacklist_check_schedule (self); } /*****************************************************************************/ @@ -3898,6 +4994,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_table[30]; char str_scope[30], s_source[50]; char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; @@ -3909,20 +5006,9 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - if (route->tos) - nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos); - if (route->window) - nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window); - if (route->cwnd) - nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd); - if (route->initcwnd) - nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd); - if (route->initrwnd) - nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd); - if (route->mtu) - nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu); g_snprintf (buf, len, + "%s" /* table */ "%s/%d" " via %s" "%s" @@ -3939,6 +5025,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi "%s" /* initrwnd */ "%s" /* mtu */ "", + route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_uncoerce (route->table_coerced, FALSE)) : "", s_network, route->plen, s_gateway, @@ -3951,12 +5038,12 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi route->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "", route->pref_src ? " pref-src " : "", route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "", - route->tos ? str_tos : "", - route->window ? str_window : "", - route->cwnd ? str_cwnd : "", - route->initcwnd ? str_initcwnd : "", - route->initrwnd ? str_initrwnd : "", - route->mtu ? str_mtu : ""); + route->tos ? nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos) : "", + route->window || route->lock_window ? nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window) : "", + route->cwnd || route->lock_cwnd ? nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd) : "", + route->initcwnd || route->lock_initcwnd ? nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd) : "", + route->initrwnd || route->lock_initrwnd ? nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd) : "", + route->mtu || route->lock_mtu ? nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu) : ""); return buf; } @@ -3976,16 +5063,18 @@ const char * nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len) { char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN]; - char s_src[INET6_ADDRSTRLEN]; + char s_src_all[INET6_ADDRSTRLEN + 40], s_src[INET6_ADDRSTRLEN]; + char str_table[30]; + char str_pref[40]; + char str_pref2[30]; char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50]; - char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; + char str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) return buf; inet_ntop (AF_INET6, &route->network, s_network, sizeof (s_network)); inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof (s_gateway)); - inet_ntop (AF_INET6, &route->src, s_src, sizeof (s_src)); if (IN6_IS_ADDR_UNSPECIFIED (&route->pref_src)) s_pref_src[0] = 0; @@ -3994,36 +5083,25 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - if (route->tos) - nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos); - if (route->window) - nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window); - if (route->cwnd) - nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd); - if (route->initcwnd) - nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd); - if (route->initrwnd) - nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd); - if (route->mtu) - nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu); - g_snprintf (buf, len, + "%s" /* table */ "%s/%d" " via %s" "%s" " metric %"G_GUINT32_FORMAT " mss %"G_GUINT32_FORMAT " rt-src %s" /* protocol */ - " src %s/%u" /* source */ + "%s" /* source */ "%s" /* cloned */ "%s%s" /* pref-src */ - "%s" /* tos */ "%s" /* window */ "%s" /* cwnd */ "%s" /* initcwnd */ "%s" /* initrwnd */ "%s" /* mtu */ + "%s" /* pref */ "", + route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_uncoerce (route->table_coerced, FALSE)) : "", s_network, route->plen, s_gateway, @@ -4031,358 +5109,724 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi route->metric, route->mss, nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)), - s_src, route->src_plen, + route->src_plen || !IN6_IS_ADDR_UNSPECIFIED (&route->src) + ? nm_sprintf_buf (s_src_all, " src %s/%u", nm_utils_inet6_ntop (&route->src, s_src), (unsigned) route->src_plen) + : "", route->rt_cloned ? " cloned" : "", s_pref_src[0] ? " pref-src " : "", s_pref_src[0] ? s_pref_src : "", - route->tos ? str_tos : "", - route->window ? str_window : "", - route->cwnd ? str_cwnd : "", - route->initcwnd ? str_initcwnd : "", - route->initrwnd ? str_initrwnd : "", - route->mtu ? str_mtu : ""); + route->window || route->lock_window ? nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window) : "", + route->cwnd || route->lock_cwnd ? nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd) : "", + route->initcwnd || route->lock_initcwnd ? nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd) : "", + route->initrwnd || route->lock_initrwnd ? nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd) : "", + route->mtu || route->lock_mtu ? nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu) : "", + route->rt_pref ? nm_sprintf_buf (str_pref, " pref %s", nm_icmpv6_router_pref_to_string (route->rt_pref, str_pref2, sizeof (str_pref2))) : ""); return buf; } -#define _CMP_SELF(a, b) \ - G_STMT_START { \ - if ((a) == (b)) \ - return 0; \ - if (!(a)) \ - return -1; \ - if (!(b)) \ - return 1; \ - } G_STMT_END - -#define _CMP_DIRECT(a, b) \ - G_STMT_START { \ - if ((a) != (b)) \ - return ((a) < (b)) ? -1 : 1; \ - } G_STMT_END - -#define _CMP_DIRECT_MEMCMP(a, b, size) \ - G_STMT_START { \ - int c = memcmp ((a), (b), (size)); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD(a, b, field) \ - G_STMT_START { \ - if (((a)->field) != ((b)->field)) \ - return (((a)->field) < ((b)->field)) ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_BOOL(a, b, field) \ - G_STMT_START { \ - if ((!((a)->field)) != (!((b)->field))) \ - return ((!((a)->field)) < (!((b)->field))) ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_STR(a, b, field) \ - G_STMT_START { \ - int c = strcmp ((a)->field, (b)->field); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_STR_INTERNED(a, b, field) \ - G_STMT_START { \ - if (((a)->field) != ((b)->field)) { \ - /* just to be sure, also do a strcmp() if the pointers don't match */ \ - int c = g_strcmp0 ((a)->field, (b)->field); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } \ - } G_STMT_END - -#define _CMP_FIELD_STR0(a, b, field) \ - G_STMT_START { \ - int c = g_strcmp0 ((a)->field, (b)->field); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ - G_STMT_START { \ - int c = memcmp (&((a)->field), &((b)->field), \ - MIN (len, sizeof ((a)->field))); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_MEMCMP(a, b, field) \ - G_STMT_START { \ - int c = memcmp (&((a)->field), &((b)->field), \ - sizeof ((a)->field)); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END +void +nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->ifindex, + obj->master, + obj->parent, + obj->n_ifi_flags, + obj->mtu, + obj->type, + obj->arptype, + obj->inet6_addr_gen_mode_inv, + obj->inet6_token, + obj->rx_packets, + obj->rx_bytes, + obj->tx_packets, + obj->tx_bytes, + NM_HASH_COMBINE_BOOLS (guint8, + obj->connected, + obj->initialized)); + nm_hash_update_strarr (h, obj->name); + nm_hash_update_str0 (h, obj->kind); + nm_hash_update_str0 (h, obj->driver); + /* nm_hash_update_mem() also hashes the length obj->addr.len */ + nm_hash_update_mem (h, obj->addr.data, obj->addr.len); +} int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - _CMP_FIELD (a, b, type); - _CMP_FIELD_STR (a, b, name); - _CMP_FIELD (a, b, master); - _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, n_ifi_flags); - _CMP_FIELD (a, b, connected); - _CMP_FIELD (a, b, mtu); - _CMP_FIELD_BOOL (a, b, initialized); - _CMP_FIELD (a, b, arptype); - _CMP_FIELD (a, b, addr.len); - _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); - _CMP_FIELD_STR_INTERNED (a, b, kind); - _CMP_FIELD_STR_INTERNED (a, b, driver); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD (a, b, type); + NM_CMP_FIELD_STR (a, b, name); + NM_CMP_FIELD (a, b, master); + NM_CMP_FIELD (a, b, parent); + NM_CMP_FIELD (a, b, n_ifi_flags); + NM_CMP_FIELD_UNSAFE (a, b, connected); + NM_CMP_FIELD (a, b, mtu); + NM_CMP_FIELD_BOOL (a, b, initialized); + NM_CMP_FIELD (a, b, arptype); + NM_CMP_FIELD (a, b, addr.len); + NM_CMP_FIELD (a, b, inet6_addr_gen_mode_inv); + NM_CMP_FIELD_STR_INTERNED (a, b, kind); + NM_CMP_FIELD_STR_INTERNED (a, b, driver); if (a->addr.len) - _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); - _CMP_FIELD_MEMCMP (a, b, inet6_token); - _CMP_FIELD (a, b, rx_packets); - _CMP_FIELD (a, b, rx_bytes); - _CMP_FIELD (a, b, tx_packets); - _CMP_FIELD (a, b, tx_bytes); + NM_CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); + NM_CMP_FIELD_MEMCMP (a, b, inet6_token); + NM_CMP_FIELD (a, b, rx_packets); + NM_CMP_FIELD (a, b, rx_bytes); + NM_CMP_FIELD (a, b, tx_packets); + NM_CMP_FIELD (a, b, tx_bytes); return 0; } +void +nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->input_flags, + obj->output_flags, + obj->input_key, + obj->output_key, + obj->ttl, + obj->tos, + (bool) obj->path_mtu_discovery); +} + int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, input_flags); - _CMP_FIELD (a, b, output_flags); - _CMP_FIELD (a, b, input_key); - _CMP_FIELD (a, b, output_key); - _CMP_FIELD (a, b, local); - _CMP_FIELD (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tos); - _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, input_flags); + NM_CMP_FIELD (a, b, output_flags); + NM_CMP_FIELD (a, b, input_key); + NM_CMP_FIELD (a, b, output_key); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery); return 0; } +void +nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h) +{ + nm_hash_update_val (h, obj->p_key); + nm_hash_update_str0 (h, obj->mode); +} + int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, p_key); - _CMP_FIELD_STR_INTERNED (a, b, mode); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, p_key); + NM_CMP_FIELD_STR_INTERNED (a, b, mode); return 0; } +void +nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ttl, + obj->tclass, + obj->encap_limit, + obj->proto, + obj->flow_label); +} + int nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD_MEMCMP (a, b, local); - _CMP_FIELD_MEMCMP (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tclass); - _CMP_FIELD (a, b, encap_limit); - _CMP_FIELD (a, b, flow_label); - _CMP_FIELD (a, b, proto); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD_MEMCMP (a, b, local); + NM_CMP_FIELD_MEMCMP (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tclass); + NM_CMP_FIELD (a, b, encap_limit); + NM_CMP_FIELD (a, b, flow_label); + NM_CMP_FIELD (a, b, proto); return 0; } +void +nm_platform_lnk_ipip_hash_update (const NMPlatformLnkIpIp *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ttl, + obj->tos, + (bool) obj->path_mtu_discovery); +} + int nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, local); - _CMP_FIELD (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tos); - _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery); return 0; } +void +nm_platform_lnk_macsec_hash_update (const NMPlatformLnkMacsec *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->parent_ifindex, + obj->sci, + obj->cipher_suite, + obj->window, + obj->icv_length, + obj->encoding_sa, + obj->validation, + NM_HASH_COMBINE_BOOLS (guint8, + obj->encrypt, + obj->protect, + obj->include_sci, + obj->es, + obj->scb, + obj->replay_protect)); +} + int nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, sci); - _CMP_FIELD (a, b, icv_length); - _CMP_FIELD (a, b, cipher_suite); - _CMP_FIELD (a, b, window); - _CMP_FIELD (a, b, encoding_sa); - _CMP_FIELD (a, b, validation); - _CMP_FIELD (a, b, encrypt); - _CMP_FIELD (a, b, protect); - _CMP_FIELD (a, b, include_sci); - _CMP_FIELD (a, b, es); - _CMP_FIELD (a, b, scb); - _CMP_FIELD (a, b, replay_protect); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, sci); + NM_CMP_FIELD (a, b, icv_length); + NM_CMP_FIELD (a, b, cipher_suite); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, encoding_sa); + NM_CMP_FIELD (a, b, validation); + NM_CMP_FIELD_UNSAFE (a, b, encrypt); + NM_CMP_FIELD_UNSAFE (a, b, protect); + NM_CMP_FIELD_UNSAFE (a, b, include_sci); + NM_CMP_FIELD_UNSAFE (a, b, es); + NM_CMP_FIELD_UNSAFE (a, b, scb); + NM_CMP_FIELD_UNSAFE (a, b, replay_protect); return 0; } +void +nm_platform_lnk_macvlan_hash_update (const NMPlatformLnkMacvlan *obj, NMHashState *h ) +{ + nm_hash_update_vals (h, + obj->mode, + NM_HASH_COMBINE_BOOLS (guint8, + obj->no_promisc, + obj->tap)); +} + int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, mode); - _CMP_FIELD_BOOL (a, b, no_promisc); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, mode); + NM_CMP_FIELD_UNSAFE (a, b, no_promisc); + NM_CMP_FIELD_UNSAFE (a, b, tap); return 0; } +void +nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->flags, + obj->ttl, + obj->tos, + obj->proto, + (bool) obj->path_mtu_discovery); +} + int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, local); - _CMP_FIELD (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tos); - _CMP_FIELD_BOOL (a, b, path_mtu_discovery); - _CMP_FIELD (a, b, flags); - _CMP_FIELD (a, b, proto); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery); + NM_CMP_FIELD (a, b, flags); + NM_CMP_FIELD (a, b, proto); return 0; } +void +nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->id, + obj->flags); +} + int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, id); - _CMP_FIELD (a, b, flags); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, id); + NM_CMP_FIELD (a, b, flags); return 0; } +void +nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->group6, + obj->local6, + obj->group, + obj->local, + obj->parent_ifindex, + obj->id, + obj->ageing, + obj->limit, + obj->dst_port, + obj->src_port_min, + obj->src_port_max, + obj->tos, + obj->ttl, + NM_HASH_COMBINE_BOOLS (guint8, + obj->learning, + obj->proxy, + obj->rsc, + obj->l2miss, + obj->l3miss)); +} + int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, id); - _CMP_FIELD (a, b, group); - _CMP_FIELD (a, b, local); - _CMP_FIELD_MEMCMP (a, b, group6); - _CMP_FIELD_MEMCMP (a, b, local6); - _CMP_FIELD (a, b, tos); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD_BOOL (a, b, learning); - _CMP_FIELD (a, b, ageing); - _CMP_FIELD (a, b, limit); - _CMP_FIELD (a, b, dst_port); - _CMP_FIELD (a, b, src_port_min); - _CMP_FIELD (a, b, src_port_max); - _CMP_FIELD_BOOL (a, b, proxy); - _CMP_FIELD_BOOL (a, b, rsc); - _CMP_FIELD_BOOL (a, b, l2miss); - _CMP_FIELD_BOOL (a, b, l3miss); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, id); + NM_CMP_FIELD (a, b, group); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD_MEMCMP (a, b, group6); + NM_CMP_FIELD_MEMCMP (a, b, local6); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD_BOOL (a, b, learning); + NM_CMP_FIELD (a, b, ageing); + NM_CMP_FIELD (a, b, limit); + NM_CMP_FIELD (a, b, dst_port); + NM_CMP_FIELD (a, b, src_port_min); + NM_CMP_FIELD (a, b, src_port_max); + NM_CMP_FIELD_BOOL (a, b, proxy); + NM_CMP_FIELD_BOOL (a, b, rsc); + NM_CMP_FIELD_BOOL (a, b, l2miss); + NM_CMP_FIELD_BOOL (a, b, l3miss); return 0; } +void +nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address); + nm_hash_update_strarr (h, obj->label); +} + int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - _CMP_FIELD (a, b, address); - _CMP_FIELD (a, b, plen); - _CMP_FIELD (a, b, peer_address); - _CMP_FIELD (a, b, addr_source); - _CMP_FIELD (a, b, timestamp); - _CMP_FIELD (a, b, lifetime); - _CMP_FIELD (a, b, preferred); - _CMP_FIELD (a, b, n_ifa_flags); - _CMP_FIELD_STR (a, b, label); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD (a, b, address); + NM_CMP_FIELD (a, b, plen); + NM_CMP_FIELD (a, b, peer_address); + NM_CMP_FIELD (a, b, addr_source); + NM_CMP_FIELD (a, b, timestamp); + NM_CMP_FIELD (a, b, lifetime); + NM_CMP_FIELD (a, b, preferred); + NM_CMP_FIELD (a, b, n_ifa_flags); + NM_CMP_FIELD_STR (a, b, label); return 0; } +void +nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address); +} + int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) { const struct in6_addr *p_a, *p_b; - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - _CMP_FIELD_MEMCMP (a, b, address); - _CMP_FIELD (a, b, plen); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD_MEMCMP (a, b, address); + NM_CMP_FIELD (a, b, plen); p_a = nm_platform_ip6_address_get_peer (a); p_b = nm_platform_ip6_address_get_peer (b); - _CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a)); - _CMP_FIELD (a, b, addr_source); - _CMP_FIELD (a, b, timestamp); - _CMP_FIELD (a, b, lifetime); - _CMP_FIELD (a, b, preferred); - _CMP_FIELD (a, b, n_ifa_flags); + NM_CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a)); + NM_CMP_FIELD (a, b, addr_source); + NM_CMP_FIELD (a, b, timestamp); + NM_CMP_FIELD (a, b, lifetime); + NM_CMP_FIELD (a, b, preferred); + NM_CMP_FIELD (a, b, n_ifa_flags); return 0; } +void +nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h) +{ + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + nm_utils_ip4_address_clear_host_address (obj->network, obj->plen), + obj->plen, + obj->metric, + obj->tos); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + nm_utils_ip4_address_clear_host_address (obj->network, obj->plen), + obj->plen, + obj->metric, + obj->tos, + /* on top of WEAK_ID: */ + obj->ifindex, + nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source), + _ip_route_scope_inv_get_normalized (obj), + obj->gateway, + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + NM_HASH_COMBINE_BOOLS (guint8, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu)); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + obj->ifindex, + nm_utils_ip4_address_clear_host_address (obj->network, obj->plen), + obj->plen, + obj->metric, + obj->gateway, + nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source), + _ip_route_scope_inv_get_normalized (obj), + obj->tos, + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu)); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + nm_hash_update_vals (h, + obj->table_coerced, + obj->ifindex, + obj->network, + obj->plen, + obj->metric, + obj->gateway, + obj->rt_source, + obj->scope_inv, + obj->tos, + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu)); + break; + } +} + int -nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part) -{ - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - if (consider_host_part) - _CMP_FIELD (a, b, network); - else { - _CMP_DIRECT (nm_utils_ip4_address_clear_host_address (a->network, a->plen), - nm_utils_ip4_address_clear_host_address (b->network, b->plen)); +nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type) +{ + NM_CMP_SELF (a, b); + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); + NM_CMP_FIELD (a, b, plen); + NM_CMP_FIELD (a, b, metric); + NM_CMP_FIELD (a, b, tos); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + NM_CMP_DIRECT (_ip_route_scope_inv_get_normalized (a), + _ip_route_scope_inv_get_normalized (b)); + NM_CMP_FIELD (a, b, gateway); + NM_CMP_FIELD (a, b, mss); + NM_CMP_FIELD (a, b, pref_src); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, cwnd); + NM_CMP_FIELD (a, b, initcwnd); + NM_CMP_FIELD (a, b, initrwnd); + NM_CMP_FIELD (a, b, mtu); + NM_CMP_FIELD_UNSAFE (a, b, lock_window); + NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_mtu); + } + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + } else + NM_CMP_FIELD (a, b, table_coerced); + NM_CMP_FIELD (a, b, ifindex); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); + else + NM_CMP_FIELD (a, b, network); + NM_CMP_FIELD (a, b, plen); + NM_CMP_FIELD (a, b, metric); + NM_CMP_FIELD (a, b, gateway); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + NM_CMP_DIRECT (_ip_route_scope_inv_get_normalized (a), + _ip_route_scope_inv_get_normalized (b)); + } else { + NM_CMP_FIELD (a, b, rt_source); + NM_CMP_FIELD (a, b, scope_inv); + } + NM_CMP_FIELD (a, b, mss); + NM_CMP_FIELD (a, b, pref_src); + NM_CMP_FIELD_UNSAFE (a, b, rt_cloned); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_UNSAFE (a, b, lock_window); + NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_mtu); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, cwnd); + NM_CMP_FIELD (a, b, initcwnd); + NM_CMP_FIELD (a, b, initrwnd); + NM_CMP_FIELD (a, b, mtu); + break; } - _CMP_FIELD (a, b, plen); - _CMP_FIELD (a, b, metric); - _CMP_FIELD (a, b, gateway); - _CMP_FIELD (a, b, rt_source); - _CMP_FIELD (a, b, mss); - _CMP_FIELD (a, b, scope_inv); - _CMP_FIELD (a, b, pref_src); - _CMP_FIELD (a, b, rt_cloned); - _CMP_FIELD (a, b, tos); - _CMP_FIELD (a, b, lock_window); - _CMP_FIELD (a, b, lock_cwnd); - _CMP_FIELD (a, b, lock_initcwnd); - _CMP_FIELD (a, b, lock_initrwnd); - _CMP_FIELD (a, b, lock_mtu); - _CMP_FIELD (a, b, window); - _CMP_FIELD (a, b, cwnd); - _CMP_FIELD (a, b, initcwnd); - _CMP_FIELD (a, b, initrwnd); - _CMP_FIELD (a, b, mtu); return 0; } -int -nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part) -{ - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - if (consider_host_part) - _CMP_FIELD_MEMCMP (a, b, network); - else { - struct in6_addr n1, n2; +void +nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h) +{ + struct in6_addr a1, a2; + + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + *nm_utils_ip6_address_clear_host_address (&a1, &obj->network, obj->plen), + obj->plen, + nm_utils_ip6_route_metric_normalize (obj->metric), + *nm_utils_ip6_address_clear_host_address (&a2, &obj->src, obj->src_plen), + obj->src_plen); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + *nm_utils_ip6_address_clear_host_address (&a1, &obj->network, obj->plen), + obj->plen, + nm_utils_ip6_route_metric_normalize (obj->metric), + *nm_utils_ip6_address_clear_host_address (&a2, &obj->src, obj->src_plen), + obj->src_plen, + /* on top of WEAK_ID: */ + obj->ifindex, + obj->gateway); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + obj->ifindex, + *nm_utils_ip6_address_clear_host_address (&a1, &obj->network, obj->plen), + obj->plen, + nm_utils_ip6_route_metric_normalize (obj->metric), + obj->gateway, + obj->pref_src, + *nm_utils_ip6_address_clear_host_address (&a2, &obj->src, obj->src_plen), + obj->src_plen, + nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source), + obj->mss, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu), + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + _route_pref_normalize (obj->rt_pref)); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + nm_hash_update_vals (h, + obj->table_coerced, + obj->ifindex, + obj->network, + obj->plen, + obj->metric, + obj->gateway, + obj->pref_src, + obj->src, + obj->src_plen, + obj->rt_source, + obj->mss, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu), + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + obj->rt_pref); + break; + } +} - nm_utils_ip6_address_clear_host_address (&n1, &a->network, a->plen); - nm_utils_ip6_address_clear_host_address (&n2, &b->network, b->plen); - _CMP_DIRECT_MEMCMP (&n1, &n2, sizeof (struct in6_addr)); +int +nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type) +{ + NM_CMP_SELF (a, b); + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); + NM_CMP_FIELD (a, b, plen); + NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen)); + NM_CMP_FIELD (a, b, src_plen); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD_IN6ADDR (a, b, gateway); + } + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + } else + NM_CMP_FIELD (a, b, table_coerced); + NM_CMP_FIELD (a, b, ifindex); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); + else + NM_CMP_FIELD_IN6ADDR (a, b, network); + NM_CMP_FIELD (a, b, plen); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); + else + NM_CMP_FIELD (a, b, metric); + NM_CMP_FIELD_IN6ADDR (a, b, gateway); + NM_CMP_FIELD_IN6ADDR (a, b, pref_src); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen)); + NM_CMP_FIELD (a, b, src_plen); + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + } else { + NM_CMP_FIELD_IN6ADDR (a, b, src); + NM_CMP_FIELD (a, b, src_plen); + NM_CMP_FIELD (a, b, rt_source); + } + NM_CMP_FIELD (a, b, mss); + NM_CMP_FIELD_UNSAFE (a, b, rt_cloned); + NM_CMP_FIELD_UNSAFE (a, b, lock_window); + NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_mtu); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, cwnd); + NM_CMP_FIELD (a, b, initcwnd); + NM_CMP_FIELD (a, b, initrwnd); + NM_CMP_FIELD (a, b, mtu); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT (_route_pref_normalize (a->rt_pref), _route_pref_normalize (b->rt_pref)); + else + NM_CMP_FIELD (a, b, rt_pref); + break; } - _CMP_FIELD (a, b, plen); - _CMP_FIELD (a, b, metric); - _CMP_FIELD_MEMCMP (a, b, gateway); - _CMP_FIELD_MEMCMP (a, b, pref_src); - _CMP_FIELD_MEMCMP (a, b, src); - _CMP_FIELD (a, b, src_plen); - _CMP_FIELD (a, b, rt_source); - _CMP_FIELD (a, b, mss); - _CMP_FIELD (a, b, rt_cloned); - _CMP_FIELD (a, b, tos); - _CMP_FIELD (a, b, lock_window); - _CMP_FIELD (a, b, lock_cwnd); - _CMP_FIELD (a, b, lock_initcwnd); - _CMP_FIELD (a, b, lock_initrwnd); - _CMP_FIELD (a, b, lock_mtu); - _CMP_FIELD (a, b, window); - _CMP_FIELD (a, b, cwnd); - _CMP_FIELD (a, b, initcwnd); - _CMP_FIELD (a, b, initrwnd); - _CMP_FIELD (a, b, mtu); return 0; } @@ -4404,7 +5848,7 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor { gint64 ta = 0, tb = 0; - _CMP_SELF (a, b); + NM_CMP_SELF (a, b); if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0) ta = G_MAXINT64; @@ -4485,120 +5929,139 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform /*****************************************************************************/ -NMPNetns * -nm_platform_netns_get (NMPlatform *self) +void +nm_platform_cache_update_emit_signal (NMPlatform *self, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) { - _CHECK_SELF (self, klass, NULL); + gboolean visible_new; + gboolean visible_old; + const NMPObject *o; + const NMPClass *klass; - return self->_netns; -} + nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); -gboolean -nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns) -{ - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new); - if ( platform->_netns - && !nmp_netns_push (platform->_netns)) { - NM_SET_OUT (netns, NULL); - return FALSE; + nm_assert (NM_IN_SET (nm_platform_netns_get (self), + NULL, + nmp_netns_get_current ())); + + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self); + + switch (cache_op) { + case NMP_CACHE_OPS_ADDED: + if (!nmp_object_is_visible (obj_new)) + return; + o = obj_new; + break; + case NMP_CACHE_OPS_UPDATED: + visible_old = nmp_object_is_visible (obj_old); + visible_new = nmp_object_is_visible (obj_new); + if (!visible_old && visible_new) { + o = obj_new; + cache_op = NMP_CACHE_OPS_ADDED; + } else if (visible_old && !visible_new) { + o = obj_old; + cache_op = NMP_CACHE_OPS_REMOVED; + } else if (!visible_new) { + /* it was invisible and stayed invisible. Nothing to do. */ + return; + } else + o = obj_new; + break; + case NMP_CACHE_OPS_REMOVED: + if (!nmp_object_is_visible (obj_old)) + return; + o = obj_old; + break; + default: + nm_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); + return; } - NM_SET_OUT (netns, platform->_netns); - return TRUE; + klass = NMP_OBJECT_GET_CLASS (o); + + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id + && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) + _ip4_dev_route_blacklist_notify_route (self, o); + + _LOGt ("emit signal %s %s: %s", + klass->signal_type, + nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + nmp_object_ref (o); + g_signal_emit (self, + _nm_platform_signal_id_get (klass->signal_type_id), + 0, + (int) klass->obj_type, + o->object.ifindex, + &o->object, + (int) cache_op); + nmp_object_unref (o); } /*****************************************************************************/ -static gboolean -_vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) +NMPCache * +nm_platform_get_cache (NMPlatform *self) { - NMPlatformIP4Route rt = route->r4; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; - - return nm_platform_ip4_route_add (self, &rt); + return NM_PLATFORM_GET_PRIVATE (self)->cache; } -static gboolean -_vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) +NMPNetns * +nm_platform_netns_get (NMPlatform *self) { - NMPlatformIP6Route rt = route->r6; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; + _CHECK_SELF (self, klass, NULL); - return nm_platform_ip6_route_add (self, &rt); + return self->_netns; } -static gboolean -_vtr_v4_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) +gboolean +nm_platform_netns_push (NMPlatform *self, NMPNetns **netns) { - return nm_platform_ip4_route_delete (self, - ifindex > 0 ? ifindex : route->rx.ifindex, - route->r4.network, - route->rx.plen, - route->rx.metric); -} + g_return_val_if_fail (NM_IS_PLATFORM (self), FALSE); -static gboolean -_vtr_v6_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) -{ - return nm_platform_ip6_route_delete (self, - ifindex > 0 ? ifindex : route->rx.ifindex, - route->r6.network, - route->rx.plen, - route->rx.metric); + if ( self->_netns + && !nmp_netns_push (self->_netns)) { + NM_SET_OUT (netns, NULL); + return FALSE; + } + + NM_SET_OUT (netns, self->_netns); + return TRUE; } +/*****************************************************************************/ + static guint32 _vtr_v4_metric_normalize (guint32 metric) { return metric; } -static gboolean -_vtr_v4_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) -{ - return nm_platform_ip4_route_delete (self, ifindex, 0, 0, metric); -} - -static gboolean -_vtr_v6_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) -{ - return nm_platform_ip6_route_delete (self, ifindex, in6addr_any, 0, metric); -} - /*****************************************************************************/ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, .addr_family = AF_INET, .sizeof_route = sizeof (NMPlatformIP4Route), - .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip4_route_cmp_full, + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .route_get_all = nm_platform_ip4_route_get_all, - .route_add = _vtr_v4_route_add, - .route_delete = _vtr_v4_route_delete, - .route_delete_default = _vtr_v4_route_delete_default, .metric_normalize = _vtr_v4_metric_normalize, }; const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, .addr_family = AF_INET6, .sizeof_route = sizeof (NMPlatformIP6Route), - .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip6_route_cmp_full, + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .route_get_all = nm_platform_ip6_route_get_all, - .route_add = _vtr_v6_route_add, - .route_delete = _vtr_v6_route_delete, - .route_delete_default = _vtr_v6_route_delete_default, .metric_normalize = nm_utils_ip6_route_metric_normalize, }; @@ -4622,6 +6085,10 @@ set_property (GObject *object, guint prop_id, self->_netns = g_object_ref (netns); } break; + case PROP_USE_UDEV: + /* construct-only */ + priv->use_udev = g_value_get_boolean (value); + break; case PROP_LOG_WITH_PTR: /* construct-only */ priv->log_with_ptr = g_value_get_boolean (value); @@ -4638,12 +6105,40 @@ nm_platform_init (NMPlatform *self) self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_PLATFORM, NMPlatformPrivate); } +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMPlatform *self; + NMPlatformPrivate *priv; + + object = G_OBJECT_CLASS (nm_platform_parent_class)->constructor (type, + n_construct_params, + construct_params); + self = NM_PLATFORM (object); + priv = NM_PLATFORM_GET_PRIVATE (self); + + priv->multi_idx = nm_dedup_multi_index_new (); + + priv->cache = nmp_cache_new (nm_platform_get_multi_idx (self), + priv->use_udev); + return object; +} + static void finalize (GObject *object) { NMPlatform *self = NM_PLATFORM (object); + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_check_id); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); g_clear_object (&self->_netns); + nm_dedup_multi_index_unref (priv->multi_idx); + nmp_cache_free (priv->cache); } static void @@ -4653,6 +6148,7 @@ nm_platform_class_init (NMPlatformClass *platform_class) g_type_class_add_private (object_class, sizeof (NMPlatformPrivate)); + object_class->constructor = constructor; object_class->set_property = set_property; object_class->finalize = finalize; @@ -4667,6 +6163,14 @@ nm_platform_class_init (NMPlatformClass *platform_class) G_PARAM_STATIC_STRINGS)); g_object_class_install_property + (object_class, PROP_USE_UDEV, + g_param_spec_boolean (NM_PLATFORM_USE_UDEV, "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_LOG_WITH_PTR, g_param_spec_boolean (NM_PLATFORM_LOG_WITH_PTR, "", "", TRUE, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 1b8fa133..d155c109 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -45,12 +45,16 @@ /*****************************************************************************/ #define NM_PLATFORM_NETNS_SUPPORT "netns-support" +#define NM_PLATFORM_USE_UDEV "use-udev" #define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" /*****************************************************************************/ struct udev_device; +typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, + gpointer user_data); + /* workaround for older libnl version, that does not define these flags. */ #ifndef IFA_F_MANAGETEMPADDR #define IFA_F_MANAGETEMPADDR 0x100 @@ -59,6 +63,8 @@ struct udev_device; #define IFA_F_NOPREFIXROUTE 0x200 #endif +#define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ + /* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers * that don't define it. */ #define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ @@ -72,6 +78,74 @@ struct udev_device; /* Redefine this in host's endianness */ #define NM_GRE_KEY 0x2000 +typedef enum { + /* use our own platform enum for the nlmsg-flags. Otherwise, we'd have + * to include <linux/netlink.h> */ + NMP_NLM_FLAG_F_REPLACE = 0x100, /* NLM_F_REPLACE, Override existing */ + NMP_NLM_FLAG_F_EXCL = 0x200, /* NLM_F_EXCL, Do not touch, if it exists */ + NMP_NLM_FLAG_F_CREATE = 0x400, /* NLM_F_CREATE, Create, if it does not exist */ + NMP_NLM_FLAG_F_APPEND = 0x800, /* NLM_F_APPEND, Add to end of list */ + + NMP_NLM_FLAG_FMASK = 0xFFFF, /* a mask for all NMP_NLM_FLAG_F_* flags */ + + /* instructs NM to suppress logging an error message for any failures + * received from kernel. + * + * It will still log with debug-level, and it will still log + * other failures aside the kernel response. */ + NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE = 0x10000, + + /* the following aliases correspond to iproute2's `ip route CMD` for + * RTM_NEWROUTE, with CMD being one of add, change, replace, prepend, + * append and test. */ + NMP_NLM_FLAG_ADD = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_EXCL, + NMP_NLM_FLAG_CHANGE = NMP_NLM_FLAG_F_REPLACE, + NMP_NLM_FLAG_REPLACE = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_REPLACE, + NMP_NLM_FLAG_PREPEND = NMP_NLM_FLAG_F_CREATE, + NMP_NLM_FLAG_APPEND = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_APPEND, + NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL, +} NMPNlmFlags; + +typedef enum { + /* compare fields which kernel considers as similar routes. + * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + * and means that `ip route add` would fail to add two routes + * that have the same NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID. + * On the other hand, `ip route append` would allow that, as + * long as NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID differs. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, + + /* compare two routes as kernel would allow to add them with + * `ip route append`. In other words, kernel does not allow you to + * add two routes (at the same time) which compare equal according + * to NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID. + * + * For the ID we can only recognize route fields that we actually implement. + * However, kernel supports more routing options, some of them also part of + * the ID. NetworkManager is oblivious to these options and will wrongly think + * that two routes are idential, while they are not. That can lead to an + * inconsistent platform cache. Not much what we can do about that, except + * implementing all options that kernel supports *sigh*. See rh#1337860. + */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + + /* compare all fields as they make sense for kernel. For example, + * a route destination 192.168.1.5/24 is not accepted by kernel and + * we treat it identical to 192.168.1.0/24. Semantically these + * routes are identical, but NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL will + * report them as different. + * + * The result shall be identical to call first nm_platform_ip_route_normalize() + * on both routes and then doing a full comparison. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY, + + /* compare all fields. This should have the same effect as memcmp(), + * except allowing for undefined data in holes between field alignment. + */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, + +} NMPlatformIPRouteCmpType; + typedef enum { /*< skip >*/ /* dummy value, to enforce that the enum type is signed and has a size @@ -91,6 +165,8 @@ typedef enum { /*< skip >*/ NM_PLATFORM_ERROR_NOT_SLAVE, NM_PLATFORM_ERROR_NO_FIRMWARE, NM_PLATFORM_ERROR_OPNOTSUPP, + NM_PLATFORM_ERROR_NETLINK, + NM_PLATFORM_ERROR_CANT_SET_MTU, } NMPlatformError; #define NM_PLATFORM_LINK_OTHER_NETNS (-1) @@ -172,21 +248,9 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -typedef enum { /*< skip >*/ - NM_PLATFORM_GET_ROUTE_FLAGS_NONE = 0, - - /* Whether to include default-routes/non-default-routes. Omitting - * both WITH_DEFAULT and WITH_NON_DEFAULT, is equal to specifying - * both of them. */ - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT = (1LL << 0), - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT = (1LL << 1), - - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL = (1LL << 2), -} NMPlatformGetRouteFlags; - -typedef struct { +struct _NMPlatformObject { __NMPlatformObject_COMMON; -} NMPlatformObject; +}; #define __NMPlatformIPAddress_COMMON \ @@ -298,7 +362,15 @@ typedef union { /* The NMIPConfigSource. For routes that we receive from cache this corresponds * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values). * When adding a route, the source will be coerced to the protocol using - * nmp_utils_ip_config_source_coerce_to_rtprot(). */ \ + * nmp_utils_ip_config_source_coerce_to_rtprot(). + * + * rtm_protocol is part of the primary key of an IPv4 route (meaning, you can add + * two IPv4 routes that only differ in their rtm_protocol. For IPv6, that is not + * the case. + * + * When deleting an IPv4/IPv6 route, the rtm_protocol field must match (even + * if it is not part of the primary key for IPv6) -- unless rtm_protocol is set + * to zero, in which case the first matching route (with proto ignored) is deleted. */ \ NMIPConfigSource rt_source; \ \ guint8 plen; \ @@ -308,21 +380,58 @@ typedef union { * of platform users. This flag is internal to track those hidden * routes. Such a route is not alive, according to nmp_object_is_alive(). */ \ bool rt_cloned:1; \ + \ + \ + /* RTA_METRICS: + * + * For IPv4 routes, these properties are part of their + * ID (meaning: you can add otherwise idential IPv4 routes that + * only differ by the metric property). + * On the other hand, for IPv6 you cannot add two IPv6 routes that only differ + * by an RTA_METRICS property. + * + * When deleting a route, kernel seems to ignore the RTA_METRICS propeties. + * That is a problem/bug for IPv4 because you cannot explicitly select which + * route to delete. Kernel just picks the first. See rh#1475642. */ \ + \ + /* RTA_METRICS.RTAX_LOCK (iproute2: "lock" arguments) */ \ bool lock_window:1; \ bool lock_cwnd:1; \ bool lock_initcwnd:1; \ bool lock_initrwnd:1; \ bool lock_mtu:1; \ \ - guint32 metric; \ + /* RTA_METRICS.RTAX_ADVMSS (iproute2: advmss) */ \ guint32 mss; \ - guint32 tos; \ + \ + /* RTA_METRICS.RTAX_WINDOW (iproute2: window) */ \ guint32 window; \ + \ + /* RTA_METRICS.RTAX_CWND (iproute2: cwnd) */ \ guint32 cwnd; \ + \ + /* RTA_METRICS.RTAX_INITCWND (iproute2: initcwnd) */ \ guint32 initcwnd; \ + \ + /* RTA_METRICS.RTAX_INITRWND (iproute2: initrwnd) */ \ guint32 initrwnd; \ + \ + /* RTA_METRICS.RTAX_MTU (iproute2: mtu) */ \ guint32 mtu; \ - ; + \ + \ + /* RTA_PRIORITY (iproute2: metric) */ \ + guint32 metric; \ + \ + /* rtm_table, RTA_TABLE. + * + * This is not the original table ID. Instead, 254 (RT_TABLE_MAIN) and + * zero (RT_TABLE_UNSPEC) are swapped, so that the default is the main + * table. Use nm_platform_route_table_coerce()/nm_platform_route_table_uncoerce(). */ \ + guint32 table_coerced; \ + \ + /*end*/ + typedef struct { __NMPlatformIPRoute_COMMON; @@ -332,30 +441,89 @@ typedef struct { }; } NMPlatformIPRoute; +#if _NM_CC_SUPPORT_GENERIC +#define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \ + (_Generic ((route), \ + const NMPlatformIPRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIPRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + const void *: ((const NMPlatformIPRoute *) (route))->plen, \ + void *: ((const NMPlatformIPRoute *) (route))->plen) == 0) +#else #define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \ ( ((const NMPlatformIPRoute *) (route))->plen <= 0 ) +#endif struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; in_addr_t network; - in_addr_t gateway; - /* The bitwise inverse of the route scope. It is inverted so that the - * default value (RT_SCOPE_NOWHERE) is nul. */ - guint8 scope_inv; + /* RTA_GATEWAY. The gateway is part of the primary key for a route */ + in_addr_t gateway; - /* RTA_PREFSRC/rtnl_route_get_pref_src(). A value of zero means that - * no pref-src is set. */ + /* RTA_PREFSRC (called "src" by iproute2). + * + * pref_src is part of the ID of an IPv4 route. When deleting a route, + * pref_src must match, unless set to 0.0.0.0 to match any. */ in_addr_t pref_src; + + /* rtm_tos (iproute2: tos) + * + * For IPv4, tos is part of the weak-id (like metric). + * + * For IPv6, tos is ignored by kernel. */ + guint8 tos; + + /* The bitwise inverse of the route scope rtm_scope. It is inverted so that the + * default value (RT_SCOPE_NOWHERE) is zero. Use nm_platform_route_scope_inv() + * to convert back and forth between the inverese representation and the + * real value. + * + * rtm_scope is part of the primary key for IPv4 routes. When deleting a route, + * the scope must match, unless it is left at RT_SCOPE_NOWHERE, in which case the first + * matching route is deleted. + * + * For IPv6 routes, the scope is ignored and kernel always assumes global scope. + * Hence, this field is only in NMPlatformIP4Route. */ + guint8 scope_inv; }; struct _NMPlatformIP6Route { __NMPlatformIPRoute_COMMON; struct in6_addr network; + + /* RTA_GATEWAY. The gateway is part of the primary key for a route */ struct in6_addr gateway; + + /* RTA_PREFSRC (called "src" by iproute2). + * + * pref_src is not part of the ID for an IPv6 route. You cannot add two + * routes that only differ by pref_src. + * + * When deleting a route, pref_src is ignored by kernel. */ struct in6_addr pref_src; + + /* RTA_SRC and rtm_src_len (called "from" by iproute2). + * + * Kernel clears the host part of src/src_plen. + * + * src/src_plen is part of the ID of a route just like network/plen. That is, + * Not only `ip route append`, but also `ip route add` allows to add routes that only + * differ in their src/src_plen. + */ struct in6_addr src; guint8 src_plen; + + /* RTA_PREF router preference. + * + * The type is guint8 to keep the struct size small. But the values are compatible with + * the NMIcmpv6RouterPref enum. */ + guint8 rt_pref; }; typedef union { @@ -372,14 +540,11 @@ typedef union { typedef struct { gboolean is_ip4; + NMPObjectType obj_type; int addr_family; gsize sizeof_route; - int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part); + int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type); const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); - GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); - gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); - gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); - gboolean (*route_delete_default) (NMPlatform *self, int ifindex, guint32 metric); guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; @@ -426,7 +591,7 @@ typedef struct { typedef struct { int parent_ifindex; - guint64 sci; /* host byte order */ + guint64 sci; /* host byte order */ guint64 cipher_suite; guint32 window; guint8 icv_length; @@ -452,11 +617,11 @@ typedef struct { in_addr_t local; in_addr_t remote; int parent_ifindex; + guint16 flags; guint8 ttl; guint8 tos; guint8 proto; bool path_mtu_discovery:1; - guint16 flags; } NMPlatformLnkSit; typedef struct { @@ -501,6 +666,12 @@ typedef enum { NM_PLATFORM_LINK_DUPLEX_FULL, } NMPlatformLinkDuplexType; +typedef enum { + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS = (1LL << 0), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL = (1LL << 1), + NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF = (1LL << 2), +} NMPlatformKernelSupportFlags; + /*****************************************************************************/ struct _NMPlatformPrivate; @@ -517,22 +688,14 @@ 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); - const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); - const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); - const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); - - const NMPObject *(*link_get_lnk) (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); - - GArray *(*link_get_all) (NMPlatform *); gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link); gboolean (*link_delete) (NMPlatform *, int ifindex); - const char *(*link_get_type_name) (NMPlatform *, int ifindex); - gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged); gboolean (*link_refresh) (NMPlatform *, int ifindex); @@ -556,7 +719,8 @@ typedef struct { guint8 *buf, size_t *length); NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); - gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); + NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); + gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); gboolean (*link_set_sriov_num_vfs) (NMPlatform *, int ifindex, guint num_vfs); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); @@ -641,8 +805,6 @@ typedef struct { gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); gboolean (*mesh_set_ssid) (NMPlatform *, int ifindex, const guint8 *ssid, gsize len); - GArray * (*ip4_address_get_all) (NMPlatform *, int ifindex); - GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex); gboolean (*ip4_address_add) (NMPlatform *, int ifindex, in_addr_t address, @@ -662,20 +824,21 @@ typedef struct { guint32 flags); gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); - const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - - GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); - GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); - gboolean (*ip4_route_add) (NMPlatform *, const NMPlatformIP4Route *route); - gboolean (*ip6_route_add) (NMPlatform *, const NMPlatformIP6Route *route); - gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); - gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); - const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); - const NMPlatformIP6Route *(*ip6_route_get) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); - - gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); - gboolean (*check_support_user_ipv6ll) (NMPlatform *); + + NMPlatformError (*ip_route_add) (NMPlatform *, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route); + gboolean (*ip_route_delete) (NMPlatform *, const NMPObject *obj); + + NMPlatformError (*ip_route_get) (NMPlatform *self, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route); + + NMPlatformKernelSupportFlags (*check_kernel_support) (NMPlatform * self, + NMPlatformKernelSupportFlags request_flags); } NMPlatformClass; /* NMPlatform signals @@ -709,6 +872,72 @@ NMPlatform *nm_platform_get (void); /*****************************************************************************/ /** + * nm_platform_route_table_coerce: + * @table: the route table, in its original value as received + * from rtm_table/RTA_TABLE. + * + * Returns: returns the coerced table id, that can be stored in + * NMPlatformIPRoute.table_coerced. + */ +static inline guint32 +nm_platform_route_table_coerce (guint32 table) +{ + /* For kernel, the default table is RT_TABLE_MAIN (254). + * We want that in NMPlatformIPRoute.table_coerced a numeric + * zero is the default. Hence, @table_coerced swaps the + * value 0 and 254. Use nm_platform_route_table_coerce() + * and nm_platform_route_table_uncoerce() to convert between + * the two domains. */ + switch (table) { + case 0 /* RT_TABLE_UNSPEC */: + return 254; + case 254 /* RT_TABLE_MAIN */: + return 0; + default: + return table; + } +} + +/** + * nm_platform_route_table_uncoerce: + * @table: the route table, in its coerced value + * @normalize: whether to normalize RT_TABLE_UNSPEC to + * RT_TABLE_MAIN. For kernel, routes with a table id + * RT_TABLE_UNSPEC do not exist and are treated like + * RT_TABLE_MAIN. + * + * Returns: reverts the coerced table ID in NMPlatformIPRoute.table_coerced + * to the original value as kernel understands it. + */ +static inline guint32 +nm_platform_route_table_uncoerce (guint32 table_coerced, gboolean normalize) +{ + /* this undoes nm_platform_route_table_coerce(). */ + switch (table_coerced) { + case 0 /* RT_TABLE_UNSPEC */: + return 254; + case 254 /* RT_TABLE_MAIN */: + return normalize ? 254 : 0; + default: + return table_coerced; + } +} + +static inline gboolean +nm_platform_route_table_is_main (guint32 table) +{ + /* same as + * nm_platform_route_table_uncoerce (table, TRUE) == RT_TABLE_MAIN + * and + * nm_platform_route_table_uncoerce (nm_platform_route_table_coerce (table), TRUE) == RT_TABLE_MAIN + * + * That is, the function operates the same on @table and its coerced + * form. + */ + return table == 0 || table == 254; +} + +/** * nm_platform_route_scope_inv: * @scope: the route scope, either its original value, or its inverse. * @@ -725,6 +954,7 @@ _nm_platform_uint8_inv (guint8 scope) return (guint8) ~scope; } +gboolean nm_platform_get_use_udev (NMPlatform *self); gboolean nm_platform_get_log_with_ptr (NMPlatform *self); NMPNetns *nm_platform_netns_get (NMPlatform *self); @@ -732,8 +962,11 @@ gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns); const char *nm_link_type_to_string (NMLinkType link_type); -const char *_nm_platform_error_to_string (NMPlatformError error); -#define nm_platform_error_to_string(error) NM_UTILS_LOOKUP_STR (_nm_platform_error_to_string, error) +const char *nm_platform_error_to_string (NMPlatformError error, + char *buf, + gsize buf_len); +#define nm_platform_error_to_string_a(error) \ + (nm_platform_error_to_string ((error), g_alloca (30), 30)) #define NMP_SYSCTL_PATHID_ABSOLUTE(path) \ ((const char *) NULL), -1, (path) @@ -759,19 +992,39 @@ 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); +const NMPObject *nm_platform_link_get_obj (NMPlatform *self, + int ifindex, + gboolean visible_only); const NMPlatformLink *nm_platform_link_get (NMPlatform *self, int ifindex); const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname); const NMPlatformLink *nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length); -GArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); +GPtrArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); NMPlatformError nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_veth_add (NMPlatform *self, const char *name, const char *peer, const NMPlatformLink **out_link); + gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd); +struct _NMDedupMultiHeadEntry; +struct _NMPLookup; +const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *platform, + const struct _NMPLookup *lookup); + +gboolean nm_platform_lookup_predicate_routes_main (const NMPObject *obj, + gpointer user_data); +gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data); + +GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, + const struct _NMPLookup *lookup, + NMPObjectPredicateFunc predicate, + gpointer user_data); + /* convienience methods to lookup the link and access fields of NMPlatformLink. */ int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); @@ -808,7 +1061,8 @@ gboolean nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtils gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); -gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); +NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); +gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); gboolean nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); @@ -938,9 +1192,8 @@ NMPlatformError nm_platform_link_sit_add (NMPlatform *self, const NMPlatformLnkSit *props, const NMPlatformLink **out_link); -const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -GArray *nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex); -GArray *nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex); +const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address); + gboolean nm_platform_ip4_address_add (NMPlatform *self, int ifindex, in_addr_t address, @@ -960,18 +1213,44 @@ 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, const GArray *known_addresses, GPtrArray **out_added_addresses); -gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local); -gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); - -const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); -const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); -GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); -GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); -gboolean nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route); -gboolean nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route); -gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); -gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); +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_ip_address_flush (NMPlatform *self, + int addr_family, + int ifindex); + +void nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route); + +NMPlatformError nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route); +NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); + +gboolean nm_platform_ip_route_delete (NMPlatform *self, const NMPObject *route); + +GPtrArray *nm_platform_ip_route_get_prune_list (NMPlatform *self, + int addr_family, + int ifindex, + NMIPRouteTableSyncMode route_table_sync); + +gboolean nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + GPtrArray *routes_prune, + GPtrArray **out_temporary_not_available); + +gboolean nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex); + +NMPlatformError nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route); const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); @@ -1006,23 +1285,39 @@ int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVla int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); -int nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part); -int nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part); + +int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type); +int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type); static inline int -nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) +nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) { - return nm_platform_ip4_route_cmp_full (a, b, TRUE); + return nm_platform_ip4_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static inline int -nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) +nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) { - return nm_platform_ip6_route_cmp_full (a, b, TRUE); + return nm_platform_ip6_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } -gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self); -gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self); +void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h); +void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h); +void nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h); +void nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h); +void nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h); +void nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h); +void nm_platform_lnk_ipip_hash_update (const NMPlatformLnkIpIp *obj, NMHashState *h); +void nm_platform_lnk_macsec_hash_update (const NMPlatformLnkMacsec *obj, NMHashState *h); +void nm_platform_lnk_macvlan_hash_update (const NMPlatformLnkMacvlan *obj, NMHashState *h); +void nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *h); +void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h); +void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h); + +NMPlatformKernelSupportFlags nm_platform_check_kernel_support (NMPlatform *self, + NMPlatformKernelSupportFlags request_flags); const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); @@ -1035,4 +1330,10 @@ gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMS gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist); + +struct _NMDedupMultiIndex *nm_platform_get_multi_idx (NMPlatform *self); + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c index 4acd4761..34215828 100644 --- a/src/platform/nmp-netns.c +++ b/src/platform/nmp-netns.c @@ -299,7 +299,7 @@ _netns_new (GError **error) g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "Failed opening mntns: %s", g_strerror (errsv)); - close (fd_net); + nm_close (fd_net); return NULL; } @@ -620,7 +620,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd) filename, g_strerror (errsv)); return FALSE; } - close (fd); + nm_close (fd); if (mount (PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { errsv = errno; @@ -702,15 +702,11 @@ dispose (GObject *object) NMPNetns *self = NMP_NETNS (object); NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE (self); - if (priv->fd_net > 0) { - close (priv->fd_net); - priv->fd_net = 0; - } + nm_close (priv->fd_net); + priv->fd_net = -1; - if (priv->fd_mnt > 0) { - close (priv->fd_mnt); - priv->fd_mnt = 0; - } + nm_close (priv->fd_mnt); + priv->fd_mnt = -1; G_OBJECT_CLASS (nmp_netns_parent_class)->dispose (object); } diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h index 56c1e7e8..55a4b95f 100644 --- a/src/platform/nmp-netns.h +++ b/src/platform/nmp-netns.h @@ -60,8 +60,7 @@ _nm_auto_pop_netns (NMPNetns **p) errno = errsv; } } - -#define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns))) +#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) gboolean nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd); gboolean nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index ecec8f0f..a8600705 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -51,6 +51,11 @@ /*****************************************************************************/ +typedef struct { + NMDedupMultiIdxType parent; + NMPCacheIdType cache_id_type; +} DedupMultiIdxType; + struct _NMPCache { /* the cache contains only one hash table for all object types, and similarly * it contains only one NMMultiIndex. @@ -66,23 +71,238 @@ struct _NMPCache { * This effectively merges the udev-device cache into the NMPCache. */ - GHashTable *idx_main; - NMMultiIndex *idx_multi; + NMDedupMultiIndex *multi_idx; + + /* an idx_type entry for each NMP_CACHE_ID_TYPE. Note that NONE (zero) + * is skipped, so the index is shifted by one: idx_type[cache_id_type - 1]. + * + * Don't bother, use _idx_type_get() instead! */ + DedupMultiIdxType idx_types[NMP_CACHE_ID_TYPE_MAX]; gboolean use_udev; }; /*****************************************************************************/ -static inline guint -_id_hash_ip6_addr (const struct in6_addr *addr) +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class; + +static void +_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) { - guint hash = (guint) 0x897da53981a13ULL; - int i; + const NMPObject *o = (NMPObject *) obj; + + nm_assert (idx_type && idx_type->klass == &_dedup_multi_idx_type_class); + nm_assert (NMP_OBJECT_GET_TYPE (o) != NMP_OBJECT_TYPE_UNKNOWN); + + nmp_object_id_hash_update (o, h); +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const NMPObject *o_a = (NMPObject *) obj_a; + const NMPObject *o_b = (NMPObject *) obj_b; + + nm_assert (idx_type && idx_type->klass == &_dedup_multi_idx_type_class); + nm_assert (NMP_OBJECT_GET_TYPE (o_a) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (NMP_OBJECT_GET_TYPE (o_b) != NMP_OBJECT_TYPE_UNKNOWN); - for (i = 0; i < sizeof (*addr); i++) - hash = (hash * 33) + ((const guint8 *) addr)[i]; - return hash; + return nmp_object_id_equal (o_a, o_b); +} + +static guint +_idx_obj_part (const DedupMultiIdxType *idx_type, + const NMPObject *obj_a, + const NMPObject *obj_b, + NMHashState *h) +{ + NMPObjectType obj_type; + + /* the hash/equals functions are strongly related. So, keep them + * side-by-side and do it all in _idx_obj_part(). */ + + nm_assert (idx_type); + nm_assert (idx_type->parent.klass == &_dedup_multi_idx_type_class); + nm_assert (obj_a); + nm_assert (NMP_OBJECT_GET_TYPE (obj_a) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_b || (NMP_OBJECT_GET_TYPE (obj_b) != NMP_OBJECT_TYPE_UNKNOWN)); + nm_assert (!h || !obj_b); + + switch (idx_type->cache_id_type) { + + case NMP_CACHE_ID_TYPE_OBJECT_TYPE: + if (obj_b) + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b); + if (h) { + nm_hash_update_vals (h, + idx_type->cache_id_type, + NMP_OBJECT_GET_TYPE (obj_a)); + } + return 1; + + case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: + if (NMP_OBJECT_GET_TYPE (obj_a) != NMP_OBJECT_TYPE_LINK) { + /* first check, whether obj_a is suitable for this idx_type. + * If not, return 0 (which is correct for partitionable(), hash() and equal() + * functions. */ + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + /* we are in equal() mode. Compare obj_b with obj_a. */ + return NMP_OBJECT_GET_TYPE (obj_b) == NMP_OBJECT_TYPE_LINK + && nm_streq (obj_a->link.name, obj_b->link.name); + } + if (h) { + nm_hash_update_val (h, idx_type->cache_id_type); + nm_hash_update_strarr (h, obj_a->link.name); + } + /* just return 1, to indicate that obj_a is partitionable by this idx_type. */ + return 1; + + case NMP_CACHE_ID_TYPE_DEFAULT_ROUTES: + if ( !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj_a->ip_route) + || !nmp_object_is_visible (obj_a)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj_b->ip_route) + && nmp_object_is_visible (obj_b); + } + if (h) { + nm_hash_update_vals (h, + idx_type->cache_id_type, + NMP_OBJECT_GET_TYPE (obj_a)); + } + return 1; + + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + if ( !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || !nmp_object_is_visible (obj_a)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + nm_assert (obj_a->object.ifindex > 0); + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) + && obj_a->object.ifindex == obj_b->object.ifindex + && nmp_object_is_visible (obj_b); + } + if (h) { + nm_hash_update_vals (h, + idx_type->cache_id_type, + obj_a->object.ifindex); + } + return 1; + + case NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID: + obj_type = NMP_OBJECT_GET_TYPE (obj_a); + if ( !NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || obj_a->object.ifindex <= 0) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return obj_type == NMP_OBJECT_GET_TYPE (obj_b) + && obj_b->object.ifindex > 0 + && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + ? (nm_platform_ip4_route_cmp (&obj_a->ip4_route, &obj_b->ip4_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0) + : (nm_platform_ip6_route_cmp (&obj_a->ip6_route, &obj_b->ip6_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0)); + } + if (h) { + nm_hash_update_val (h, idx_type->cache_id_type); + if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) + nm_platform_ip4_route_hash_update (&obj_a->ip4_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, h); + else + nm_platform_ip6_route_hash_update (&obj_a->ip6_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, h); + } + return 1; + + case NMP_CACHE_ID_TYPE_NONE: + case __NMP_CACHE_ID_TYPE_MAX: + break; + } + nm_assert_not_reached (); + return 0; +} + +static gboolean +_idx_obj_partitionable (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + (NMPObject *) obj, + NULL, + NULL) != 0; +} + +static void +_idx_obj_partition_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) +{ + _idx_obj_part ((DedupMultiIdxType *) idx_type, + (NMPObject *) obj, + NULL, + h); +} + +static gboolean +_idx_obj_partition_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + (NMPObject *) obj_a, + (NMPObject *) obj_b, + NULL); +} + +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = { + .idx_obj_id_hash_update = _idx_obj_id_hash_update, + .idx_obj_id_equal = _idx_obj_id_equal, + .idx_obj_partitionable = _idx_obj_partitionable, + .idx_obj_partition_hash_update = _idx_obj_partition_hash_update, + .idx_obj_partition_equal = _idx_obj_partition_equal, +}; + +static void +_dedup_multi_idx_type_init (DedupMultiIdxType *idx_type, NMPCacheIdType cache_id_type) +{ + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, + &_dedup_multi_idx_type_class); + idx_type->cache_id_type = cache_id_type; +} + +/*****************************************************************************/ + +static void +_vlan_xgress_qos_mappings_hash_update (guint n_map, + const NMVlanQosMapping *map, + NMHashState *h) +{ + /* ensure no padding. */ + G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == 2 * sizeof (guint32)); + + nm_hash_update_val (h, n_map); + if (n_map) + nm_hash_update (h, map, n_map * sizeof (*map)); } static int @@ -150,12 +370,18 @@ _link_get_driver (struct udev_device *udevice, const char *kind, int ifindex) } void -_nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) +_nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev) { const char *driver = NULL; gboolean initialized = FALSE; + NMPObject *obj; - nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + nm_assert (obj_orig || *obj_new); + nm_assert (obj_new); + nm_assert (!obj_orig || NMP_OBJECT_GET_TYPE (obj_orig) == NMP_OBJECT_TYPE_LINK); + nm_assert (!*obj_new || NMP_OBJECT_GET_TYPE (*obj_new) == NMP_OBJECT_TYPE_LINK); + + obj = *obj_new ?: obj_orig; /* The link contains internal fields that are combined by * properties from netlink and udev. Update those properties */ @@ -179,17 +405,34 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) } } + if ( nm_streq0 (obj->link.driver, driver) + && obj->link.initialized == initialized) + return; + + if (!*obj_new) + obj = *obj_new = nmp_object_clone (obj, FALSE); + obj->link.driver = driver; obj->link.initialized = initialized; } static void -_nmp_object_fixup_link_master_connected (NMPObject *obj, const NMPCache *cache) +_nmp_object_fixup_link_master_connected (NMPObject **obj_new, NMPObject *obj_orig, const NMPCache *cache) { - nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + NMPObject *obj; + + nm_assert (obj_orig || *obj_new); + nm_assert (obj_new); + nm_assert (!obj_orig || NMP_OBJECT_GET_TYPE (obj_orig) == NMP_OBJECT_TYPE_LINK); + nm_assert (!*obj_new || NMP_OBJECT_GET_TYPE (*obj_new) == NMP_OBJECT_TYPE_LINK); + + obj = *obj_new ?: obj_orig; - if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) + if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) { + if (!*obj_new) + obj = *obj_new = nmp_object_clone (obj, FALSE); obj->link.connected = !obj->link.connected; + } } /*****************************************************************************/ @@ -204,33 +447,6 @@ nmp_class_from_type (NMPObjectType obj_type) /*****************************************************************************/ -NMPObject * -nmp_object_ref (NMPObject *obj) -{ - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); - g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL); - obj->_ref_count++; - - return obj; -} - -void -nmp_object_unref (NMPObject *obj) -{ - if (obj) { - g_return_if_fail (obj->_ref_count > 0); - g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT); - if (--obj->_ref_count <= 0) { - const NMPClass *klass = obj->_class; - - nm_assert (!obj->is_cached); - if (klass->cmd_obj_dispose) - klass->cmd_obj_dispose (obj); - g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), obj); - } - } -} - static void _vt_cmd_obj_dispose_link (NMPObject *obj) { @@ -259,7 +475,7 @@ _nmp_object_new_from_class (const NMPClass *klass) obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object)); obj->_class = klass; - obj->_ref_count = 1; + obj->parent._ref_count = 1; return obj; } @@ -287,14 +503,29 @@ nmp_object_new_link (int ifindex) /*****************************************************************************/ -static const NMPObject * +static void _nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) { + nm_assert (obj); + nm_assert (klass); + + memset (obj, 0, sizeof (NMPObject)); + obj->_class = klass; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; +} + +static NMPObject * +_nmp_object_stackinit_from_type (NMPObject *obj, NMPObjectType obj_type) +{ + const NMPClass *klass; + + nm_assert (obj); + klass = nmp_class_from_type (obj_type); nm_assert (klass); memset (obj, 0, sizeof (NMPObject)); obj->_class = klass; - obj->_ref_count = NMP_REF_COUNT_STACKINIT; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; } @@ -318,31 +549,24 @@ nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) nm_assert (obj); klass = NMP_OBJECT_GET_CLASS (src); - if (!klass->cmd_obj_stackinit_id) - nmp_object_stackinit (obj, klass->obj_type, NULL); - else - klass->cmd_obj_stackinit_id (obj, src); + _nmp_object_stackinit_from_class (obj, klass); + if (klass->cmd_plobj_id_copy) + klass->cmd_plobj_id_copy (&obj->object, &src->object); return obj; } const NMPObject * nmp_object_stackinit_id_link (NMPObject *obj, int ifindex) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_LINK, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_LINK); obj->link.ifindex = ifindex; return obj; } -static void -_vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_link (obj, src->link.ifindex); -} - const NMPObject * nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP4_ADDRESS); obj->ip4_address.ifindex = ifindex; obj->ip4_address.address = address; obj->ip4_address.plen = plen; @@ -350,64 +574,16 @@ nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 addres return obj; } -static void -_vt_cmd_obj_stackinit_id_ip4_address (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen, src->ip4_address.peer_address); -} - const NMPObject * -nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, guint8 plen) +nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP6_ADDRESS); obj->ip4_address.ifindex = ifindex; if (address) obj->ip6_address.address = *address; - obj->ip6_address.plen = plen; return obj; } -static void -_vt_cmd_obj_stackinit_id_ip6_address (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip6_address (obj, src->ip_address.ifindex, &src->ip6_address.address, src->ip_address.plen); -} - -const NMPObject * -nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric) -{ - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); - obj->ip4_route.ifindex = ifindex; - obj->ip4_route.network = network; - obj->ip4_route.plen = plen; - obj->ip4_route.metric = metric; - return obj; -} - -static void -_vt_cmd_obj_stackinit_id_ip4_route (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip4_route (obj, src->ip_route.ifindex, src->ip4_route.network, src->ip_route.plen, src->ip_route.metric); -} - -const NMPObject * -nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric) -{ - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - obj->ip6_route.ifindex = ifindex; - if (network) - obj->ip6_route.network = *network; - obj->ip6_route.plen = plen; - obj->ip6_route.metric = metric; - return obj; -} - -static void -_vt_cmd_obj_stackinit_id_ip6_route (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip6_route (obj, src->ip_route.ifindex, &src->ip6_route.network, src->ip_route.plen, src->ip_route.metric); -} - /*****************************************************************************/ const char * @@ -435,9 +611,8 @@ nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2))); @@ -462,9 +637,8 @@ _vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_strin return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', obj->_link.netlink.is_in_netlink ? '+' : '-', @@ -503,9 +677,8 @@ _vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_s case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); @@ -562,10 +735,53 @@ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsiz _vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); _vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen, obj->peer_address != obj->address ? "," : "", - obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : ""); + obj->peer_address != obj->address ? nm_utils_inet4_ntop (nm_utils_ip4_address_clear_host_address (obj->peer_address, obj->plen), buf2) : ""); _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1)); -_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); -_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); + +void +nmp_object_hash_update (const NMPObject *obj, NMHashState *h) +{ + const NMPClass *klass; + + g_return_if_fail (NMP_OBJECT_IS_VALID (obj)); + + klass = NMP_OBJECT_GET_CLASS (obj); + + nm_hash_update_val (h, klass->obj_type); + if (klass->cmd_obj_hash_update) + klass->cmd_obj_hash_update (obj, h); + else if (klass->cmd_plobj_hash_update) + klass->cmd_plobj_hash_update (&obj->object, h); + else + nm_hash_update_val (h, obj); +} + +static void +_vt_cmd_obj_hash_update_link (const NMPObject *obj, NMHashState *h) +{ + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + + nm_platform_link_hash_update (&obj->link, h); + nm_hash_update_vals (h, + obj->_link.netlink.is_in_netlink, + obj->_link.udev.device); + if (obj->_link.netlink.lnk) + nmp_object_hash_update (obj->_link.netlink.lnk, h); +} + +static void +_vt_cmd_obj_hash_update_lnk_vlan (const NMPObject *obj, NMHashState *h) +{ + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LNK_VLAN); + + nm_platform_lnk_vlan_hash_update (&obj->lnk_vlan, h); + _vlan_xgress_qos_mappings_hash_update (obj->_lnk_vlan.n_ingress_qos_map, + obj->_lnk_vlan.ingress_qos_map, + h); + _vlan_xgress_qos_mappings_hash_update (obj->_lnk_vlan.n_egress_qos_map, + obj->_lnk_vlan.egress_qos_map, + h); +} int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) @@ -585,8 +801,10 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) klass1 = NMP_OBJECT_GET_CLASS (obj1); klass2 = NMP_OBJECT_GET_CLASS (obj2); - if (klass1 != klass2) + if (klass1 != klass2) { + nm_assert (klass1->obj_type != klass2->obj_type); return klass1->obj_type < klass2->obj_type ? -1 : 1; + } if (klass1->cmd_obj_cmp) return klass1->cmd_obj_cmp (obj1, obj2); @@ -731,16 +949,12 @@ _vt_cmd_plobj_id_copy (ip6_address, NMPlatformIP6Address, { dst->address = src->address; }); _vt_cmd_plobj_id_copy (ip4_route, NMPlatformIP4Route, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->metric = src->metric; - dst->network = src->network; + *dst = *src; + nm_assert (nm_platform_ip4_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->metric = src->metric; - dst->network = src->network; + *dst = *src; + nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); /* Uses internally nmp_object_copy(), hence it also violates the const @@ -761,127 +975,157 @@ nmp_object_clone (const NMPObject *obj, gboolean id_only) return dst; } -gboolean -nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) +int +nmp_object_id_cmp (const NMPObject *obj1, const NMPObject *obj2) { - const NMPClass *klass; + const NMPClass *klass, *klass2; - if (obj1 == obj2) - return TRUE; - if (!obj1 || !obj2) - return FALSE; + NM_CMP_SELF (obj1, obj2); g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); klass = NMP_OBJECT_GET_CLASS (obj1); - return klass == NMP_OBJECT_GET_CLASS (obj2) - && klass->cmd_plobj_id_equal - && klass->cmd_plobj_id_equal (&obj1->object, &obj2->object); + nm_assert (!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); + + klass2 = NMP_OBJECT_GET_CLASS (obj2); + nm_assert (klass); + if (klass != klass2) { + nm_assert (klass2); + NM_CMP_DIRECT (klass->obj_type, klass2->obj_type); + /* resort to pointer comparison */ + if (klass < klass2) + return -1; + return 1; + } + + if (!klass->cmd_plobj_id_cmp) { + /* the klass doesn't implement ID cmp(). That means, different objects + * never compare equal, but the cmp() according to their pointer value. */ + return (obj1 < obj2) ? -1 : 1; + } + + return klass->cmd_plobj_id_cmp (&obj1->object, &obj2->object); } -#define _vt_cmd_plobj_id_equal(type, plat_type, cmd) \ -static gboolean \ -_vt_cmd_plobj_id_equal_##type (const NMPlatformObject *_obj1, const NMPlatformObject *_obj2) \ +#define _vt_cmd_plobj_id_cmp(type, plat_type, cmd) \ +static int \ +_vt_cmd_plobj_id_cmp_##type (const NMPlatformObject *_obj1, const NMPlatformObject *_obj2) \ { \ const plat_type *const obj1 = (const plat_type *) _obj1; \ const plat_type *const obj2 = (const plat_type *) _obj2; \ - return (cmd); \ -} -_vt_cmd_plobj_id_equal (link, NMPlatformLink, - obj1->ifindex == obj2->ifindex); -_vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address, - obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen - && obj1->address == obj2->address - /* for IPv4 addresses, you can add the same local address with differing peer-adddress - * (IFA_ADDRESS), provided that their net-part differs. */ - && ((obj1->peer_address ^ obj2->peer_address) & nm_utils_ip4_prefix_to_netmask (obj1->plen)) == 0); -_vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, - obj1->ifindex == obj2->ifindex - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - && IN6_ARE_ADDR_EQUAL (&obj1->address, &obj2->address)); -_vt_cmd_plobj_id_equal (ip4_route, NMPlatformIP4Route, - obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen - && obj1->metric == obj2->metric - && nm_utils_ip4_address_clear_host_address (obj1->network, obj1->plen) == nm_utils_ip4_address_clear_host_address (obj2->network, obj2->plen)); -_vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route, - obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen - && obj1->metric == obj2->metric - && ({ - struct in6_addr n1, n2; - - IN6_ARE_ADDR_EQUAL(nm_utils_ip6_address_clear_host_address (&n1, &obj1->network, obj1->plen), - nm_utils_ip6_address_clear_host_address (&n2, &obj2->network, obj2->plen)); - })); + \ + NM_CMP_SELF (obj1, obj2); \ + { cmd; } \ + return 0; \ +} +_vt_cmd_plobj_id_cmp (link, NMPlatformLink, + NM_CMP_FIELD (obj1, obj2, ifindex); +) +_vt_cmd_plobj_id_cmp (ip4_address, NMPlatformIP4Address, + NM_CMP_FIELD (obj1, obj2, ifindex); + NM_CMP_FIELD (obj1, obj2, plen); + NM_CMP_FIELD (obj1, obj2, address); + /* for IPv4 addresses, you can add the same local address with differing peer-adddress + * (IFA_ADDRESS), provided that their net-part differs. */ + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (obj1->peer_address, obj2->peer_address, obj1->plen); +) +_vt_cmd_plobj_id_cmp (ip6_address, NMPlatformIP6Address, + NM_CMP_FIELD (obj1, obj2, ifindex); + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + NM_CMP_FIELD_IN6ADDR (obj1, obj2, address); +) -guint -nmp_object_id_hash (const NMPObject *obj) +static int +_vt_cmd_plobj_id_cmp_ip4_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - const NMPClass *klass; + return nm_platform_ip4_route_cmp ((NMPlatformIP4Route *) obj1, (NMPlatformIP4Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); +} - if (!obj) - return 0; +static int +_vt_cmd_plobj_id_cmp_ip6_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); +} + +void +nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h) +{ + const NMPClass *klass; - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0); + g_return_if_fail (NMP_OBJECT_IS_VALID (obj)); klass = NMP_OBJECT_GET_CLASS (obj); - if (klass->cmd_plobj_id_hash) - return klass->cmd_plobj_id_hash (&obj->object); + nm_assert (!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); + + if (!klass->cmd_plobj_id_hash_update) { + /* The klass doesn't implement ID compare. It means, to use pointer + * equality. */ + nm_hash_update_val (h, obj); + return; + } - /* unhashable objects implement pointer equality. */ - return g_direct_hash (obj); + nm_hash_update_val (h, klass->obj_type); + klass->cmd_plobj_id_hash_update (&obj->object, h); } -#define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \ -static guint \ -_vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ +guint +nmp_object_id_hash (const NMPObject *obj) +{ + NMHashState h; + + if (!obj) + return 0; + + nm_hash_init (&h, 914932607u); + nmp_object_id_hash_update (obj, &h); + return nm_hash_complete (&h); +} + +#define _vt_cmd_plobj_id_hash_update(type, plat_type, cmd) \ +static void \ +_vt_cmd_plobj_id_hash_update_##type (const NMPlatformObject *_obj, NMHashState *h) \ { \ const plat_type *const obj = (const plat_type *) _obj; \ - guint hash; \ { cmd; } \ - return hash; \ } -_vt_cmd_plobj_id_hash (link, NMPlatformLink, { - hash = (guint) 3982791431u; - hash = hash + ((guint) obj->ifindex); +_vt_cmd_plobj_id_hash_update (link, NMPlatformLink, { + nm_hash_update_val (h, obj->ifindex); }) -_vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { - hash = (guint) 3591309853u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->address); - - /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen))); +_vt_cmd_plobj_id_hash_update (ip4_address, NMPlatformIP4Address, { + nm_hash_update_vals (h, + obj->ifindex, + obj->plen, + obj->address, + /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ + nm_utils_ip4_address_clear_host_address (obj->peer_address, obj->plen)); }) -_vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { - hash = (guint) 2907861637u; - hash = hash + ((guint) obj->ifindex); - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - hash = hash * 33 + _id_hash_ip6_addr (&obj->address); +_vt_cmd_plobj_id_hash_update (ip6_address, NMPlatformIP6Address, { + nm_hash_update_vals (h, + obj->ifindex, + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + obj->address); }) -_vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { - hash = (guint) 2569857221u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->metric); - hash = hash * 33 + ((guint) nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); +_vt_cmd_plobj_id_hash_update (ip4_route, NMPlatformIP4Route, { + nm_platform_ip4_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) -_vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { - hash = (guint) 3999787007u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->metric); - hash = hash * 33 + ({ - struct in6_addr n1; - _id_hash_ip6_addr (nm_utils_ip6_address_clear_host_address (&n1, &obj->network, obj->plen)); - }); +_vt_cmd_plobj_id_hash_update (ip6_route, NMPlatformIP6Route, { + nm_platform_ip6_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) +static inline void +_vt_cmd_plobj_hash_update_ip4_route (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_ip4_route_hash_update ((const NMPlatformIP4Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); +} + +static inline void +_vt_cmd_plobj_hash_update_ip6_route (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_ip6_route_hash_update ((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); +} + gboolean nmp_object_is_alive (const NMPObject *obj) { @@ -958,365 +1202,92 @@ _vt_cmd_obj_is_visible_link (const NMPObject *obj) /*****************************************************************************/ -_NM_UTILS_LOOKUP_DEFINE (static, _nmp_cache_id_size_by_type, NMPCacheIdType, guint, - NM_UTILS_LOOKUP_DEFAULT (({ nm_assert_not_reached (); (guint) 0; })), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, nm_offsetofend (NMPCacheId, link_by_ifname)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, nm_offsetofend (NMPCacheId, routes_by_destination_ip4)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, nm_offsetofend (NMPCacheId, routes_by_destination_ip6)), - NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_CACHE_ID_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM_IGNORE (__NMP_CACHE_ID_TYPE_MAX), -); - -gboolean -nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b) -{ - if (a->_id_type != b->_id_type) - return FALSE; - return memcmp (a, b, _nmp_cache_id_size_by_type (a->_id_type)) == 0; -} - -guint -nmp_cache_id_hash (const NMPCacheId *id) -{ - guint hash = 5381; - guint i, n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - for (i = 0; i < n; i++) - hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */ - return hash; -} - -NMPCacheId * -nmp_cache_id_clone (const NMPCacheId *id) -{ - NMPCacheId *id2; - guint n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - id2 = g_slice_alloc (n); - memcpy (id2, id, n); - return id2; -} +static const guint8 _supported_cache_ids_link[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, + 0, +}; -void -nmp_cache_id_destroy (NMPCacheId *id) -{ - guint n; +static const guint8 _supported_cache_ids_ipx_address[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, + 0, +}; - n = _nmp_cache_id_size_by_type (id->_id_type); - g_slice_free1 (n, id); -} +static const guint8 _supported_cache_ids_ipx_route[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, + NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + 0, +}; /*****************************************************************************/ static void -_nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type) -{ - /* there is no need to set the entire @id to zero when - * initializing the ID. - * - * First, depending on the @id_type only part of the - * @id is actually used (_nmp_cache_id_size_by_type). - * - * Second, the nmp_cache_id_init_*() *MUST* anyway make sure - * that all relevant fields are set. Since it happens that - * all structs have the packed attribute, there are no holes - * due to alignment, and it becomes simple for nmp_cache_id_init_*() - * to ensure that all fields are set. */ - -#if NM_MORE_ASSERTS - nm_assert (id); - { - guint i; - - /* initialized with some bogus canary to hopefully detect when we miss - * to initialize a field of the cache-id. */ - for (i = 0; i < sizeof (*id); i++) { - ((char *) id)[i] = GPOINTER_TO_UINT (id) ^ i; - } - } -#endif - - id->_id_type = id_type; -} - -NMPCacheId * -nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only) +_vt_dedup_obj_destroy (NMDedupMultiObj *obj) { - _nmp_cache_id_init (id, visible_only - ? NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY - : NMP_CACHE_ID_TYPE_OBJECT_TYPE); - id->object_type.obj_type = obj_type; - return id; -} - -NMPCacheId * -nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, - NMPObjectType obj_type, - int ifindex) -{ - g_return_val_if_fail (NM_IN_SET (obj_type, - NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); - - if (ifindex <= 0) - return nmp_cache_id_init_object_type (id, obj_type, TRUE); - - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX); - id->object_type_by_ifindex.obj_type = obj_type; - memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_routes_visible (NMPCacheId *id, - NMPObjectType obj_type, - gboolean with_default, - gboolean with_non_default, - int ifindex) -{ - g_return_val_if_fail (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); - - if (with_default) { - if (with_non_default) { - if (ifindex <= 0) - return nmp_cache_id_init_object_type (id, obj_type, TRUE); - return nmp_cache_id_init_addrroute_visible_by_ifindex (id, obj_type, ifindex); - } - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT); - } else if (with_non_default) - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT); - else - g_return_val_if_reached (NULL); - - id->object_type_by_ifindex.obj_type = obj_type; - memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_link_by_ifname (NMPCacheId *id, - const char *ifname) -{ - gsize l; - - if ( !ifname - || (l = strlen (ifname)) > sizeof (id->link_by_ifname.ifname_short)) - g_return_val_if_reached (id); - - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME); + NMPObject *o = (NMPObject *) obj; + const NMPClass *klass; - memset (id->link_by_ifname.ifname_short, 0, sizeof (id->link_by_ifname.ifname_short)); - /* the trailing NUL is dropped!! */ - memcpy (id->link_by_ifname.ifname_short, ifname, l); + nm_assert (o->parent._ref_count == 0); + nm_assert (!o->parent._multi_idx); - return id; + klass = o->_class; + if (klass->cmd_obj_dispose) + klass->cmd_obj_dispose (o); + g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), o); } -NMPCacheId * -nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, - guint32 network, - guint8 plen, - guint32 metric) +static const NMDedupMultiObj * +_vt_dedup_obj_clone (const NMDedupMultiObj *obj) { - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4); - id->routes_by_destination_ip4.plen = plen; - memcpy (&id->routes_by_destination_ip4._misaligned_metric, &metric, sizeof (guint32)); - memcpy (&id->routes_by_destination_ip4._misaligned_network, &network, sizeof (guint32)); - return id; + return (const NMDedupMultiObj *) nmp_object_clone ((const NMPObject *) obj, FALSE); } -NMPCacheId * -nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, - const struct in6_addr *network, - guint8 plen, - guint32 metric) -{ - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6); - id->routes_by_destination_ip4.plen = plen; - memcpy (&id->routes_by_destination_ip6._misaligned_metric, &metric, sizeof (guint32)); - memcpy (&id->routes_by_destination_ip6._misaligned_network, network ?: &nm_ip_addr_zero.addr6, sizeof (struct in6_addr)); - return id; -} +#define DEDUP_MULTI_OBJ_CLASS_INIT() \ + { \ + .obj_clone = _vt_dedup_obj_clone, \ + .obj_destroy = _vt_dedup_obj_destroy, \ + .obj_full_hash_update = (void (*)(const NMDedupMultiObj *obj, NMHashState *h)) nmp_object_hash_update, \ + .obj_full_equal = (gboolean (*)(const NMDedupMultiObj *obj_a, const NMDedupMultiObj *obj_b)) nmp_object_equal, \ + } /*****************************************************************************/ -static gboolean -_nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +static NMDedupMultiIdxType * +_idx_type_get (const NMPCache *cache, NMPCacheIdType cache_id_type) { - const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + nm_assert (cache); + nm_assert (cache_id_type > NMP_CACHE_ID_TYPE_NONE); + nm_assert (cache_id_type <= NMP_CACHE_ID_TYPE_MAX); + nm_assert ((int) cache_id_type - 1 >= 0); + nm_assert ((int) cache_id_type - 1 < G_N_ELEMENTS (cache->idx_types)); - switch (id_type) { - case NMP_CACHE_ID_TYPE_OBJECT_TYPE: - *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, FALSE); - return TRUE; - case NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY: - if (nmp_object_is_visible (obj)) - *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, TRUE); - else - *out_id = NULL; - return TRUE; - default: - return klass->cmd_obj_init_cache_id - && klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); - } + return (NMDedupMultiIdxType *) &cache->idx_types[cache_id_type - 1]; } -static const guint8 _supported_cache_ids_link[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - 0, -}; - -static gboolean -_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +gboolean +nmp_cache_use_udev_get (const NMPCache *cache) { - switch (id_type) { - case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: - if (obj->link.name[0]) { - *out_id = nmp_cache_id_init_link_by_ifname (id, obj->link.name); - return TRUE; - } - break; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - -static const guint8 _supported_cache_ids_ipx_address[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - 0, -}; - -static gboolean -_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (nmp_object_is_visible (obj)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); - return TRUE; - } - break; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - -static const guint8 _supported_cache_ids_ip4_route[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, - 0, -}; - -static const guint8 _supported_cache_ids_ip6_route[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, - 0, -}; + g_return_val_if_fail (cache, TRUE); -static gboolean -_vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (nmp_object_is_visible (obj)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: - if ( nmp_object_is_visible (obj) - && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: - if ( nmp_object_is_visible (obj) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT: - if ( nmp_object_is_visible (obj) - && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT: - if ( nmp_object_is_visible (obj) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4: - if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { - *out_id = nmp_cache_id_init_routes_by_destination_ip4 (id, obj->ip4_route.network, obj->ip_route.plen, obj->ip_route.metric); - return TRUE; - } - return FALSE; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6: - if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) { - *out_id = nmp_cache_id_init_routes_by_destination_ip6 (id, &obj->ip6_route.network, obj->ip_route.plen, obj->ip_route.metric); - return TRUE; - } - return FALSE; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; + return cache->use_udev; } /*****************************************************************************/ gboolean -nmp_cache_use_udev_get (const NMPCache *cache) +nmp_cache_link_connected_for_slave (int ifindex_master, const NMPObject *slave) { - g_return_val_if_fail (cache, TRUE); + nm_assert (NMP_OBJECT_GET_TYPE (slave) == NMP_OBJECT_TYPE_LINK); - return cache->use_udev; + return ifindex_master > 0 + && slave->link.master == ifindex_master + && slave->link.connected + && nmp_object_is_visible (slave); } -/*****************************************************************************/ - /** * nmp_cache_link_connected_needs_toggle: * @cache: the platform cache @@ -1341,9 +1312,7 @@ nmp_cache_use_udev_get (const NMPCache *cache) gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave) { - const NMPlatformLink *const *links; gboolean is_lower_up = FALSE; - guint len, i; if ( !master || NMP_OBJECT_GET_TYPE (master) != NMP_OBJECT_TYPE_LINK @@ -1361,27 +1330,23 @@ nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *m potential_slave = NULL; if ( potential_slave - && nmp_object_is_visible (potential_slave) - && potential_slave->link.ifindex > 0 - && potential_slave->link.master == master->link.ifindex - && potential_slave->link.connected) { + && nmp_cache_link_connected_for_slave (master->link.ifindex, potential_slave)) is_lower_up = TRUE; - } else { - NMPCacheId cache_id; - - links = (const NMPlatformLink *const *) nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, FALSE), &len); - for (i = 0; i < len; i++) { - const NMPlatformLink *link = links[i]; + else { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *link = NULL; + + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_LINK)), + &link) { const NMPObject *obj = NMP_OBJECT_UP_CAST ((NMPlatformObject *) link); - nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST ((NMPlatformObject *) link)) == NMP_OBJECT_TYPE_LINK); - if ( (!potential_slave || potential_slave->link.ifindex != link->ifindex) && ignore_slave != obj - && link->ifindex > 0 - && link->master == master->link.ifindex - && nmp_object_is_visible (obj) - && link->connected) { + && nmp_cache_link_connected_for_slave (master->link.ifindex, obj)) { is_lower_up = TRUE; break; } @@ -1424,95 +1389,293 @@ nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int mas /*****************************************************************************/ -const NMPlatformObject *const * -nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len) +static const NMDedupMultiEntry * +_lookup_entry_with_idx_type (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj) { - return (const NMPlatformObject *const *) nm_multi_index_lookup (cache->idx_multi, - (const NMMultiIndexId *) cache_id, - out_len); + const NMDedupMultiEntry *entry; + + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + entry = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + _idx_type_get (cache, cache_id_type), + obj); + nm_assert (!entry + || ( NMP_OBJECT_IS_VALID (entry->obj) + && NMP_OBJECT_GET_CLASS (entry->obj) == NMP_OBJECT_GET_CLASS (obj))); + return entry; } -GArray * -nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id) +static const NMDedupMultiEntry * +_lookup_entry (const NMPCache *cache, const NMPObject *obj) { - const NMPClass *klass = nmp_class_from_type (obj_type); - guint len, i; - const NMPlatformObject *const *objects; - GArray *array; - - g_return_val_if_fail (klass, NULL); + return _lookup_entry_with_idx_type (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj); +} - objects = nmp_cache_lookup_multi (cache, cache_id, &len); - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); +const NMDedupMultiEntry * +nmp_cache_lookup_entry_with_idx_type (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + g_return_val_if_fail (cache, NULL); + g_return_val_if_fail (obj, NULL); + g_return_val_if_fail (cache_id_type > NMP_CACHE_ID_TYPE_NONE && cache_id_type <= NMP_CACHE_ID_TYPE_MAX, NULL); - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[i])) == klass); - g_array_append_vals (array, objects[i], 1); - } - return array; + return _lookup_entry_with_idx_type (cache, cache_id_type, obj); } -const NMPObject * -nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) +const NMDedupMultiEntry * +nmp_cache_lookup_entry (const NMPCache *cache, const NMPObject *obj) { + g_return_val_if_fail (cache, NULL); g_return_val_if_fail (obj, NULL); - return g_hash_table_lookup (cache->idx_main, obj); + return _lookup_entry (cache, obj); } -const NMPObject * -nmp_cache_lookup_link (const NMPCache *cache, int ifindex) +const NMDedupMultiEntry * +nmp_cache_lookup_entry_link (const NMPCache *cache, int ifindex) { NMPObject obj_needle; - return nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); + g_return_val_if_fail (cache, NULL); + g_return_val_if_fail (ifindex > 0, NULL); + + nmp_object_stackinit_id_link (&obj_needle, ifindex); + return _lookup_entry (cache, &obj_needle); } -/** - * nmp_cache_find_other_route_for_same_destination: - * @cache: - * @route: - * - * Look into the cache whether there is a route to the same destination, - * in terms of network/plen,metric. - * - * Returns: (transfer none): the first found route object from the cache - * that has the same (network/plen,metric) values as @route, but has different - * ID. Or %NULL, if no such route exists. - */ const NMPObject * -nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route) +nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) { - NMPCacheId cache_id; - const NMPlatformObject *const *list; + return nm_dedup_multi_entry_get_obj (nmp_cache_lookup_entry (cache, obj)); +} +const NMPObject * +nmp_cache_lookup_link (const NMPCache *cache, int ifindex) +{ + return nm_dedup_multi_entry_get_obj (nmp_cache_lookup_entry_link (cache, ifindex)); +} + +/*****************************************************************************/ + +const NMDedupMultiHeadEntry * +nmp_cache_lookup_all (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *select_obj) +{ nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (select_obj)); - switch (NMP_OBJECT_GET_TYPE (route)) { + return nm_dedup_multi_index_lookup_head (cache->multi_idx, + _idx_type_get (cache, cache_id_type), + select_obj); +} + +static const NMPLookup * +_L (const NMPLookup *lookup) +{ +#if NM_MORE_ASSERTS + DedupMultiIdxType idx_type; + + nm_assert (lookup); + _dedup_multi_idx_type_init (&idx_type, lookup->cache_id_type); + nm_assert (idx_type.parent.klass->idx_obj_partitionable ((NMDedupMultiIdxType *) &idx_type, (NMDedupMultiObj *) &lookup->selector_obj)); +#endif + return lookup; +} + +const NMPLookup * +nmp_lookup_init_obj_type (NMPLookup *lookup, + NMPObjectType obj_type) +{ + NMPObject *o; + + nm_assert (lookup); + + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: - nmp_cache_id_init_routes_by_destination_ip4 (&cache_id, route->ip4_route.network, route->ip_route.plen, route->ip_route.metric); - break; case NMP_OBJECT_TYPE_IP6_ROUTE: - nmp_cache_id_init_routes_by_destination_ip6 (&cache_id, &route->ip6_route.network, route->ip_route.plen, route->ip_route.metric); - break; + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; + return _L (lookup); default: + nm_assert_not_reached (); + return NULL; + } +} + +const NMPLookup * +nmp_lookup_init_link_by_ifname (NMPLookup *lookup, + const char *ifname) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (ifname); + nm_assert (strlen (ifname) < IFNAMSIZ); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_LINK); + if (g_strlcpy (o->link.name, ifname, sizeof (o->link.name)) >= sizeof (o->link.name)) g_return_val_if_reached (NULL); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_LINK_BY_IFNAME; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_addrroute (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + if (ifindex <= 0) { + return nmp_lookup_init_obj_type (lookup, + obj_type); } - list = nmp_cache_lookup_multi (cache, &cache_id, NULL); - if (list) { - for (; *list; list++) { - const NMPObject *candidate = NMP_OBJECT_UP_CAST (*list); + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + o->object.ifindex = ifindex; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_route_default (NMPLookup *lookup, + NMPObjectType obj_type) +{ + NMPObject *o; - nm_assert (NMP_OBJECT_GET_CLASS (route) == NMP_OBJECT_GET_CLASS (candidate)); + nm_assert (lookup); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - if (!nmp_object_id_equal (route, candidate)) - return candidate; - } + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + o->object.ifindex = 1; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_route_by_weak_id (NMPLookup *lookup, + const NMPObject *obj) +{ + const NMPlatformIP4Route *r4; + const NMPlatformIP6Route *r6; + + nm_assert (lookup); + + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); + return nmp_lookup_init_ip4_route_by_weak_id (lookup, + r4->network, + r4->plen, + r4->metric, + r4->tos); + case NMP_OBJECT_TYPE_IP6_ROUTE: + r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); + return nmp_lookup_init_ip6_route_by_weak_id (lookup, + &r6->network, + r6->plen, + r6->metric, + &r6->src, + r6->src_plen); + default: + nm_assert_not_reached (); + return NULL; } - return NULL; } +const NMPLookup * +nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos) +{ + NMPObject *o; + + nm_assert (lookup); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip4_route.network = network; + o->ip4_route.tos = tos; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + NMPObject *o; + + nm_assert (lookup); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip6_route.network = *network; + if (src) + o->ip6_route.src = *src; + o->ip6_route.src_plen = src_plen; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; + return _L (lookup); +} + +/*****************************************************************************/ + +GArray * +nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, + NMPObjectType obj_type, + gboolean visible_only) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + NMDedupMultiIter iter; + const NMPObject *o; + GArray *array; + + g_return_val_if_fail (klass, NULL); + + array = g_array_sized_new (FALSE, FALSE, + klass->sizeof_public, + head_entry ? head_entry->len : 0); + nmp_cache_iter_for_each (&iter, + head_entry, + &o) { + nm_assert (NMP_OBJECT_GET_CLASS (o) == klass); + if ( visible_only + && !nmp_object_is_visible (o)) + continue; + g_array_append_vals (array, &o->object, 1); + } + return array; +} + +/*****************************************************************************/ + const NMPObject * nmp_cache_lookup_link_full (const NMPCache *cache, int ifindex, @@ -1524,9 +1687,10 @@ nmp_cache_lookup_link_full (const NMPCache *cache, { NMPObject obj_needle; const NMPObject *obj; - const NMPlatformObject *const *list; - guint i, len; - NMPCacheId cache_id, *p_cache_id; + NMDedupMultiIter iter; + const NMDedupMultiHeadEntry *head_entry; + const NMPlatformLink *link = NULL; + NMPLookup lookup; if (ifindex > 0) { obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); @@ -1541,25 +1705,21 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } else if (!ifname && !match_fn) return NULL; else { - if ( ifname - && strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) { - p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname); - ifname = NULL; - } else { - p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only); - visible_only = FALSE; - } + if (ifname) { + if (strlen (ifname) >= IFNAMSIZ) + return NULL; + nmp_lookup_init_link_by_ifname (&lookup, ifname); + } else + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); - list = nmp_cache_lookup_multi (cache, p_cache_id, &len); - for (i = 0; i < len; i++) { - obj = NMP_OBJECT_UP_CAST (list[i]); + head_entry = nmp_cache_lookup (cache, &lookup); + nmp_cache_iter_for_each_link (&iter, head_entry, &link) { + obj = NMP_OBJECT_UP_CAST (link); if (visible_only && !nmp_object_is_visible (obj)) continue; if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) continue; - if (ifname && strcmp (ifname, obj->link.name)) - continue; if (match_fn && !match_fn (obj, user_data)) continue; @@ -1569,455 +1729,710 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } } -GHashTable * -nmp_cache_lookup_all_to_hash (const NMPCache *cache, - NMPCacheId *cache_id, - GHashTable *hash) -{ - NMMultiIndexIdIter iter; - gpointer plobj; - - nm_multi_index_id_iter_init (&iter, cache->idx_multi, (const NMMultiIndexId *) cache_id); - - if (nm_multi_index_id_iter_next (&iter, &plobj)) { - if (!hash) - hash = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - do { - g_hash_table_add (hash, nmp_object_ref (NMP_OBJECT_UP_CAST (plobj))); - } while (nm_multi_index_id_iter_next (&iter, &plobj)); - } - - return hash; -} - /*****************************************************************************/ static void -_nmp_cache_update_cache (NMPCache *cache, NMPObject *obj, gboolean remove) -{ - const guint8 *id_type; - - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - NMPCacheId cache_id_storage; - const NMPCacheId *cache_id; - - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id)) - continue; - if (!cache_id) - continue; - - /* We don't put @obj itself into the multi index, but &obj->object. As of now, all - * users expect a pointer to NMPlatformObject, not NMPObject. - * You can use NMP_OBJECT_UP_CAST() to retrieve the original @obj pointer. - * - * If need be, we could determine based on @id_type which pointer we want to store. */ - - if (remove) { - if (!nm_multi_index_remove (cache->idx_multi, &cache_id->base, &obj->object)) - g_assert_not_reached (); +_idxcache_update_other_cache_ids (NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj_old, + const NMPObject *obj_new, + gboolean is_dump) +{ + const NMDedupMultiEntry *entry_new; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_order; + NMDedupMultiIdxType *idx_type; + + nm_assert (obj_new || obj_old); + nm_assert (!obj_new || NMP_OBJECT_GET_TYPE (obj_new) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_old || NMP_OBJECT_GET_TYPE (obj_old) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_new) == NMP_OBJECT_GET_CLASS (obj_old)); + nm_assert (!obj_old || !obj_new || !nmp_object_equal (obj_new, obj_old)); + nm_assert (!obj_new || obj_new == nm_dedup_multi_index_obj_find (cache->multi_idx, obj_new)); + nm_assert (!obj_old || obj_old == nm_dedup_multi_index_obj_find (cache->multi_idx, obj_old)); + + idx_type = _idx_type_get (cache, cache_id_type); + + if (obj_old) { + entry_old = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + idx_type, + obj_old); + if (!obj_new) { + if (entry_old) + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); + return; + } + } else + entry_old = NULL; + + if (obj_new) { + if ( obj_old + && nm_dedup_multi_idx_type_id_equal (idx_type, obj_old, obj_new) + && nm_dedup_multi_idx_type_partition_equal (idx_type, obj_old, obj_new)) { + /* optimize. We just looked up the @obj_old entry and @obj_new compares equal + * according to idx_obj_id_equal(). entry_new is the same as entry_old. */ + entry_new = entry_old; } else { - if (!nm_multi_index_add (cache->idx_multi, &cache_id->base, &obj->object)) - g_assert_not_reached (); + entry_new = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + idx_type, + obj_new); } - } -} -static void -_nmp_cache_update_add (NMPCache *cache, NMPObject *obj) -{ - nm_assert (!obj->is_cached); - nmp_object_ref (obj); - nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); - if (!nm_g_hash_table_add (cache->idx_main, obj)) - g_assert_not_reached (); - obj->is_cached = TRUE; - _nmp_cache_update_cache (cache, obj, FALSE); -} + if (entry_new) + entry_order = entry_new; + else if ( entry_old + && nm_dedup_multi_idx_type_partition_equal (idx_type, entry_old->obj, obj_new)) + entry_order = entry_old; + else + entry_order = NULL; + nm_dedup_multi_index_add_full (cache->multi_idx, + idx_type, + obj_new, + is_dump + ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE + : NM_DEDUP_MULTI_IDX_MODE_APPEND, + is_dump + ? NULL + : entry_order, + entry_new ?: NM_DEDUP_MULTI_ENTRY_MISSING, + entry_new ? entry_new->head : (entry_order ? entry_order->head : NULL), + &entry_new, + NULL); -static void -_nmp_cache_update_remove (NMPCache *cache, NMPObject *obj) -{ - nm_assert (obj->is_cached); - _nmp_cache_update_cache (cache, obj, TRUE); - obj->is_cached = FALSE; - if (!g_hash_table_remove (cache->idx_main, obj)) - g_assert_not_reached (); +#if NM_MORE_ASSERTS + if (entry_new) { + nm_assert (idx_type->klass->idx_obj_partitionable); + nm_assert (idx_type->klass->idx_obj_partition_equal); + nm_assert (idx_type->klass->idx_obj_partitionable (idx_type, entry_new->obj)); + nm_assert (idx_type->klass->idx_obj_partition_equal (idx_type, (gpointer) obj_new, entry_new->obj)); + } +#endif + } else + entry_new = NULL; - /* @obj is possibly a dangling pointer at this point. No problem, multi-index doesn't dereference. */ - nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); + if ( entry_old + && entry_old != entry_new) + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); } static void -_nmp_cache_update_update (NMPCache *cache, NMPObject *obj, const NMPObject *new) +_idxcache_update (NMPCache *cache, + const NMDedupMultiEntry *entry_old, + NMPObject *obj_new, + gboolean is_dump, + const NMDedupMultiEntry **out_entry_new) { - const guint8 *id_type; + const NMPClass *klass; + const guint8 *i_idx_type; + NMDedupMultiIdxType *idx_type_o = _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE); + const NMDedupMultiEntry *entry_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + /* we update an object in the cache. + * + * Note that @entry_old MUST be what is currently tracked in multi_idx, and it must + * have the same ID as @obj_new. */ - nm_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (new)); - nm_assert (obj->is_cached); - nm_assert (!new->is_cached); + nm_assert (cache); + nm_assert (entry_old || obj_new); + nm_assert (!obj_new || nmp_object_is_alive (obj_new)); + nm_assert (!entry_old || entry_old == nm_dedup_multi_index_lookup_obj (cache->multi_idx, idx_type_o, entry_old->obj)); + nm_assert (!obj_new || entry_old == nm_dedup_multi_index_lookup_obj (cache->multi_idx, idx_type_o, obj_new)); + nm_assert (!entry_old || entry_old->head->idx_type == idx_type_o); + nm_assert ( !entry_old + || !obj_new + || nm_dedup_multi_idx_type_partition_equal (idx_type_o, entry_old->obj, obj_new)); + nm_assert ( !entry_old + || !obj_new + || nm_dedup_multi_idx_type_id_equal (idx_type_o, entry_old->obj, obj_new)); + nm_assert ( !entry_old + || !obj_new + || ( obj_new->parent.klass == ((const NMPObject *) entry_old->obj)->parent.klass + && !obj_new->parent.klass->obj_full_equal ((NMDedupMultiObj *) obj_new, entry_old->obj))); + + /* keep a reference to the pre-existing entry */ + if (entry_old) + obj_old = nmp_object_ref (entry_old->obj); + + /* first update the main index NMP_CACHE_ID_TYPE_OBJECT_TYPE. + * We already know the pre-existing @entry old, so all that + * nm_dedup_multi_index_add_full() effectively does, is update the + * obj reference. + * + * We also get the new boxed object, which we need below. */ + if (obj_new) { + nm_auto_nmpobj NMPObject *obj_old2 = NULL; + + nm_dedup_multi_index_add_full (cache->multi_idx, + idx_type_o, + obj_new, + is_dump + ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE + : NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, + NULL, + &entry_new, + (const NMDedupMultiObj **) &obj_old2); + nm_assert (entry_new); + nm_assert (obj_old == obj_old2); + nm_assert (!entry_old || entry_old == entry_new); + } else + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - NMPCacheId cache_id_storage_obj, cache_id_storage_new; - const NMPCacheId *cache_id_obj, *cache_id_new; + /* now update all other indexes. We know the previously boxed entry, and the + * newly boxed one. */ + klass = NMP_OBJECT_GET_CLASS (entry_new ? entry_new->obj : obj_old); + for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) { + NMPCacheIdType id_type = *i_idx_type; - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage_obj, &cache_id_obj)) + if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) continue; - if (!_nmp_object_init_cache_id (new, *id_type, &cache_id_storage_new, &cache_id_new)) - g_assert_not_reached (); - if (!nm_multi_index_move (cache->idx_multi, (NMMultiIndexId *) cache_id_obj, (NMMultiIndexId *) cache_id_new, &obj->object)) - g_assert_not_reached (); + _idxcache_update_other_cache_ids (cache, id_type, + obj_old, + entry_new ? entry_new->obj : NULL, + is_dump); } - nmp_object_copy (obj, new, FALSE); + + NM_SET_OUT (out_entry_new, entry_new); } NMPCacheOpsType -nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_remove (NMPCache *cache, + const NMPObject *obj_needle, + gboolean equals_by_ptr, + gboolean only_dirty, + const NMPObject **out_obj_old) { - NMPObject *old; + const NMDedupMultiEntry *entry_old; + const NMPObject *obj_old; - nm_assert (NMP_OBJECT_IS_VALID (obj)); + entry_old = _lookup_entry (cache, obj_needle); - old = g_hash_table_lookup (cache->idx_main, obj); - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); return NMP_CACHE_OPS_UNCHANGED; } - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - if (equals_by_ptr && old != obj) { + obj_old = entry_old->obj; + + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + + if ( equals_by_ptr + && obj_old != obj_needle) { /* We found an identical object, but we only delete it if it's the same pointer as - * @obj. */ + * @obj_needle. */ return NMP_CACHE_OPS_UNCHANGED; } - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if ( only_dirty + && !entry_old->dirty) { + /* the entry is not dirty. Skip. */ + return NMP_CACHE_OPS_UNCHANGED; + } + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); return NMP_CACHE_OPS_REMOVED; } NMPCacheOpsType -nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_remove_netlink (NMPCache *cache, + const NMPObject *obj_needle, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new = NULL; + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + + entry_old = _lookup_entry (cache, obj_needle); + + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } + + obj_old = entry_old->obj; + if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { /* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be * removed from netlink. Link objects are alive without being in netlink when they * have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink * flag. */ - old = (NMPObject *) nmp_cache_lookup_link (cache, obj_needle->link.ifindex); - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; - return NMP_CACHE_OPS_UNCHANGED; - } - - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (!old->_link.netlink.is_in_netlink) { - nm_assert (old->_link.udev.device); + if (!obj_old->_link.netlink.is_in_netlink) { + nm_assert (obj_old->_link.udev.device); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; } - if (!old->_link.udev.device) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if (!obj_old->_link.udev.device) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_REMOVED; } - obj = nmp_object_clone (old, FALSE); - obj->_link.netlink.is_in_netlink = FALSE; + obj_new = nmp_object_clone (obj_old, FALSE); + obj_new->_link.netlink.is_in_netlink = FALSE; - _nmp_object_fixup_link_master_connected (obj, cache); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + _nmp_object_fixup_link_master_connected (&obj_new, NULL, cache); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + _idxcache_update (cache, + entry_old, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; - } else - return nmp_cache_remove (cache, obj_needle, FALSE, out_obj, out_was_visible, pre_hook, user_data); + } + + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, NULL); + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + return NMP_CACHE_OPS_REMOVED; } /** * nmp_cache_update_netlink: * @cache: the platform cache - * @obj: a #NMPObject instance as received from netlink and created via + * @obj_hand_over: a #NMPObject instance as received from netlink and created via * nmp_object_from_nl(). Especially for link, it must not have the udev * replated fields set. * This instance will be modified and might be put into the cache. When * calling nmp_cache_update_netlink() you hand @obj over to the cache. * Except, that the cache will increment the ref count as appropriate. You * must still unref the obj to release your part of the ownership. - * @out_obj: (allow-none): (out): return the object instance that is inside - * the cache. If you specify non %NULL, you must always unref the returned - * instance. If the return value indicates that the object was removed, - * the object is no longer in the cache. Even if the return value indicates - * that the object was unchanged, it will still return @out_obj -- if - * such an object is in the cache. - * @out_was_visible: (allow-none): (out): whether the object was visible before - * the update operation. - * @pre_hook: (allow-none): a callback *before* the object gets updated. You cannot - * influence the outcome and must not do anything beyong inspecting the changes. - * @user_data: + * @is_dump: whether this update comes during a dump of object of the same kind. + * kernel dumps objects in a certain order, which matters especially for routes. + * Before a dump we mark all objects as dirty, and remove all untouched objects + * afterwards. Hence, during a dump, every update should move the object to the + * end of the list, to obtain the correct order. That means, to use NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, + * instead of NM_DEDUP_MULTI_IDX_MODE_APPEND. + * @out_obj_old: (allow-none): (out): return the object with same ID as @obj_hand_over, + * that was in the cache before update. If an object is returned, the caller must + * unref it afterwards. + * @out_obj_new: (allow-none): (out): return the object from the cache after update. + * The caller must unref this object. * * Returns: how the cache changed. + * + * Even if there was no change in the cace (NMP_CACHE_OPS_UNCHANGED), @out_obj_old + * and @out_obj_new will be set accordingly. **/ NMPCacheOpsType -nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_update_netlink (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!NMP_OBJECT_IS_STACKINIT (obj)); - nm_assert (!obj->is_cached); + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + const NMPObject *obj_old; + gboolean is_alive; + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj_hand_over)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj_hand_over)); /* A link object from netlink must have the udev related fields unset. * We could implement to handle that, but there is no need to support such * a use-case */ - nm_assert (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_LINK || - ( !obj->_link.udev.device - && !obj->link.driver)); + nm_assert (NMP_OBJECT_GET_TYPE (obj_hand_over) != NMP_OBJECT_TYPE_LINK || + ( !obj_hand_over->_link.udev.device + && !obj_hand_over->link.driver)); + nm_assert (nm_dedup_multi_index_obj_find (cache->multi_idx, obj_hand_over) != obj_hand_over); - old = g_hash_table_lookup (cache->idx_main, obj); + entry_old = _lookup_entry (cache, obj_hand_over); - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + if (!entry_old) { - if (!old) { - if (!nmp_object_is_alive (obj)) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, NULL); - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - _nmp_object_fixup_link_master_connected (obj, cache); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + if (!nmp_object_is_alive (obj_hand_over)) { + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; } - if (out_obj) - *out_obj = nmp_object_ref (obj); + if (NMP_OBJECT_GET_TYPE (obj_hand_over) == NMP_OBJECT_TYPE_LINK) { + _nmp_object_fixup_link_master_connected (&obj_hand_over, NULL, cache); + _nmp_object_fixup_link_udev_fields (&obj_hand_over, NULL, cache->use_udev); + } - if (pre_hook) - pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); - _nmp_cache_update_add (cache, obj); + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_ADDED; - } else if (old == obj) { - /* updating a cached object inplace is not supported because the object contributes to hash-key - * for NMMultiIndex. Modifying an object that is inside NMMultiIndex means that these - * keys change. - * The problem is, that for a given object NMMultiIndex does not support (efficient) - * reverse lookup to get all the NMPCacheIds to which it belongs. If that would be implemented, - * it would be possible to implement inplace-update. - * - * There is an un-optimized reverse lookup via nm_multi_index_iter_init(), but we don't want - * that because we might have a large number of indexes to search. - * - * We could add efficient reverse lookup by adding a reverse index to NMMultiIndex. But that - * also adds some cost to support an (uncommon?) usage pattern. - * - * Instead we just don't support it, instead we expect the user to - * create a new instance from netlink. - * - * TL;DR: a cached object must never be modified. - */ - g_assert_not_reached (); - } else { - gboolean is_alive = FALSE; - - nm_assert (old->is_cached); - - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - if (!obj->_link.netlink.is_in_netlink) { - if (!old->_link.netlink.is_in_netlink) { - nm_assert (old->_link.udev.device); - return NMP_CACHE_OPS_UNCHANGED; - } - if (old->_link.udev.device) { - /* @obj is not in netlink. - * - * This is similar to nmp_cache_remove_netlink(), but there we preserve the - * preexisting netlink properties. The use case of that is when kernel_get_object() - * cannot load an object (based on the id of a needle). - * - * Here we keep the data provided from @obj. The usecase is when receiving - * a valid @obj instance from netlink with RTM_DELROUTE. - */ - is_alive = TRUE; - } - } else - is_alive = TRUE; + } - if (is_alive) { - _nmp_object_fixup_link_master_connected (obj, cache); + obj_old = entry_old->obj; - /* Merge the netlink parts with what we have from udev. */ - udev_device_unref (obj->_link.udev.device); - obj->_link.udev.device = old->_link.udev.device ? udev_device_ref (old->_link.udev.device) : NULL; - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + if (NMP_OBJECT_GET_TYPE (obj_hand_over) == NMP_OBJECT_TYPE_LINK) { + if (!obj_hand_over->_link.netlink.is_in_netlink) { + if (!obj_old->_link.netlink.is_in_netlink) { + nm_assert (obj_old->_link.udev.device); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); + return NMP_CACHE_OPS_UNCHANGED; } + if (obj_old->_link.udev.device) { + /* @obj_hand_over is not in netlink. + * + * This is similar to nmp_cache_remove_netlink(), but there we preserve the + * preexisting netlink properties. The use case of that is when kernel_get_object() + * cannot load an object (based on the id of a needle). + * + * Here we keep the data provided from @obj_hand_over. The usecase is when receiving + * a valid @obj_hand_over instance from netlink with RTM_DELROUTE. + */ + is_alive = TRUE; + } else + is_alive = FALSE; } else - is_alive = nmp_object_is_alive (obj); + is_alive = TRUE; - if (!is_alive) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); - return NMP_CACHE_OPS_REMOVED; + if (is_alive) { + _nmp_object_fixup_link_master_connected (&obj_hand_over, NULL, cache); + + /* Merge the netlink parts with what we have from udev. */ + udev_device_unref (obj_hand_over->_link.udev.device); + obj_hand_over->_link.udev.device = obj_old->_link.udev.device ? udev_device_ref (obj_old->_link.udev.device) : NULL; + _nmp_object_fixup_link_udev_fields (&obj_hand_over, NULL, cache->use_udev); + + if (obj_hand_over->_link.netlink.lnk) { + nm_auto_nmpobj const NMPObject *lnk_old = obj_hand_over->_link.netlink.lnk; + + /* let's dedup/intern the lnk object. */ + obj_hand_over->_link.netlink.lnk = nm_dedup_multi_index_obj_intern (cache->multi_idx, lnk_old); + } } + } else + is_alive = nmp_object_is_alive (obj_hand_over); - if (nmp_object_equal (old, obj)) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); - return NMP_CACHE_OPS_UPDATED; + if (!is_alive) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_REMOVED; } + + if (nmp_object_equal (obj_old, obj_hand_over)) { + 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; + } + + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); + return NMP_CACHE_OPS_UPDATED; } NMPCacheOpsType -nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) -{ - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; +nmp_cache_update_netlink_route (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + guint16 nlmsgflags, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new, + const NMPObject **out_obj_replace, + gboolean *out_resync_required) +{ + NMDedupMultiIter iter; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + const NMDedupMultiEntry *entry_cur; + const NMDedupMultiEntry *entry_replace; + const NMDedupMultiHeadEntry *head_entry; + gboolean is_alive; + NMPCacheOpsType ops_type = NMP_CACHE_OPS_UNCHANGED; + gboolean resync_required; - old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj_hand_over)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj_hand_over)); + /* A link object from netlink must have the udev related fields unset. + * We could implement to handle that, but there is no need to support such + * a use-case */ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_hand_over), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (nm_dedup_multi_index_obj_find (cache->multi_idx, obj_hand_over) != obj_hand_over); - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + entry_old = _lookup_entry (cache, obj_hand_over); + entry_new = NULL; - if (!old) { - if (!udevice) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_old))); + + if (!entry_old) { - obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); - obj->link.ifindex = ifindex; - obj->_link.udev.device = udev_device_ref (udevice); + if (!nmp_object_is_alive (obj_hand_over)) + goto update_done; + + _idxcache_update (cache, + NULL, + obj_hand_over, + is_dump, + &entry_new); + ops_type = NMP_CACHE_OPS_ADDED; + goto update_done; + } - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + is_alive = nmp_object_is_alive (obj_hand_over); + + if (!is_alive) { + /* the update would make @entry_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + ops_type = NMP_CACHE_OPS_REMOVED; + goto update_done; + } + + if (nmp_object_equal (entry_old->obj, obj_hand_over)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto update_done; + } - nm_assert (nmp_object_is_alive (obj)); + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + ops_type = NMP_CACHE_OPS_UPDATED; - if (out_obj) - *out_obj = nmp_object_ref (obj); +update_done: + NM_SET_OUT (out_obj_new, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_new))); - if (pre_hook) - pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); - _nmp_cache_update_add (cache, obj); + /* a RTM_GETROUTE event may signal that another object was replaced. + * Find out whether that is the case and return it as @obj_replaced. + * + * Also, fixup the order of @entry_new within NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID + * index. For most parts, we don't care about the order of objects (including routes). + * But NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID we must keep in the correct order, to + * properly find @obj_replaced. */ + resync_required = FALSE; + entry_replace = NULL; + if (is_dump) { + goto out; + } + + if (!entry_new) { + if ( NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE) + && nmp_cache_lookup_all (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + obj_hand_over)) { + /* hm. @obj_hand_over was not added, meaning it was not alive. + * However, we track some other objects with the same weak-id. + * It's unclear what that means. To be sure, resync. */ + resync_required = TRUE; + } + goto out; + } + + entry_cur = _lookup_entry_with_idx_type (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + entry_new->obj); + if (!entry_cur) { + nm_assert_not_reached (); + goto out; + } + nm_assert (entry_cur->obj == entry_new->obj); + + head_entry = entry_cur->head; + nm_assert (head_entry == nmp_cache_lookup_all (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + entry_cur->obj)); + + if (head_entry->len == 1) { + /* there is only one object, and we expect it to be @obj_new. */ + nm_assert (nm_dedup_multi_head_entry_get_idx (head_entry, 0) == entry_cur); + goto out; + } + + switch (nlmsgflags & (NLM_F_REPLACE | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND)) { + case NLM_F_REPLACE: + /* ip route change */ + + /* get the first element (but skip @obj_new). */ + nm_dedup_multi_iter_init (&iter, head_entry); + if (!nm_dedup_multi_iter_next (&iter)) + nm_assert_not_reached (); + if (iter.current == entry_cur) { + if (!nm_dedup_multi_iter_next (&iter)) + nm_assert_not_reached (); + } + entry_replace = iter.current; + + nm_assert ( entry_replace + && entry_cur != entry_replace); + + nm_dedup_multi_entry_reorder (entry_cur, entry_replace, FALSE); + break; + case NLM_F_CREATE | NLM_F_APPEND: + /* ip route append */ + nm_dedup_multi_entry_reorder (entry_cur, NULL, TRUE); + break; + case NLM_F_CREATE: + /* ip route prepend */ + nm_dedup_multi_entry_reorder (entry_cur, NULL, FALSE); + break; + default: + /* this is an unexecpted case, probably a bug that we need to handle better. */ + resync_required = TRUE; + break; + } + +out: + NM_SET_OUT (out_obj_replace, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_replace))); + NM_SET_OUT (out_resync_required, resync_required); + return ops_type; +} + + +NMPCacheOpsType +nmp_cache_update_link_udev (NMPCache *cache, + int ifindex, + struct udev_device *udevice, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) +{ + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + + entry_old = nmp_cache_lookup_entry_link (cache, ifindex); + + if (!entry_old) { + if (!udevice) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } + + obj_new = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); + obj_new->link.ifindex = ifindex; + obj_new->_link.udev.device = udev_device_ref (udevice); + + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); + + _idxcache_update (cache, + NULL, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_ADDED; } else { - nm_assert (old->is_cached); - - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); + obj_old = entry_old->obj; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (old->_link.udev.device == udevice) + if (obj_old->_link.udev.device == udevice) { + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; + } - if (!udevice && !old->_link.netlink.is_in_netlink) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if (!udevice && !obj_old->_link.netlink.is_in_netlink) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_REMOVED; } - obj = nmp_object_clone (old, FALSE); - - udev_device_unref (obj->_link.udev.device); - obj->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL; + obj_new = nmp_object_clone (obj_old, FALSE); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + udev_device_unref (obj_new->_link.udev.device); + obj_new->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL; - nm_assert (nmp_object_is_alive (obj)); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + _idxcache_update (cache, + entry_old, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; } } NMPCacheOpsType -nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_update_link_master_connected (NMPCache *cache, + int ifindex, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new = NULL; + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + + entry_old = nmp_cache_lookup_entry_link (cache, ifindex); - old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + obj_old = entry_old->obj; + if (!nmp_cache_link_connected_needs_toggle (cache, obj_old, NULL, NULL)) { + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; } - nm_assert (old->is_cached); + obj_new = nmp_object_clone (obj_old, FALSE); + obj_new->link.connected = !obj_old->link.connected; - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + _idxcache_update (cache, + entry_old, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); + return NMP_CACHE_OPS_UPDATED; +} - if (!nmp_cache_link_connected_needs_toggle (cache, old, NULL, NULL)) - return NMP_CACHE_OPS_UNCHANGED; +/*****************************************************************************/ - obj = nmp_object_clone (old, FALSE); - obj->link.connected = !old->link.connected; +void +nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type) +{ + NMPObject obj_needle; - nm_assert (nmp_object_is_alive (obj)); + nm_assert (cache); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); - return NMP_CACHE_OPS_UPDATED; + nm_dedup_multi_index_dirty_set_head (cache->multi_idx, + _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE), + _nmp_object_stackinit_from_type (&obj_needle, obj_type)); } /*****************************************************************************/ NMPCache * -nmp_cache_new (gboolean use_udev) -{ - NMPCache *cache = g_new (NMPCache, 1); - - cache->idx_main = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - NULL); - cache->idx_multi = nm_multi_index_new ((NMMultiIndexFuncHash) nmp_cache_id_hash, - (NMMultiIndexFuncEqual) nmp_cache_id_equal, - (NMMultiIndexFuncClone) nmp_cache_id_clone, - (NMMultiIndexFuncDestroy) nmp_cache_id_destroy); +nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev) +{ + NMPCache *cache = g_slice_new0 (NMPCache); + guint i; + + for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) + _dedup_multi_idx_type_init ((DedupMultiIdxType *) _idx_type_get (cache, i), i); + + cache->multi_idx = nm_dedup_multi_index_ref (multi_idx); + cache->use_udev = !!use_udev; return cache; } @@ -2025,23 +2440,14 @@ nmp_cache_new (gboolean use_udev) void nmp_cache_free (NMPCache *cache) { - GHashTableIter iter; - NMPObject *obj; + guint i; - /* No need to cumbersomely remove the objects properly. They are not hooked up - * in a complicated way, we can just unref them together with cache->idx_main. - * - * But we must clear the @is_cached flag. */ - g_hash_table_iter_init (&iter, cache->idx_main); - while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { - nm_assert (obj->is_cached); - obj->is_cached = FALSE; - } + for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) + nm_dedup_multi_index_remove_idx (cache->multi_idx, _idx_type_get (cache, i)); - nm_multi_index_free (cache->idx_multi); - g_hash_table_unref (cache->idx_main); + nm_dedup_multi_index_unref (cache->multi_idx); - g_free (cache); + g_slice_free (NMPCache, cache); } /*****************************************************************************/ @@ -2049,63 +2455,13 @@ nmp_cache_free (NMPCache *cache) void ASSERT_nmp_cache_is_consistent (const NMPCache *cache) { -#if NM_MORE_ASSERTS - NMMultiIndexIter iter_multi; - GHashTableIter iter_hash; - guint i, len; - NMPCacheId cache_id_storage; - const NMPCacheId *cache_id, *cache_id2; - const NMPlatformObject *const *objects; - const NMPObject *obj; - - g_assert (cache); - - g_hash_table_iter_init (&iter_hash, cache->idx_main); - while (g_hash_table_iter_next (&iter_hash, (gpointer *) &obj, NULL)) { - const guint8 *id_type; - - g_assert (NMP_OBJECT_IS_VALID (obj)); - g_assert (nmp_object_is_alive (obj)); - - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id)) - continue; - if (!cache_id) - continue; - g_assert (nm_multi_index_contains (cache->idx_multi, &cache_id->base, &obj->object)); - } - } - - nm_multi_index_iter_init (&iter_multi, cache->idx_multi, NULL); - while (nm_multi_index_iter_next (&iter_multi, - (const NMMultiIndexId **) &cache_id, - (void *const**) &objects, - &len)) { - g_assert (len > 0 && objects && objects[len] == NULL); - - for (i = 0; i < len; i++) { - g_assert (objects[i]); - obj = NMP_OBJECT_UP_CAST (objects[i]); - g_assert (NMP_OBJECT_IS_VALID (obj)); - - /* for now, enforce that all objects for a certain index are of the same type. */ - g_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[0]))); - - if (!_nmp_object_init_cache_id (obj, cache_id->_id_type, &cache_id_storage, &cache_id2)) - g_assert_not_reached (); - g_assert (cache_id2); - g_assert (nmp_cache_id_equal (cache_id, cache_id2)); - g_assert_cmpint (nmp_cache_id_hash (cache_id), ==, nmp_cache_id_hash (cache_id2)); - - g_assert (obj == g_hash_table_lookup (cache->idx_main, obj)); - } - } -#endif } + /*****************************************************************************/ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { [NMP_OBJECT_TYPE_LINK - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LINK, .sizeof_data = sizeof (NMPObjectLink), .sizeof_public = sizeof (NMPlatformLink), @@ -2115,22 +2471,23 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, .supported_cache_ids = _supported_cache_ids_link, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, .cmd_obj_cmp = _vt_cmd_obj_cmp_link, .cmd_obj_copy = _vt_cmd_obj_copy_link, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link, .cmd_obj_dispose = _vt_cmd_obj_dispose_link, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, .cmd_obj_to_string = _vt_cmd_obj_to_string_link, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_link, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_link, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_link_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_link_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp, }, [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, .sizeof_data = sizeof (NMPObjectIP4Address), .sizeof_public = sizeof (NMPlatformIP4Address), @@ -2140,17 +2497,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_address, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_address, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_address_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_ip4_address_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp, }, [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, .sizeof_data = sizeof (NMPObjectIP6Address), .sizeof_public = sizeof (NMPlatformIP6Address), @@ -2160,17 +2517,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_address, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_address_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_ip6_address_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, .sizeof_data = sizeof (NMPObjectIP4Route), .sizeof_public = sizeof (NMPlatformIP4Route), @@ -2179,18 +2536,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .rtm_gettype = RTM_GETROUTE, .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ip4_route, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route, + .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_route, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_route, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp_full, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, .sizeof_data = sizeof (NMPObjectIP6Route), .sizeof_public = sizeof (NMPlatformIP6Route), @@ -2199,109 +2556,129 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .rtm_gettype = RTM_GETROUTE, .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ip6_route, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route, + .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_route, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_route, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_route, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full, }, [NMP_OBJECT_TYPE_LNK_GRE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_GRE, .sizeof_data = sizeof (NMPObjectLnkGre), .sizeof_public = sizeof (NMPlatformLnkGre), .obj_type_name = "gre", .lnk_link_type = NM_LINK_TYPE_GRE, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_gre_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp, }, [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, .sizeof_data = sizeof (NMPObjectLnkInfiniband), .sizeof_public = sizeof (NMPlatformLnkInfiniband), .obj_type_name = "infiniband", .lnk_link_type = NM_LINK_TYPE_INFINIBAND, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_infiniband_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_infiniband_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_infiniband_cmp, }, [NMP_OBJECT_TYPE_LNK_IP6TNL - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IP6TNL, .sizeof_data = sizeof (NMPObjectLnkIp6Tnl), .sizeof_public = sizeof (NMPlatformLnkIp6Tnl), .obj_type_name = "ip6tnl", .lnk_link_type = NM_LINK_TYPE_IP6TNL, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp, }, [NMP_OBJECT_TYPE_LNK_IPIP - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IPIP, .sizeof_data = sizeof (NMPObjectLnkIpIp), .sizeof_public = sizeof (NMPlatformLnkIpIp), .obj_type_name = "ipip", .lnk_link_type = NM_LINK_TYPE_IPIP, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ipip_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ipip_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ipip_cmp, }, [NMP_OBJECT_TYPE_LNK_MACSEC - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACSEC, .sizeof_data = sizeof (NMPObjectLnkMacsec), .sizeof_public = sizeof (NMPlatformLnkMacsec), .obj_type_name = "macsec", .lnk_link_type = NM_LINK_TYPE_MACSEC, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macsec_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_macsec_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macsec_cmp, }, [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN, .sizeof_data = sizeof (NMPObjectLnkMacvlan), .sizeof_public = sizeof (NMPlatformLnkMacvlan), .obj_type_name = "macvlan", .lnk_link_type = NM_LINK_TYPE_MACVLAN, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_macvlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, }, [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP, .sizeof_data = sizeof (NMPObjectLnkMacvtap), .sizeof_public = sizeof (NMPlatformLnkMacvtap), .obj_type_name = "macvtap", .lnk_link_type = NM_LINK_TYPE_MACVTAP, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_macvlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, }, [NMP_OBJECT_TYPE_LNK_SIT - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_SIT, .sizeof_data = sizeof (NMPObjectLnkSit), .sizeof_public = sizeof (NMPlatformLnkSit), .obj_type_name = "sit", .lnk_link_type = NM_LINK_TYPE_SIT, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_sit_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_sit_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp, }, [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, .sizeof_data = sizeof (NMPObjectLnkVlan), .sizeof_public = sizeof (NMPlatformLnkVlan), .obj_type_name = "vlan", .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, }, [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VXLAN, .sizeof_data = sizeof (NMPObjectLnkVxlan), .sizeof_public = sizeof (NMPlatformLnkVxlan), .obj_type_name = "vxlan", .lnk_link_type = NM_LINK_TYPE_VXLAN, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vxlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vxlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vxlan_cmp, }, }; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index b69680f6..41fd08cb 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -21,8 +21,9 @@ #ifndef __NMP_OBJECT_H__ #define __NMP_OBJECT_H__ +#include "nm-utils/nm-obj.h" +#include "nm-utils/nm-dedup-multi.h" #include "nm-platform.h" -#include "nm-multi-index.h" struct udev_device; @@ -47,36 +48,42 @@ typedef enum { /*< skip >*/ * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT. * * Of one index type, there can be multiple indexes or not. - * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX there + * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX there * are multiple instances (for different route/addresses, v4/v6, per-ifindex). * * But one object, can only be indexed by one particular index of a * type. For example, a certain address instance is only indexed by - * the index NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX with + * the index NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX with * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible. * */ typedef enum { /*< skip >*/ NMP_CACHE_ID_TYPE_NONE, - /* all the objects of a certain type */ + /* all the objects of a certain type. + * + * This index is special. It is the only one that contains *all* object. + * Other indexes may consider some object as non "partitionable", hence + * they don't track all objects. + * + * Hence, this index type is used when looking at all objects (still + * partitioned by type). + * + * Also, note that links may be considered invisible. This index type + * expose all links, even invisible ones. For addresses/routes, this + * distiction doesn't exist, as all addresses/routes that are alive + * are visible as well. */ NMP_CACHE_ID_TYPE_OBJECT_TYPE, /* index for the link objects by ifname. */ NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - /* all the visible objects of a certain type */ - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, + /* indeces for the visible default-routes, ignoring ifindex. + * This index only contains two partitions: all visible default-routes, + * separate for IPv4 and IPv6. */ + NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, - /* indeces for the visible routes, ignoring ifindex. */ - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - - /* all the visible addresses/routes (by object-type) for an ifindex. */ - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - - /* three indeces for the visible routes, per ifindex. */ - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, + /* all the addresses/routes (by object-type) for an ifindex. */ + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, /* Consider all the destination fields of a route, that is, the ID without the ifindex * and gateway (meaning: network/plen,metric). @@ -86,65 +93,21 @@ typedef enum { /*< skip >*/ * sends one RTM_NEWADDR notification without notifying about the deletion. We detect * that by having this index to contain overlapping routes which require special * cache-resync. */ - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, __NMP_CACHE_ID_TYPE_MAX, NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, } NMPCacheIdType; -typedef struct _NMPCacheId NMPCacheId; - -struct _NMPCacheId { - union { - NMMultiIndexId base; - guint8 _id_type; /* NMPCacheIdType as guint8 */ - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_OBJECT_TYPE */ - /* NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */ - guint8 _id_type; - guint8 obj_type; /* NMPObjectType as guint8 */ - } object_type; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT */ - guint8 _id_type; - guint8 obj_type; /* NMPObjectType as guint8 */ - int _misaligned_ifindex; - } object_type_by_ifindex; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_LINK_BY_IFNAME */ - guint8 _id_type; - char ifname_short[IFNAMSIZ - 1]; /* don't include the trailing NUL so the struct fits in 4 bytes. */ - } link_by_ifname; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4 */ - guint8 _id_type; - guint8 plen; - guint32 _misaligned_metric; - guint32 _misaligned_network; - } routes_by_destination_ip4; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6 */ - guint8 _id_type; - guint8 plen; - guint32 _misaligned_metric; - struct in6_addr _misaligned_network; - } routes_by_destination_ip6; - }; -}; - typedef struct { + NMDedupMultiObjClass parent; + const char *obj_type_name; + int sizeof_data; + int sizeof_public; NMPObjectType obj_type; int addr_family; int rtm_gettype; - int sizeof_data; - int sizeof_public; NMPlatformSignalIdType signal_type_id; - const char *obj_type_name; const char *signal_type; const guint8 *supported_cache_ids; @@ -152,13 +115,9 @@ typedef struct { /* Only for NMPObjectLnk* types. */ NMLinkType lnk_link_type; - /* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index, - * initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */ - gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id); - + void (*cmd_obj_hash_update) (const NMPObject *obj, NMHashState *h); int (*cmd_obj_cmp) (const NMPObject *obj1, const NMPObject *obj2); void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src); - void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src); void (*cmd_obj_dispose) (NMPObject *obj); gboolean (*cmd_obj_is_alive) (const NMPObject *obj); gboolean (*cmd_obj_is_visible) (const NMPObject *obj); @@ -166,10 +125,11 @@ typedef struct { /* functions that operate on NMPlatformObject */ void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src); - gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); - guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj); + int (*cmd_plobj_id_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); + void (*cmd_plobj_id_hash_update) (const NMPlatformObject *obj, NMHashState *h); const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size); const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj, char *buf, gsize len); + void (*cmd_plobj_hash_update) (const NMPlatformObject *obj, NMHashState *h); int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; @@ -182,7 +142,7 @@ typedef struct { bool is_in_netlink; /* Additional data that depends on the link-type (IFLA_INFO_DATA) */ - NMPObject *lnk; + const NMPObject *lnk; } netlink; struct { @@ -266,9 +226,10 @@ typedef struct { } NMPObjectIP6Route; struct _NMPObject { - const NMPClass *_class; - int _ref_count; - bool is_cached; + union { + NMDedupMultiObj parent; + const NMPClass *_class; + }; union { NMPlatformObject object; @@ -326,8 +287,6 @@ NMP_CLASS_IS_VALID (const NMPClass *klass) && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0; } -#define NMP_REF_COUNT_STACKINIT (G_MAXINT) - static inline NMPObject * NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { @@ -336,7 +295,7 @@ NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) obj = plobj ? (NMPObject *) ( &(((char *) plobj)[-((int) G_STRUCT_OFFSET (NMPObject, object))]) ) : NULL; - nm_assert (!obj || (obj->_ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); + nm_assert (!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); return obj; } #define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST ((const NMPlatformObject *) (plobj))) @@ -345,7 +304,7 @@ static inline gboolean NMP_OBJECT_IS_VALID (const NMPObject *obj) { nm_assert (!obj || ( obj - && obj->_ref_count > 0 + && obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); /* There isn't really much to check. Either @obj is NULL, or we must @@ -358,7 +317,7 @@ NMP_OBJECT_IS_STACKINIT (const NMPObject *obj) { nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); - return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT; + return obj && obj->parent._ref_count == NM_OBJ_REF_COUNT_STACKINIT; } static inline const NMPClass * @@ -377,65 +336,264 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; } - +#define NMP_OBJECT_CAST_LINK(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_LINK); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP4_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP6_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ + }) const NMPClass *nmp_class_from_type (NMPObjectType obj_type); -NMPObject *nmp_object_ref (NMPObject *object); -void nmp_object_unref (NMPObject *object); +static inline const NMPObject * +nmp_object_ref (const NMPObject *obj) +{ + if (!obj) { + /* for convenience, allow NULL. */ + return NULL; + } + + /* ref and unref accept const pointers. NMPObject is supposed to be shared + * and kept immutable. Disallowing to take/retrun a reference to a const + * NMPObject is cumbersome, because callers are precisely expected to + * keep a ref on the otherwise immutable object. */ + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + g_return_val_if_fail (obj->parent._ref_count != NM_OBJ_REF_COUNT_STACKINIT, NULL); + + return (const NMPObject *) nm_dedup_multi_obj_ref ((const NMDedupMultiObj *) obj); +} + +static inline const NMPObject * +nmp_object_unref (const NMPObject *obj) +{ + nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); + return NULL; +} + +#define nm_clear_nmp_object(ptr) \ + ({ \ + typeof (ptr) _ptr = (ptr); \ + typeof (*_ptr) _pptr; \ + gboolean _changed = FALSE; \ + \ + if ( _ptr \ + && (_pptr = *_ptr)) { \ + *_ptr = NULL; \ + nmp_object_unref (_pptr); \ + _changed = TRUE; \ + } \ + _changed; \ + }) + NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); NMPObject *nmp_object_new_link (int ifindex); const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj); + +static inline NMPObject * +nmp_object_stackinit_obj (NMPObject *obj, const NMPObject *src) +{ + return obj == src + ? obj + : (NMPObject *) nmp_object_stackinit (obj, NMP_OBJECT_GET_TYPE (src), &src->object); +} + const NMPObject *nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src); const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex); const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address); -const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, guint8 plen); -const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric); -const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric); +const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address); const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); +void nmp_object_hash_update (const NMPObject *obj, NMHashState *h); int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2); gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2); void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only); NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only); -gboolean nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2); + +int nmp_object_id_cmp (const NMPObject *obj1, const NMPObject *obj2); +void nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h); guint nmp_object_id_hash (const NMPObject *obj); + +static inline gboolean +nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + return nmp_object_id_cmp (obj1, obj2) == 0; +} + gboolean nmp_object_is_alive (const NMPObject *obj); gboolean nmp_object_is_visible (const NMPObject *obj); -void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev); +void _nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev); -#define nm_auto_nmpobj __attribute__((cleanup(_nm_auto_nmpobj_cleanup))) static inline void -_nm_auto_nmpobj_cleanup (NMPObject **pobj) +_nm_auto_nmpobj_cleanup (gpointer p) { - nmp_object_unref (*pobj); + nmp_object_unref (*((const NMPObject **) p)); } +#define nm_auto_nmpobj nm_auto(_nm_auto_nmpobj_cleanup) typedef struct _NMPCache NMPCache; typedef void (*NMPCachePreHook) (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); typedef gboolean (*NMPObjectMatchFn) (const NMPObject *obj, gpointer user_data); -gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b); -guint nmp_cache_id_hash (const NMPCacheId *id); -NMPCacheId *nmp_cache_id_clone (const NMPCacheId *id); -void nmp_cache_id_destroy (NMPCacheId *id); +const NMDedupMultiEntry *nmp_cache_lookup_entry (const NMPCache *cache, + const NMPObject *obj); +const NMDedupMultiEntry *nmp_cache_lookup_entry_with_idx_type (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj); +const NMDedupMultiEntry *nmp_cache_lookup_entry_link (const NMPCache *cache, + int ifindex); +const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, + const NMPObject *obj); +const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, + int ifindex); + +typedef struct _NMPLookup NMPLookup; + +struct _NMPLookup { + NMPCacheIdType cache_id_type; + NMPObject selector_obj; +}; + +const NMDedupMultiHeadEntry *nmp_cache_lookup_all (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *select_obj); + +static inline const NMDedupMultiHeadEntry * +nmp_cache_lookup (const NMPCache *cache, + const NMPLookup *lookup) +{ + return nmp_cache_lookup_all (cache, lookup->cache_id_type, &lookup->selector_obj); +} + +const NMPLookup *nmp_lookup_init_obj_type (NMPLookup *lookup, + NMPObjectType obj_type); +const NMPLookup *nmp_lookup_init_link_by_ifname (NMPLookup *lookup, + const char *ifname); +const NMPLookup *nmp_lookup_init_addrroute (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex); +const NMPLookup *nmp_lookup_init_route_default (NMPLookup *lookup, + NMPObjectType obj_type); +const NMPLookup *nmp_lookup_init_route_by_weak_id (NMPLookup *lookup, + const NMPObject *obj); +const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos); +const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); + +GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, + NMPObjectType obj_type, + gboolean visible_only); + +static inline gboolean +nmp_cache_iter_next (NMDedupMultiIter *iter, const NMPObject **out_obj) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (iter); + nm_assert (!has_next || NMP_OBJECT_IS_VALID (iter->current->obj)); + if (out_obj) + *out_obj = has_next ? iter->current->obj : NULL; + return has_next; +} + +static inline gboolean +nmp_cache_iter_next_link (NMDedupMultiIter *iter, const NMPlatformLink **out_obj) +{ + gboolean has_next; -NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only); -NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex); -NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex); -NMPCacheId *nmp_cache_id_init_link_by_ifname (NMPCacheId *id, const char *ifname); -NMPCacheId *nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, guint32 network, guint8 plen, guint32 metric); -NMPCacheId *nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, const struct in6_addr *network, guint8 plen, guint32 metric); + has_next = nm_dedup_multi_iter_next (iter); + nm_assert (!has_next || NMP_OBJECT_GET_TYPE (iter->current->obj) == NMP_OBJECT_TYPE_LINK); + if (out_obj) + *out_obj = has_next ? &(((const NMPObject *) iter->current->obj)->link) : NULL; + return has_next; +} -const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len); -GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id); -const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj); -const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex); +#define nmp_cache_iter_for_each(iter, head, obj) \ + for (nm_dedup_multi_iter_init ((iter), \ + (head)); \ + nmp_cache_iter_next ((iter), (obj)); \ + ) -const NMPObject *nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route); +#define nmp_cache_iter_for_each_link(iter, head, obj) \ + for (nm_dedup_multi_iter_init ((iter), \ + (head)); \ + nmp_cache_iter_next_link ((iter), (obj)); \ + ) const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, int ifindex, @@ -444,10 +602,8 @@ const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, NMLinkType link_type, NMPObjectMatchFn match_fn, gpointer user_data); -GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache, - NMPCacheId *cache_id, - GHashTable *hash); +gboolean nmp_cache_link_connected_for_slave (int ifindex_master, const NMPObject *slave); gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave); const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave); @@ -455,13 +611,182 @@ gboolean nmp_cache_use_udev_get (const NMPCache *cache); void ASSERT_nmp_cache_is_consistent (const NMPCache *cache); -NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); - -NMPCache *nmp_cache_new (gboolean use_udev); +NMPCacheOpsType nmp_cache_remove (NMPCache *cache, + const NMPObject *obj_needle, + gboolean equals_by_ptr, + gboolean only_dirty, + const NMPObject **out_obj_old); +NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, + const NMPObject *obj_needle, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_netlink_route (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + guint16 nlmsgflags, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new, + const NMPObject **out_obj_replace, + gboolean *out_resync_required); +NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, + int ifindex, + struct udev_device *udevice, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, + int ifindex, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); + +void nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type); + +NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev); void nmp_cache_free (NMPCache *cache); +static inline void +ASSERT_nmp_cache_ops (const NMPCache *cache, + NMPCacheOpsType ops_type, + const NMPObject *obj_old, + const NMPObject *obj_new) +{ +#if NM_MORE_ASSERTS + nm_assert (cache); + nm_assert (obj_old || obj_new); + nm_assert (!obj_old || ( NMP_OBJECT_IS_VALID (obj_old) + && !NMP_OBJECT_IS_STACKINIT (obj_old) + && nmp_object_is_alive (obj_old))); + nm_assert (!obj_new || ( NMP_OBJECT_IS_VALID (obj_new) + && !NMP_OBJECT_IS_STACKINIT (obj_new) + && nmp_object_is_alive (obj_new))); + + switch (ops_type) { + case NMP_CACHE_OPS_UNCHANGED: + nm_assert (obj_old == obj_new); + break; + case NMP_CACHE_OPS_ADDED: + nm_assert (!obj_old && obj_new); + break; + case NMP_CACHE_OPS_UPDATED: + nm_assert (obj_old && obj_new && obj_old != obj_new); + break; + case NMP_CACHE_OPS_REMOVED: + nm_assert (obj_old && !obj_new); + break; + default: + nm_assert_not_reached (); + } + + nm_assert (obj_new == NULL || obj_old == NULL || nmp_object_id_equal (obj_new, obj_old)); + nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_old) == NMP_OBJECT_GET_CLASS (obj_new)); + + nm_assert (obj_new == nmp_cache_lookup_obj (cache, obj_new ?: obj_old)); +#endif +} + +const NMDedupMultiHeadEntry *nm_platform_lookup_all (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj); + +const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj); + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_obj_type (NMPlatform *platform, + NMPObjectType obj_type) +{ + NMPLookup lookup; + + nmp_lookup_init_obj_type (&lookup, obj_type); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_link_by_ifname (NMPlatform *platform, + const char *ifname) +{ + NMPLookup lookup; + + nmp_lookup_init_link_by_ifname (&lookup, ifname); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_addrroute (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex) +{ + NMPLookup lookup; + + nmp_lookup_init_addrroute (&lookup, obj_type, ifindex); + return nm_platform_lookup (platform, &lookup); +} + +static inline GPtrArray * +nm_platform_lookup_addrroute_clone (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + NMPObjectPredicateFunc predicate, + gpointer user_data) +{ + NMPLookup lookup; + + nmp_lookup_init_addrroute (&lookup, obj_type, ifindex); + return nm_platform_lookup_clone (platform, &lookup, predicate, user_data); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_route_default (NMPlatform *platform, + NMPObjectType obj_type) +{ + NMPLookup lookup; + + nmp_lookup_init_route_default (&lookup, obj_type); + return nm_platform_lookup (platform, &lookup); +} + +static inline GPtrArray * +nm_platform_lookup_route_default_clone (NMPlatform *platform, + NMPObjectType obj_type, + NMPObjectPredicateFunc predicate, + gpointer user_data) +{ + NMPLookup lookup; + + nmp_lookup_init_route_default (&lookup, obj_type); + return nm_platform_lookup_clone (platform, &lookup, predicate, user_data); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_ip4_route_by_weak_id (NMPlatform *platform, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos) +{ + NMPLookup lookup; + + nmp_lookup_init_ip4_route_by_weak_id (&lookup, network, plen, metric, tos); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_ip6_route_by_weak_id (NMPlatform *platform, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + NMPLookup lookup; + + nmp_lookup_init_ip6_route_by_weak_id (&lookup, network, plen, metric, src, src_plen); + return nm_platform_lookup (platform, &lookup); +} + #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index d0c58aeb..e1220052 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -78,6 +78,8 @@ main (int argc, char **argv) nm_linux_platform_setup (); + nm_platform_check_kernel_support (NM_PLATFORM_GET, ~((NMPlatformKernelSupportFlags) 0)); + if (global_opt.persist) g_main_loop_run (loop); diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 4c139ef0..93851ff7 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -22,7 +22,6 @@ #include "test-common.h" -#define DEVICE_NAME "nm-test-device" #define IP4_ADDRESS "192.0.2.1" #define IP4_ADDRESS_PEER "192.0.2.2" #define IP4_ADDRESS_PEER2 "192.0.3.1" @@ -30,8 +29,8 @@ #define IP6_ADDRESS "2001:db8:a:b:1:2:3:4" #define IP6_PLEN 64 -static int DEVICE_IFINDEX = -1; -static int EX = -1; +#define DEVICE_IFINDEX NMTSTP_ENV1_IFINDEX +#define EX NMTSTP_ENV1_EX /*****************************************************************************/ @@ -103,7 +102,7 @@ test_ip4_address_general (void) accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP4Address, 0); @@ -143,9 +142,9 @@ test_ip6_address_general (void) inet_pton (AF_INET6, IP6_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); accept_signal (address_added); /* Add address again (aka update) */ @@ -153,7 +152,7 @@ test_ip6_address_general (void) accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + addresses = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP6Address, 0); @@ -164,7 +163,7 @@ test_ip6_address_general (void) /* Remove address */ nmtstp_ip6_address_del (NULL, EX, ifindex, addr, IP6_PLEN); - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); accept_signal (address_removed); /* Remove address again */ @@ -229,20 +228,20 @@ test_ip6_address_general_2 (void) /* Add/delete notification */ nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); accept_signal (address_added); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); nmtstp_ip6_address_del (NULL, EX, ifindex, addr, IP6_PLEN); accept_signal (address_removed); - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); /* Add/delete conflict */ nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); accept_signal (address_added); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); ensure_no_signal (address_added); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); free_signal (address_added); free_signal (address_removed); @@ -330,7 +329,7 @@ test_ip4_address_peer_zero (void) nmtstp_ip4_address_add (NULL, EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, 0, label); - addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addrs = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addrs); g_assert_cmpint (addrs->len, ==, i + 1); g_array_unref (addrs); @@ -345,7 +344,7 @@ test_ip4_address_peer_zero (void) nmtstp_ip4_address_del (NULL, EX, ifindex, addr, plen, r_peers[i]); - addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addrs = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addrs); g_assert_cmpint (addrs->len, ==, G_N_ELEMENTS (peers) - i - 1); g_array_unref (addrs); @@ -366,62 +365,16 @@ _nmtstp_init_tests (int *argc, char ***argv) * SETUP TESTS *****************************************************************************/ -typedef struct { - const char *testpath; - GTestFunc test_func; -} TestSetup; - -static void -_g_test_run (gconstpointer user_data) -{ - const TestSetup *s = user_data; - int ifindex; - - _LOGT ("TEST: start %s", s->testpath); - - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); - - ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint (ifindex, >, 0); - g_assert_cmpint (DEVICE_IFINDEX, ==, -1); - - DEVICE_IFINDEX = ifindex; - EX = nmtstp_run_command_check_external_global (); - - s->test_func (); - - g_assert_cmpint (DEVICE_IFINDEX, ==, ifindex); - DEVICE_IFINDEX = -1; - - g_assert_cmpint (ifindex, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - _LOGT ("TEST: finished %s", s->testpath); -} - -static void -_g_test_add_func (const char *testpath, - GTestFunc test_func) -{ - TestSetup *s; - - s = g_new0 (TestSetup, 1); - s->testpath = testpath; - s->test_func = test_func; - - g_test_add_data_func_full (testpath, s, _g_test_run, g_free); -} - void _nmtstp_setup_tests (void) { - _g_test_add_func ("/address/ipv4/general", test_ip4_address_general); - _g_test_add_func ("/address/ipv6/general", test_ip6_address_general); +#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, FALSE) + add_test_func ("/address/ipv4/general", test_ip4_address_general); + add_test_func ("/address/ipv6/general", test_ip6_address_general); - _g_test_add_func ("/address/ipv4/general-2", test_ip4_address_general_2); - _g_test_add_func ("/address/ipv6/general-2", test_ip6_address_general_2); + add_test_func ("/address/ipv4/general-2", test_ip4_address_general_2); + add_test_func ("/address/ipv6/general-2", test_ip6_address_general_2); - _g_test_add_func ("/address/ipv4/peer", test_ip4_address_peer); - _g_test_add_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); + add_test_func ("/address/ipv4/peer", test_ip4_address_peer); + add_test_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); } diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 71a92cbf..937cd12c 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -22,8 +22,6 @@ #include "test-common.h" -#define DEVICE_NAME "nm-test-device" - static void test_cleanup_internal (void) { @@ -31,8 +29,8 @@ test_cleanup_internal (void) int ifindex; GArray *addresses4; GArray *addresses6; - GArray *routes4; - GArray *routes6; + GPtrArray *routes4; + GPtrArray *routes6; in_addr_t addr4; in_addr_t network4; int plen4 = 24; @@ -72,10 +70,10 @@ test_cleanup_internal (void) nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, in6addr_any, metric, mss); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, in6addr_any, metric, mss); - addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); - addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); - routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + addresses4 = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); + routes6 = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); g_assert_cmpint (addresses4->len, ==, 1); g_assert_cmpint (addresses6->len, ==, 2); /* also has a IPv6 LL address. */ @@ -84,26 +82,24 @@ test_cleanup_internal (void) g_array_unref (addresses4); g_array_unref (addresses6); - g_array_unref (routes4); - g_array_unref (routes6); + g_ptr_array_unref (routes4); + g_ptr_array_unref (routes6); /* Delete interface with all addresses and routes */ g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); - addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); - routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + addresses4 = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); + routes6 = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); g_assert_cmpint (addresses4->len, ==, 0); g_assert_cmpint (addresses6->len, ==, 0); - g_assert_cmpint (routes4->len, ==, 0); - g_assert_cmpint (routes6->len, ==, 0); + g_assert (!routes4); + g_assert (!routes6); g_array_unref (addresses4); g_array_unref (addresses6); - g_array_unref (routes4); - g_array_unref (routes6); } NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index a9d0694d..18076712 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -30,6 +30,9 @@ #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" #define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count +int NMTSTP_ENV1_IFINDEX = -1; +int NMTSTP_ENV1_EX = -1; + /*****************************************************************************/ void @@ -67,6 +70,90 @@ _init_platform (NMPlatform **platform, gboolean external_command) /*****************************************************************************/ +static GArray * +_ipx_address_get_all (NMPlatform *self, int ifindex, NMPObjectType obj_type) +{ + NMPLookup lookup; + + g_assert (NM_IS_PLATFORM (self)); + g_assert (ifindex > 0); + g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); + nmp_lookup_init_addrroute (&lookup, + obj_type, + ifindex); + return nmp_cache_lookup_to_array (nm_platform_lookup (self, &lookup), + obj_type, + FALSE /*addresses are always visible. */); +} + +GArray * +nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex) +{ + return _ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); +} + +GArray * +nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex) +{ + return _ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); +} + +/*****************************************************************************/ + +gboolean +nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) +{ + NMDedupMultiIter iter; + + nm_platform_process_events (platform); + + nm_dedup_multi_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex)) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (iter.current->obj); + + if ( r->ifindex != ifindex + || r->network != network + || r->plen != plen + || r->metric != metric) { + continue; + } + + return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r)); + } + + return TRUE; +} + +gboolean +nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +{ + NMDedupMultiIter iter; + + nm_platform_process_events (platform); + + nm_dedup_multi_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex)) { + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (iter.current->obj); + + if ( r->ifindex != ifindex + || !IN6_ARE_ADDR_EQUAL (&r->network, &network) + || r->plen != plen + || r->metric != metric) { + continue; + } + + return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r)); + } + + return TRUE; +} + +/*****************************************************************************/ + SignalData * add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname) { @@ -153,9 +240,9 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; - GArray *links; - NMPlatformLink *cached; - int i; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *cached; g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_LINK); g_assert (received); @@ -185,19 +272,21 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink /* Check the data */ g_assert (received->ifindex > 0); - links = nm_platform_link_get_all (NM_PLATFORM_GET, TRUE); - for (i = 0; i < links->len; i++) { - cached = &g_array_index (links, NMPlatformLink, i); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nm_platform_lookup (platform, &lookup), + &cached) { + if (!nmp_object_is_visible (NMP_OBJECT_UP_CAST (cached))) + continue; if (cached->ifindex == received->ifindex) { g_assert_cmpint (nm_platform_link_cmp (cached, received), ==, 0); g_assert (!memcmp (cached, received, sizeof (*cached))); if (data->change_type == NM_PLATFORM_SIGNAL_REMOVED) g_error ("Deleted link still found in the local cache."); - g_array_unref (links); return; } } - g_array_unref (links); if (data->change_type != NM_PLATFORM_SIGNAL_REMOVED) g_error ("Added/changed link not found in the local cache."); @@ -205,113 +294,219 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink /*****************************************************************************/ -gboolean -nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric) +static const NMPlatformIP4Route * +_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos, + guint *out_c_exists) { - gs_free char *arg_network = NULL; - const char *argv[] = { - NULL, - "route", - "list", - "dev", - ifname, - "exact", - NULL, - NULL, - }; - int exit_status; - gs_free char *std_out = NULL, *std_err = NULL; - char *out; - gboolean success; - gs_free_error GError *error = NULL; - gs_free char *metric_pattern = NULL; + NMDedupMultiIter iter; + NMPLookup lookup; + const NMPObject *o = NULL; + guint c; + const NMPlatformIP4Route *r = NULL; - g_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL)); - g_assert (!strstr (ifname, " metric ")); - g_assert (plen >= 0 && plen <= 32); + _init_platform (&platform, FALSE); - if (!nmtstp_is_root_test ()) { - /* If we don't test against linux-platform, we don't actually configure any - * routes in the system. */ - return -1; + nmp_lookup_init_ip4_route_by_weak_id (&lookup, + network, + plen, + metric, + tos); + + c = 0; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup (platform, &lookup), + &o) { + if ( NMP_OBJECT_CAST_IP4_ROUTE (o)->ifindex != ifindex + && ifindex > 0) + continue; + if (!r) + r = NMP_OBJECT_CAST_IP4_ROUTE (o); + c++; } - argv[0] = nm_utils_file_search_in_paths ("ip", NULL, - (const char *[]) { "/sbin", "/usr/sbin", NULL }, - G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, NULL); - argv[6] = arg_network = g_strdup_printf ("%s/%d", nm_utils_inet4_ntop (network, NULL), plen); + NM_SET_OUT (out_c_exists, c); + return r; +} - if (!argv[0]) { - /* Hm. There is no 'ip' binary. Return *unknown* */ - return -1; +const NMPlatformIP4Route * +_nmtstp_assert_ip4_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + guint32 network, + int plen, + guint32 metric, + guint8 tos) +{ + int ifindex; + guint c; + const NMPlatformIP4Route *r = NULL; + + _init_platform (&platform, FALSE); + + ifindex = -1; + if (ifname) { + ifindex = nm_platform_link_get_ifindex (platform, ifname); + g_assert (ifindex > 0); } - success = g_spawn_sync (NULL, - (char **) argv, - (char *[]) { NULL }, - 0, - NULL, - NULL, - &std_out, - &std_err, - &exit_status, - &error); - g_assert_no_error (error); - g_assert (success); - g_assert_cmpstr (std_err, ==, ""); - g_assert (std_out); - - metric_pattern = g_strdup_printf (" metric %u", metric); - out = std_out; - while (out) { - char *eol = strchr (out, '\n'); - gs_free char *line = eol ? g_strndup (out, eol - out) : g_strdup (out); - const char *p; - - out = eol ? &eol[1] : NULL; - if (!line[0]) - continue; + r = _ip4_route_get (platform, + ifindex, + network, + plen, + metric, + tos, + &c); - if (metric == 0) { - if (!strstr (line, " metric ")) - return TRUE; - } - p = strstr (line, metric_pattern); - if (p && NM_IN_SET (p[strlen (metric_pattern)], ' ', '\0')) - return TRUE; + if (c != c_exists && c_exists != -1) { + g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but platform has it %u times", + file, line, func, + nm_utils_inet4_ntop (network, NULL), plen, + metric, + tos, + c_exists, + c); } - return FALSE; + + return r; } -void -_nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric) +const NMPlatformIP4Route * +nmtstp_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos) +{ + return _ip4_route_get (platform, + ifindex, + network, + plen, + metric, + tos, + NULL); +} + +/*****************************************************************************/ + +static const NMPlatformIP6Route * +_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen, + guint *out_c_exists) +{ + NMDedupMultiIter iter; + NMPLookup lookup; + const NMPObject *o = NULL; + guint c; + const NMPlatformIP6Route *r = NULL; + + _init_platform (&platform, FALSE); + + nmp_lookup_init_ip6_route_by_weak_id (&lookup, + network, + plen, + metric, + src, + src_plen); + + c = 0; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup (platform, &lookup), + &o) { + if ( NMP_OBJECT_CAST_IP6_ROUTE (o)->ifindex != ifindex + && ifindex > 0) + continue; + if (!r) + r = NMP_OBJECT_CAST_IP6_ROUTE (o); + c++; + } + + NM_SET_OUT (out_c_exists, c); + return r; +} + +const NMPlatformIP6Route * +_nmtstp_assert_ip6_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) { int ifindex; - gboolean exists_checked; + guint c; + const NMPlatformIP6Route *r = NULL; _init_platform (&platform, FALSE); - /* Check for existance of the route by spawning iproute2. Do this because platform - * code might be entirely borked, but we expect ip-route to give a correct result. - * If the ip command cannot be found, we accept this as success. */ - exists_checked = nmtstp_ip4_route_exists (ifname, network, plen, metric); - if (exists_checked != -1 && !exists_checked != !exists) { - g_error ("[%s:%u] %s(): We expect the ip4 route %s/%d metric %u %s, but it %s", - file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, metric, - exists ? "to exist" : "not to exist", - exists ? "doesn't" : "does"); + ifindex = -1; + if (ifname) { + ifindex = nm_platform_link_get_ifindex (platform, ifname); + g_assert (ifindex > 0); } - ifindex = nm_platform_link_get_ifindex (platform, ifname); - g_assert (ifindex > 0); - if (!nm_platform_ip4_route_get (platform, ifindex, network, plen, metric) != !exists) { - g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s", + r = _ip6_route_get (platform, + ifindex, + network, + plen, + metric, + src, + src_plen, + &c); + + if (c != c_exists && c_exists != -1) { + char s_src[NM_UTILS_INET_ADDRSTRLEN]; + char s_network[NM_UTILS_INET_ADDRSTRLEN]; + + g_error ("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but platform has it %u times", file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, metric, - exists ? "exists" : "does not exist", - exists ? "it doesn't" : "it does"); + nm_utils_inet6_ntop (network, s_network), + plen, + metric, + nm_utils_inet6_ntop (src, s_src), + src_plen, + c_exists, + c); } + + return r; +} + +const NMPlatformIP6Route * +nmtstp_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + return _ip6_route_get (platform, + ifindex, + network, + plen, + metric, + src, + src_plen, + NULL); } /*****************************************************************************/ @@ -369,7 +564,7 @@ _wait_for_signal_timeout (gpointer user_data) } guint -nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms) +nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_ms) { WaitForSignalData data = { 0 }; gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route; @@ -384,8 +579,18 @@ nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms) id_ip4_route = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); id_ip6_route = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); - if (timeout_ms != 0) - data.id = g_timeout_add (timeout_ms, _wait_for_signal_timeout, &data); + /* if timeout_ms is negative, it means the wait-time already expired. + * Maybe, we should do nothing and return right away, without even + * processing events from platform. However, that inconsistency (of not + * processing events from mainloop) is inconvenient. + * + * It's better that on the return of nmtstp_wait_for_signal(), we always + * have no events pending. So, a negative timeout is treated the same as + * a zero timeout: we check whether there are any events pending in platform, + * and quite the mainloop immediately afterwards. But we always check. */ + + data.id = g_timeout_add (CLAMP (timeout_ms, 0, G_MAXUINT32), + _wait_for_signal_timeout, &data); g_main_loop_run (data.loop); @@ -414,14 +619,14 @@ nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms) if (until_ms < now) return 0; - signal_counts = nmtstp_wait_for_signal (platform, MAX (1, until_ms - now)); + signal_counts = nmtstp_wait_for_signal (platform, until_ms - now); if (signal_counts) return signal_counts; } } const NMPlatformLink * -nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms) { return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); } @@ -445,7 +650,7 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType if (until_ms < now) return NULL; - nmtstp_wait_for_signal (platform, MAX (1, until_ms - now)); + nmtstp_wait_for_signal (platform, until_ms - now); } } @@ -717,7 +922,7 @@ _ip_address_add (NMPlatform *platform, g_assert (label == NULL); g_assert (flags == 0); - a = nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen); + a = nm_platform_ip6_address_get (platform, ifindex, address->addr6); if ( a && !memcmp (nm_platform_ip6_address_get_peer (a), (IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) || IN6_ARE_ADDR_EQUAL (&address->addr6, &peer_address->addr6)) @@ -804,7 +1009,7 @@ void nmtstp_ip4_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert (nm_platform_ip4_route_add (platform, &route)); + g_assert_cmpint (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); } void nmtstp_ip6_route_add (NMPlatform *platform, @@ -828,7 +1033,7 @@ void nmtstp_ip6_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert (nm_platform_ip6_route_add (platform, &route)); + g_assert_cmpint (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); } /*****************************************************************************/ @@ -861,7 +1066,7 @@ _ip_address_del (NMPlatform *platform, if (is_v4) had_address = !!nm_platform_ip4_address_get (platform, ifindex, address->addr4, plen, peer_address->addr4); else - had_address = !!nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen); + had_address = !!nm_platform_ip6_address_get (platform, ifindex, address->addr6); if (is_v4) { success = nmtstp_run_command ("ip address delete %s%s%s/%d dev %s", @@ -913,7 +1118,7 @@ _ip_address_del (NMPlatform *platform, } else { const NMPlatformIP6Address *a; - a = nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen); + a = nm_platform_ip6_address_get (platform, ifindex, address->addr6); if (!a) break; } @@ -974,6 +1179,36 @@ nmtstp_ip6_address_del (NMPlatform *platform, } G_STMT_END const NMPlatformLink * +nmtstp_link_veth_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const char *peer) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + + g_assert (nm_utils_is_valid_iface_name (name, NULL)); + + external_command = nmtstp_run_command_check_external (external_command); + + _init_platform (&platform, external_command); + + if (external_command) { + success = !nmtstp_run_command ("ip link add dev %s type veth peer name %s", + name, peer); + if (success) { + pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_VETH, 100); + nmtstp_assert_wait_for_link (platform, peer, NM_LINK_TYPE_VETH, 10); + } + } else + success = nm_platform_link_veth_add (platform, name, peer, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + g_assert (success); + _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_VETH); + return pllink; +} + +const NMPlatformLink * nmtstp_link_dummy_add (NMPlatform *platform, gboolean external_command, const char *name) diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index a52a5db5..4010aa2f 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -5,6 +5,7 @@ #include <arpa/inet.h> #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "platform/nm-fake-platform.h" #include "platform/nm-linux-platform.h" @@ -93,9 +94,9 @@ int nmtstp_run_command (const char *format, ...) _nm_printf (1, 2); /*****************************************************************************/ -guint nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms); +guint nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_ms); guint nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms); -const NMPlatformLink *nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms); const NMPlatformLink *nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms); #define nmtstp_assert_wait_for_signal(platform, timeout_ms) \ @@ -120,10 +121,45 @@ gboolean nmtstp_run_command_check_external (int external_command); /*****************************************************************************/ -gboolean nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric); - -void _nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); -#define nmtstp_assert_ip4_route_exists(platform, exists, ifname, network, plen, metric) _nmtstp_assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, exists, ifname, network, plen, metric) +const NMPlatformIP4Route *_nmtstp_assert_ip4_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + guint32 network, + int plen, + guint32 metric, + guint8 tos); +#define nmtstp_assert_ip4_route_exists(platform, c_exists, ifname, network, plen, metric, tos) _nmtstp_assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, c_exists, ifname, network, plen, metric, tos) + +const NMPlatformIP4Route *nmtstp_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos); + +const NMPlatformIP6Route *_nmtstp_assert_ip6_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); +#define nmtstp_assert_ip6_route_exists(platform, c_exists, ifname, network, plen, metric, src, src_plen) _nmtstp_assert_ip6_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, c_exists, ifname, network, plen, metric, src, src_plen) + +const NMPlatformIP6Route *nmtstp_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); /*****************************************************************************/ @@ -187,8 +223,36 @@ void nmtstp_ip6_route_add (NMPlatform *platform, guint32 metric, guint32 mss); +static inline GPtrArray * +nmtstp_ip4_route_get_all (NMPlatform *platform, + int ifindex) +{ + return nm_platform_lookup_addrroute_clone (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + NULL); +} + +static inline GPtrArray * +nmtstp_ip6_route_get_all (NMPlatform *platform, + int ifindex) +{ + return nm_platform_lookup_addrroute_clone (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + NULL); +} + /*****************************************************************************/ +GArray *nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex); +GArray *nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex); + +gboolean nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric); +gboolean nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); + const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type); const NMPlatformLink *nmtstp_link_get (NMPlatform *platform, int ifindex, const char *name); @@ -197,6 +261,10 @@ void nmtstp_link_set_updown (NMPlatform *platform, int ifindex, gboolean up); +const NMPlatformLink *nmtstp_link_veth_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const char *peer); const NMPlatformLink *nmtstp_link_dummy_add (NMPlatform *platform, gboolean external_command, const char *name); @@ -231,6 +299,100 @@ void nmtstp_link_del (NMPlatform *platform, int ifindex, const char *name); +/*****************************************************************************/ + +extern int NMTSTP_ENV1_IFINDEX; +extern int NMTSTP_ENV1_EX; + +static inline void +_nmtstp_env1_wrapper_setup (const NmtstTestData *test_data) +{ + int *p_ifindex; + gpointer p_ifup; + + nmtst_test_data_unpack (test_data, &p_ifindex, NULL, NULL, NULL, &p_ifup); + + g_assert (p_ifindex && *p_ifindex == -1); + + _LOGT ("TEST[%s]: setup", test_data->testpath); + + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); + + *p_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + g_assert_cmpint (*p_ifindex, >, 0); + g_assert_cmpint (NMTSTP_ENV1_IFINDEX, ==, -1); + + if (GPOINTER_TO_INT (p_ifup)) + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, *p_ifindex, NULL)); + + nm_platform_process_events (NM_PLATFORM_GET); + + NMTSTP_ENV1_IFINDEX = *p_ifindex; + NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global (); +} + +static inline void +_nmtstp_env1_wrapper_run (gconstpointer user_data) +{ + const NmtstTestData *test_data = user_data; + GTestDataFunc test_func_data; + GTestFunc test_func; + gconstpointer d; + + nmtst_test_data_unpack (test_data, NULL, &test_func, &test_func_data, &d, NULL); + + _LOGT ("TEST[%s]: run", test_data->testpath); + if (test_func) + test_func (); + else + test_func_data (d); +} + +static inline void +_nmtstp_env1_wrapper_teardown (const NmtstTestData *test_data) +{ + int *p_ifindex; + + nmtst_test_data_unpack (test_data, &p_ifindex, NULL, NULL, NULL, NULL); + + g_assert_cmpint (NMTSTP_ENV1_IFINDEX, ==, *p_ifindex); + NMTSTP_ENV1_IFINDEX = -1; + + _LOGT ("TEST[%s]: teardown", test_data->testpath); + + g_assert_cmpint (*p_ifindex, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, *p_ifindex)); + + nm_platform_process_events (NM_PLATFORM_GET); + + _LOGT ("TEST[%s]: finished", test_data->testpath); + + *p_ifindex = -1; +} + +/* add test function, that set's up a particular environment, consisting + * of a dummy device with ifindex NMTSTP_ENV1_IFINDEX. */ +#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, ifup) \ + nmtst_add_test_func_full (testpath, \ + _nmtstp_env1_wrapper_run, \ + _nmtstp_env1_wrapper_setup, \ + _nmtstp_env1_wrapper_teardown, \ + ({ static int _ifindex = -1; &_ifindex; }), \ + ({ GTestFunc _test_func = (test_func); _test_func; }), \ + ({ GTestDataFunc _test_func = (test_data_func); _test_func; }), \ + (arg), \ + ({ gboolean _ifup = (ifup); GINT_TO_POINTER (_ifup);})) + +#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, ifup) \ + _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, ifup) + +#define nmtstp_env1_add_test_func(testpath, test_func, ifup) \ + _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, ifup) + +/*****************************************************************************/ + typedef void (*NMTstpSetupFunc) (void); extern NMTstpSetupFunc const _nmtstp_setup_platform_func; diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index e772662c..342aa0d6 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -44,7 +44,7 @@ static void test_link_get_all (void) { gs_unref_object NMPlatform *platform = NULL; - gs_unref_array GArray *links = NULL; + gs_unref_ptrarray GPtrArray *links = NULL; platform = nm_linux_platform_new (TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index ed435567..9c72371c 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -76,7 +76,7 @@ test_bogus(void) g_assert (!addrlen); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); - g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU)); + g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU) != NM_PLATFORM_ERROR_SUCCESS); g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); @@ -264,7 +264,8 @@ test_slave (int master, int type, SignalData *master_changed) } g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - if (nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { + if ( nmtstp_is_root_test () + && nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { if (nm_platform_link_get_type (NM_PLATFORM_GET, master) == NM_LINK_TYPE_TEAM) { /* Older team versions (e.g. Fedora 17) have a bug that team master stays * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if @@ -285,7 +286,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 0, 2); /* Enslave again * @@ -294,7 +295,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); accept_signals (link_changed, 0, 2); - ensure_no_signal (master_changed); + accept_signals (master_changed, 0, 2); /* Set slave option */ switch (type) { @@ -327,7 +328,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); accept_signal (link_removed); } - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 0, 2); ensure_no_signal (master_changed); @@ -511,7 +512,8 @@ test_bridge_addr (void) plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); - if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { + if (nm_platform_check_kernel_support (NM_PLATFORM_GET, + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); @@ -601,7 +603,7 @@ test_internal (void) accept_signal (link_changed); /* Set MTU */ - g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU)); + g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU) == NM_PLATFORM_ERROR_SUCCESS); g_assert_cmpint (nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex), ==, MTU); accept_signal (link_changed); @@ -790,7 +792,7 @@ test_software_detect (gconstpointer user_data) * namespaced, the creation can fail if a macvtap in another namespace * has the same index. Try to detect this situation and skip already * used indexes. - * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=17af2bce88d31e65ed73d638bb752d2e13c66ced + * The fix (17af2bce) is included kernel 4.7, dated 24 July, 2016. */ for (i = ifindex_parent + 1; i < ifindex_parent + 100; i++) { snprintf (buf, sizeof (buf), "/sys/class/macvtap/tap%d", i); @@ -1713,9 +1715,8 @@ test_nl_bugs_veth (void) NMTstpNamespaceHandle *ns_handle = NULL; /* create veth pair. */ - nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1); - ifindex_veth0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex; - ifindex_veth1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex; + ifindex_veth0 = nmtstp_link_veth_add (NM_PLATFORM_GET, -1, IFACE_VETH0, IFACE_VETH1)->ifindex; + ifindex_veth1 = nmtstp_link_get_typed (NM_PLATFORM_GET, -1, IFACE_VETH1, NM_LINK_TYPE_VETH)->ifindex; /* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */ g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i)); @@ -1728,8 +1729,8 @@ test_nl_bugs_veth (void) pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); g_assert (pllink_veth0); if (pllink_veth0->parent == 0) { - /* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder - * of the test. */ + /* Kernels prior to 4.1 dated 21 June, 2015 don't support exposing the veth peer + * as IFA_LINK. skip the remainder of the test. */ goto out; } g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1); @@ -2023,8 +2024,8 @@ test_netns_general (gpointer fixture, gconstpointer test_data) _sysctl_assert_eq (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6", NULL); _sysctl_assert_eq (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6", NULL); - /* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and - * skip asserts that are known to fail. */ + /* Kernels prior to 3.19 dated 8 February, 2015 don't support ethtool -i for dummy devices. + * Work around that and skip asserts that are known to fail. */ ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0; if (ethtool_support) { g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 42dfc572..a02388d2 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -33,6 +33,60 @@ struct { /*****************************************************************************/ +static void +test_obj_base (void) +{ + static const union { + GObject g; + NMPObject k; + } x = { }; + static const union { + GTypeClass k; + NMPClass c; + } l = { }; + static const GObject *g = &x.g; + static const GTypeClass *k = &l.k; + static const NMPObject *o = &x.k; + static const NMPClass *c = &l.c; + + NMObjBaseInst *obj; + gs_unref_object GCancellable *obj_cancellable = g_cancellable_new (); + nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10); + +#define STATIC_ASSERT(cond) \ + G_STMT_START { \ + G_STATIC_ASSERT (cond); \ + G_STATIC_ASSERT_EXPR (cond); \ + g_assert (cond); \ + } G_STMT_END + + STATIC_ASSERT (&g->g_type_instance == (void *) &o->_class); + STATIC_ASSERT (&g->g_type_instance.g_class == (void *) &o->_class); + + STATIC_ASSERT (sizeof (o->parent.parent) == sizeof (GTypeInstance)); + + STATIC_ASSERT (&c->parent == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type_class == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type == &k->g_type); + + STATIC_ASSERT (sizeof (c->parent.parent) == sizeof (GTypeClass)); + + STATIC_ASSERT (&o->parent == (void *) o); + STATIC_ASSERT (&o->parent.klass == (void *) &o->_class); + + obj = (NMObjBaseInst *) obj_cancellable; + g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + + obj = (NMObjBaseInst *) obj_link; + g_assert (NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (!G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + +} + +/*****************************************************************************/ + static gboolean _nmp_object_id_equal (const NMPObject *a, const NMPObject *b) { @@ -56,150 +110,144 @@ _nmp_object_equal (const NMPObject *a, const NMPObject *b) /*****************************************************************************/ static void -_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains) +_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMDedupMultiHeadEntry *head_entry, const NMPObject *obj, gboolean visible_only, gboolean contains) { - const NMPlatformObject *const *objects; - guint i, len; + NMDedupMultiIter iter; gboolean found; + guint i, len; + const NMPObject *o; - g_assert (cache_id); g_assert (NMP_OBJECT_IS_VALID (obj)); g_assert (nmp_cache_lookup_obj (cache, obj) == obj); + g_assert (!head_entry || (head_entry->len > 0 && c_list_length (&head_entry->lst_entries_head) == head_entry->len)); - objects = nmp_cache_lookup_multi (cache, cache_id, &len); - - g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len])); + len = head_entry ? head_entry->len : 0; found = FALSE; - for (i = 0; i < len; i++) { - NMPObject *o; - - g_assert (objects[i]); - o = NMP_OBJECT_UP_CAST (objects[i]); + i = 0; + nmp_cache_iter_for_each (&iter, + head_entry, + &o) { g_assert (NMP_OBJECT_IS_VALID (o)); - if (obj == o) { - g_assert (!found); - found = TRUE; + if ( !visible_only + || nmp_object_is_visible (o)) { + g_assert (!found); + found = TRUE; + } } + i++; } + g_assert (len == i); g_assert (!!contains == found); } -/*****************************************************************************/ +static void +_assert_cache_multi_lookup_contains_link (const NMPCache *cache, + gboolean visible_only, + const NMPObject *obj, + gboolean contains) +{ + const NMDedupMultiHeadEntry *head_entry; + NMPLookup lookup; -typedef struct { - NMPCache *cache; - NMPCacheOpsType expected_ops_type; - const NMPObject *obj_clone; - NMPObject *new_clone; - gboolean was_visible; - gboolean called; -} _NMPCacheUpdateData; + g_assert (cache); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + head_entry = nmp_cache_lookup (cache, &lookup); + _assert_cache_multi_lookup_contains (cache, head_entry, obj, visible_only, contains); +} + +/*****************************************************************************/ static void -_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +ops_post_check (NMPCache *cache, + NMPCacheOpsType ops_type, + const NMPObject *obj_old, + const NMPObject *obj_new, + const NMPObject *obj_new_expected, + NMPCacheOpsType expected_ops_type) { - _NMPCacheUpdateData *data = user_data; - - g_assert (data); - g_assert (!data->called); - g_assert (data->cache == cache); + g_assert (cache); - g_assert_cmpint (data->expected_ops_type, ==, ops_type); + g_assert_cmpint (expected_ops_type, ==, ops_type); switch (ops_type) { case NMP_CACHE_OPS_ADDED: - g_assert (!old); - g_assert (NMP_OBJECT_IS_VALID (new)); - g_assert (nmp_object_is_alive (new)); - g_assert (nmp_object_id_equal (data->obj_clone, new)); - g_assert (nmp_object_equal (data->obj_clone, new)); + g_assert (!obj_old); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (nmp_object_is_alive (obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + g_assert (nmp_object_equal (obj_new_expected, obj_new)); break; case NMP_CACHE_OPS_UPDATED: - g_assert (NMP_OBJECT_IS_VALID (old)); - g_assert (NMP_OBJECT_IS_VALID (new)); - g_assert (nmp_object_is_alive (old)); - g_assert (nmp_object_is_alive (new)); - g_assert (nmp_object_id_equal (data->obj_clone, new)); - g_assert (nmp_object_id_equal (data->obj_clone, old)); - g_assert (nmp_object_id_equal (old, new)); - g_assert (nmp_object_equal (data->obj_clone, new)); - g_assert (!nmp_object_equal (data->obj_clone, old)); - g_assert (!nmp_object_equal (old, new)); + g_assert (obj_old != obj_new); + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (nmp_object_is_alive (obj_old)); + g_assert (nmp_object_is_alive (obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_old)); + g_assert (nmp_object_id_equal (obj_old, obj_new)); + g_assert (nmp_object_equal (obj_new_expected, obj_new)); + g_assert (!nmp_object_equal (obj_new_expected, obj_old)); + g_assert (!nmp_object_equal (obj_old, obj_new)); break; case NMP_CACHE_OPS_REMOVED: - g_assert (!new); - g_assert (NMP_OBJECT_IS_VALID (old)); - g_assert (nmp_object_is_alive (old)); - g_assert (nmp_object_id_equal (data->obj_clone, old)); + g_assert (!obj_new); + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (nmp_object_is_alive (obj_old)); + if (obj_new_expected) + g_assert (nmp_object_id_equal (obj_new_expected, obj_old)); + break; + case NMP_CACHE_OPS_UNCHANGED: + g_assert (obj_old == obj_new); + if (obj_old) { + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (nmp_object_is_alive (obj_old)); + g_assert (nmp_object_equal (obj_old, obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + } else + g_assert (!obj_new_expected); break; default: g_assert_not_reached (); } - - data->was_visible = old ? nmp_object_is_visible (old) : FALSE; - data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL; - data->called = TRUE; } static void -_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type) +_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **out_obj_old, const NMPObject **out_obj_new, NMPCacheOpsType expected_ops_type) { NMPCacheOpsType ops_type; - NMPObject *obj2; - gboolean was_visible; - nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); - nm_auto_nmpobj NMPObject *new_clone = NULL; + const NMPObject *obj_prev; const NMPObject *obj_old; - _NMPCacheUpdateData data = { - .cache = cache, - .expected_ops_type = expected_ops_type, - .obj_clone = obj_clone, - }; - - obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex); - if (obj_old && obj_old->_link.udev.device) - obj_clone->_link.udev.device = udev_device_ref (obj_old->_link.udev.device); - _nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache)); + const NMPObject *obj_new; + nm_auto_nmpobj NMPObject *obj_new_expected = NULL; g_assert (cache); g_assert (NMP_OBJECT_IS_VALID (obj)); - ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data); - - new_clone = data.new_clone; - - g_assert_cmpint (ops_type, ==, expected_ops_type); + obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex); + obj_new_expected = nmp_object_clone (obj, FALSE); + if (obj_prev && obj_prev->_link.udev.device) + obj_new_expected->_link.udev.device = udev_device_ref (obj_prev->_link.udev.device); + _nmp_object_fixup_link_udev_fields (&obj_new_expected, NULL, nmp_cache_use_udev_get (cache)); - if (ops_type != NMP_CACHE_OPS_UNCHANGED) { - g_assert (NMP_OBJECT_IS_VALID (obj2)); - g_assert (data.called); - g_assert_cmpint (data.was_visible, ==, was_visible); + ops_type = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new); + ops_post_check (cache, ops_type, obj_old, obj_new, + nmp_object_is_alive (obj_new_expected) ? obj_new_expected : NULL, + expected_ops_type); - if (ops_type == NMP_CACHE_OPS_REMOVED) - g_assert (!data.new_clone); - else { - g_assert (data.new_clone); - g_assert (nmp_object_equal (obj2, data.new_clone)); - } - } else { - g_assert (!data.called); - g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2)); - } - - g_assert (!obj2 || nmp_object_id_equal (obj, obj2)); - if (ops_type != NMP_CACHE_OPS_REMOVED && obj2) - g_assert (nmp_object_equal (obj, obj2)); - - if (out_obj) - *out_obj = obj2; + if (out_obj_new) + *out_obj_new = obj_new; else - nmp_object_unref (obj2); - if (out_was_visible) - *out_was_visible = was_visible; + nmp_object_unref (obj_new); + if (out_obj_old) + *out_obj_old = obj_old; + else + nmp_object_unref (obj_old); } static const NMPlatformLink pl_link_2 = { @@ -218,168 +266,189 @@ static void test_cache_link (void) { NMPCache *cache; - NMPObject *obj1, *obj2; + NMPObject *objm1; + const NMPObject *obj_old, *obj_new; NMPObject objs1; - gboolean was_visible; - NMPCacheId cache_id_storage; struct udev_device *udev_device_2 = g_list_nth_data (global.udev_devices, 0); struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0); NMPCacheOpsType ops_type; + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; + + multi_idx = nm_dedup_multi_index_new (); - cache = nmp_cache_new (nmtst_get_rand_int () % 2); + cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2); /* if we have a link, and don't set is_in_netlink, adding it has no effect. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1); - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (NMP_OBJECT_UP_CAST (&objm1->object) == objm1); + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (!obj2); - g_assert (!was_visible); - g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (!obj_old); + g_assert (!obj_new); + g_assert (!nmp_cache_lookup_obj (cache, objm1)); g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); - nmp_object_unref (obj1); + nmp_object_unref (objm1); /* Only when setting @is_in_netlink the link is added. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (!obj_old); + g_assert (obj_new); + g_assert (objm1 == obj_new); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_new); /* updating the same link with identical value, has no effect. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (obj_old); + g_assert (obj_new); + g_assert (obj_new != objm1); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + nmp_object_unref (objm1); + nmp_object_unref (obj_new); + nmp_object_unref (obj_new); /* remove the link from netlink */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_REMOVED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (was_visible); - g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (obj_old); + g_assert (!obj_new); + g_assert (!nmp_cache_lookup_obj (cache, objm1)); g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); if (udev_device_2) { /* now add the link only with aspect UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj_old, &obj_new); ASSERT_nmp_cache_is_consistent (cache); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (!nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj2); + g_assert (!obj_old); + g_assert (obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (!nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + nmp_object_unref (obj_new); } /* add it in netlink too. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + if (udev_device_2) { + g_assert (obj_old); + g_assert (!nmp_object_is_visible (obj_old)); + } else + g_assert (!obj_old); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* remove again from netlink. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = FALSE; - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = FALSE; + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (was_visible); + if (udev_device_2) + g_assert (obj_new == objm1); + else + g_assert (!obj_new); + g_assert (obj_old); + g_assert (nmp_object_is_alive (obj_old)); if (udev_device_2) { - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (!nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (!nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); } else { - g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL); + g_assert (nmp_cache_lookup_obj (cache, objm1) == NULL); g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL); - g_assert (nmp_object_is_visible (obj2)); + g_assert (nmp_object_is_visible (obj_new)); } - nmp_object_unref (obj1); - nmp_object_unref (obj2); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* now another link only with aspect UDEV. */ if (udev_device_3) { /* now add the link only with aspect UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj_old, &obj_new); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (NMP_OBJECT_IS_VALID (obj2)); - g_assert (!was_visible); - g_assert (!nmp_object_is_visible (obj2)); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE); - g_assert_cmpint (obj2->link.initialized, ==, FALSE); - nmp_object_unref (obj2); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (!obj_old); + g_assert (!nmp_object_is_visible (obj_new)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, FALSE); + g_assert_cmpint (obj_new->link.initialized, ==, FALSE); + nmp_object_unref (obj_new); /* add it in netlink too. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UPDATED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint (obj2->link.initialized, ==, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (obj_old); + g_assert (obj_new == objm1); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (!obj_old || !nmp_object_is_visible (obj_old)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj_new->link.initialized, ==, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* remove UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj_old, &obj_new); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (was_visible); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache)); - nmp_object_unref (obj2); + g_assert (obj_old && nmp_object_is_visible (obj_old)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj_new->link.initialized, ==, !nmp_cache_use_udev_get (cache)); + nmp_object_unref (obj_new); + nmp_object_unref (obj_old); } nmp_cache_free (cache); @@ -429,6 +498,7 @@ main (int argc, char **argv) udev_enumerate_unref (enumerator); } + g_test_add_func ("/nmp-object/obj-base", test_obj_base); g_test_add_func ("/nmp-object/cache_link", test_cache_link); result = g_test_run (); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 9960d867..2c00fada 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -27,13 +27,50 @@ #include "test-common.h" -#define DEVICE_NAME "nm-test-device" +#define DEVICE_IFINDEX NMTSTP_ENV1_IFINDEX +#define EX NMTSTP_ENV1_EX + +static void +_wait_for_ipv6_addr_non_tentative (NMPlatform *platform, + gint64 timeout_ms, + int ifindex, + guint addr_n, + const struct in6_addr *addrs) +{ + guint i; + + /* Wait that the addresses become non-tentative. Dummy interfaces are NOARP + * and thus don't do DAD, but the kernel sets the address as tentative for a + * small amount of time, which prevents the immediate addition of the route + * with RTA_PREFSRC */ + + NMTST_WAIT_ASSERT (400, { + gboolean should_wait = FALSE; + const NMPlatformIP6Address *plt_addr; + + for (i = 0; i < addr_n; i++) { + plt_addr = nm_platform_ip6_address_get (platform, ifindex, addrs[i]); + if ( !plt_addr + || NM_FLAGS_HAS (plt_addr->n_ifa_flags, IFA_F_TENTATIVE)) { + should_wait = TRUE; + break; + } + } + if (!should_wait) + return; + nmtstp_assert_wait_for_signal (platform, + (nmtst_wait_end_us - g_get_monotonic_time ()) / 1000); + }); +} + static void ip4_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformIP4Route *received, int change_type_i, SignalData *data) { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; + NMPObject o_id; + nm_auto_nmpobj NMPObject *o_id_p = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert (received); @@ -41,6 +78,11 @@ ip4_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMP g_assert (data && data->name); g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED); + /* run code for initializing the ID only */ + nmp_object_stackinit_id (&o_id, NMP_OBJECT_UP_CAST (received)); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), TRUE); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), FALSE); + if (data->ifindex && data->ifindex != received->ifindex) return; if (data->change_type != change_type) @@ -58,6 +100,8 @@ ip6_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMP { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; + NMPObject o_id; + nm_auto_nmpobj NMPObject *o_id_p = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert (received); @@ -65,6 +109,11 @@ ip6_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMP g_assert (data && data->name); g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED); + /* run code for initializing the ID only */ + nmp_object_stackinit_id (&o_id, NMP_OBJECT_UP_CAST (received)); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), TRUE); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), FALSE); + if (data->ifindex && data->ifindex != received->ifindex) return; if (data->change_type != change_type) @@ -90,50 +139,50 @@ test_ip4_route_metric0 (void) int mss = 1000; /* No routes initially */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); /* add the first route */ nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss); accept_signal (route_added); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Deleting route with metric 0 does nothing */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); ensure_no_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* add the second route */ nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss); accept_signal (route_added); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete route with metric 0 */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); accept_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete route with metric 0 again (we expect nothing to happen) */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); ensure_no_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete the other route */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); free_signal (route_added); free_signal (route_changed); @@ -147,7 +196,7 @@ test_ip4_route (void) SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); - GArray *routes; + GPtrArray *routes; NMPlatformIP4Route rts[3]; in_addr_t network; guint8 plen = 24; @@ -164,9 +213,9 @@ test_ip4_route (void) accept_signal (route_added); /* Add route */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); accept_signal (route_added); /* Add route again */ @@ -174,9 +223,9 @@ test_ip4_route (void) accept_signals (route_changed, 0, 1); /* Add default route */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, 0, 0, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, 0, 0, metric, 0); nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, 0, 0, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, 0, 0, metric, 0); accept_signal (route_added); /* Add default route again */ @@ -184,7 +233,7 @@ test_ip4_route (void) accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].network = gateway; @@ -211,23 +260,23 @@ test_ip4_route (void) rts[2].mss = mss; rts[2].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); g_assert_cmpint (routes->len, ==, 3); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); - g_array_unref (routes); + nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); /* Remove default route */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, 0, 0, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, 0, 0, metric)); accept_signal (route_removed); /* Remove route to gateway */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, gateway, 32, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, gateway, 32, metric)); accept_signal (route_removed); free_signal (route_added); @@ -242,7 +291,7 @@ test_ip6_route (void) SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_route_callback); - GArray *routes; + GPtrArray *routes; NMPlatformIP6Route rts[3]; struct in6_addr network; guint8 plen = 64; @@ -259,28 +308,16 @@ test_ip6_route (void) NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0)); accept_signals (route_added, 0, 1); - /* Wait that the address becomes non-tentative. Dummy interfaces are NOARP - * and thus don't do DAD, but the kernel sets the address as tentative for a - * small amount of time, which prevents the immediate addition of the route - * with RTA_PREFSRC */ - NMTST_WAIT_ASSERT (200, { - const NMPlatformIP6Address *plt_addr; - - nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); - nm_platform_process_events (NM_PLATFORM_GET); - plt_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, pref_src, 128); - if (plt_addr && !NM_FLAGS_HAS (plt_addr->n_ifa_flags, IFA_F_TENTATIVE)) - break; - }); + _wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 200, ifindex, 1, &pref_src); /* Add route to gateway */ nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, in6addr_any, metric, mss); accept_signal (route_added); /* Add route */ - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss); - g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); accept_signal (route_added); /* Add route again */ @@ -288,9 +325,9 @@ test_ip6_route (void) accept_signals (route_changed, 0, 1); /* Add default route */ - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss); - g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); accept_signal (route_added); /* Add default route again */ @@ -298,7 +335,7 @@ test_ip6_route (void) accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].network = gateway; @@ -325,23 +362,23 @@ test_ip6_route (void) rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); - g_array_unref (routes); + nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); /* Remove default route */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); accept_signal (route_removed); /* Remove route to gateway */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, gateway, 128, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, gateway, 128, metric)); accept_signal (route_removed); free_signal (route_added); @@ -352,6 +389,45 @@ test_ip6_route (void) /*****************************************************************************/ static void +test_ip_route_get (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + in_addr_t a; + NMPlatformError result; + nm_auto_nmpobj NMPObject *route = NULL; + const NMPlatformIP4Route *r; + + nmtstp_run_command_check ("ip route add 1.2.3.0/24 dev %s", DEVICE_NAME); + + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (NM_PLATFORM_GET, 10); + if (nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.0"), 24, 0, 0)) + break; + }); + + a = nmtst_inet4_from_string ("1.2.3.1"); + result = nm_platform_ip_route_get (NM_PLATFORM_GET, + AF_INET, + &a, + nmtst_get_rand_int () % 2 ? 0 : ifindex, + &route); + + g_assert (result == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMP_OBJECT_GET_TYPE (route) == NMP_OBJECT_TYPE_IP4_ROUTE); + g_assert (!NMP_OBJECT_IS_STACKINIT (route)); + g_assert (route->parent._ref_count == 1); + r = NMP_OBJECT_CAST_IP4_ROUTE (route); + g_assert (r->rt_cloned); + g_assert (r->ifindex == ifindex); + g_assert (r->network == a); + g_assert (r->plen == 32); + + nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); + + nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); +} + +static void test_ip4_zero_gateway (void) { int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -361,15 +437,14 @@ test_ip4_zero_gateway (void) NMTST_WAIT_ASSERT (100, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 10); - if ( nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0) - && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0)) + if ( nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0, 0) + && nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0, 0)) break; }); nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); - nm_platform_process_events (NM_PLATFORM_GET); } static void @@ -378,7 +453,7 @@ test_ip4_route_options (void) int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); NMPlatformIP4Route route = { }; in_addr_t network; - GArray *routes; + GPtrArray *routes; NMPlatformIP4Route rts[1]; inet_pton (AF_INET, "172.16.1.0", &network); @@ -396,12 +471,10 @@ test_ip4_route_options (void) route.mtu = 1350; route.lock_cwnd = TRUE; - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, &route)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route) == NM_PLATFORM_ERROR_SUCCESS); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK); @@ -416,69 +489,268 @@ test_ip4_route_options (void) rts[0].initrwnd = 50; rts[0].mtu = 1350; rts[0].lock_cwnd = TRUE; - g_assert_cmpint (routes->len, ==, 1); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); + nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); /* Remove route */ - /* FIXME. Due to a bug, we cannot delete routes with non-zero TOS. See bgo#785004. */ - //g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20)); + g_assert (nm_platform_ip_route_delete (NM_PLATFORM_GET, routes->pdata[0])); - g_array_unref (routes); + g_ptr_array_unref (routes); } static void -test_ip6_route_options (void) +test_ip6_route_options (gconstpointer test_data) { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); - NMPlatformIP6Route route = { }; - struct in6_addr network; - GArray *routes; - NMPlatformIP6Route rts[3]; - - inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network); - - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - route.network = network; - route.plen = 64; - route.gateway = in6addr_any; - route.metric = 1024; - route.window = 20000; - route.cwnd = 8; - route.initcwnd = 22; - route.initrwnd = 33; - route.mtu = 1300; - route.lock_mtu = TRUE; - - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, &route)); - - /* Test route listing */ - routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - memset (rts, 0, sizeof (rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - rts[0].network = network; - rts[0].plen = 64; - rts[0].ifindex = ifindex; - rts[0].gateway = in6addr_any; - rts[0].metric = 1024; - rts[0].window = 20000; - rts[0].cwnd = 8; - rts[0].initcwnd = 22; - rts[0].initrwnd = 33; - rts[0].mtu = 1300; - rts[0].lock_mtu = TRUE; - - g_assert_cmpint (routes->len, ==, 1); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + GPtrArray *routes; +#define RTS_MAX 3 + NMPlatformIP6Route rts_add[RTS_MAX] = { }; + NMPlatformIP6Route rts_cmp[RTS_MAX] = { }; + NMPlatformIP6Address addr[1] = { }; + struct in6_addr addr_in6[G_N_ELEMENTS (addr)] = { }; + guint rts_n = 0; + guint addr_n = 0; + guint i; + + switch (TEST_IDX) { + case 1: + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:a:b:0:0:0:0"), + .plen = 64, + .gateway = in6addr_any, + .metric = 1024, + .window = 20000, + .cwnd = 8, + .initcwnd = 22, + .initrwnd = 33, + .mtu = 1300, + .lock_mtu = TRUE, + }); + break; + case 2: + addr[addr_n++] = ((NMPlatformIP6Address) { + .ifindex = IFINDEX, + .address = *nmtst_inet6_from_string ("2000::2"), + .plen = 128, + .peer_address = in6addr_any, + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .n_ifa_flags = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("1010::1"), + .plen = 128, + .gateway = in6addr_any, + .metric = 256, + .pref_src = *nmtst_inet6_from_string ("2000::2"), + }); + break; + case 3: + addr[addr_n++] = ((NMPlatformIP6Address) { + .ifindex = IFINDEX, + .address = *nmtst_inet6_from_string ("2001:db8:8086::5"), + .plen = 128, + .peer_address = in6addr_any, + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .n_ifa_flags = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 110, + .metric = 10021, + .mss = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), + .metric = 21, + .mss = 0, + }); + break; + default: + g_assert_not_reached (); + } + + for (i = 0; i < addr_n; i++) { + g_assert (addr[i].ifindex == IFINDEX); + addr_in6[i] = addr[i].address; + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, + IFINDEX, + addr[i].address, + addr[i].plen, + addr[i].peer_address, + addr[i].lifetime, + addr[i].preferred, + addr[i].n_ifa_flags)); + } + + _wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6); + + for (i = 0; i < rts_n; i++) + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS); + + routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX); + switch (TEST_IDX) { + case 1: + case 2: + case 3: + for (i = 0; i < rts_n; i++) { + rts_cmp[i] = rts_add[i]; + rts_cmp[i].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); + } + break; + default: + g_assert_not_reached (); + } + g_assert_cmpint (routes->len, ==, rts_n); + nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts_cmp, routes->len, TRUE); + g_ptr_array_unref (routes); + + for (i = 0; i < rts_n; i++) { + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, IFINDEX, + rts_add[i].network, rts_add[i].plen, + rts_add[i].metric)); + } + + for (i = 0; i < addr_n; i++) { + nmtstp_ip6_address_del (NM_PLATFORM_GET, + EX, + IFINDEX, + rts_add[i].network, + rts_add[i].plen); + } +} - /* Remove route */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, 64, 1024)); +/*****************************************************************************/ - g_array_unref (routes); +static void +test_ip (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + guint i, j, k; + const NMPlatformLink *l; + char ifname[IFNAMSIZ]; + char ifname2[IFNAMSIZ]; + char s1[NM_UTILS_INET_ADDRSTRLEN]; + NMPlatform *platform = NM_PLATFORM_GET; + const int EX_ = -1; + struct { + int ifindex; + } iface_data[10] = { 0 }; + int order_idx[G_N_ELEMENTS (iface_data)] = { 0 }; + guint order_len; + guint try; + + for (i = 0; i < G_N_ELEMENTS (iface_data); i++) { + nm_sprintf_buf (ifname, "v%02u", i); + nm_sprintf_buf (ifname2, "w%02u", i); + + g_assert (!nm_platform_link_get_by_ifname (platform, ifname)); + g_assert (!nm_platform_link_get_by_ifname (platform, ifname2)); + l = nmtstp_link_veth_add (platform, EX_, ifname, ifname2); + iface_data[i].ifindex = l->ifindex; + + nmtstp_link_set_updown (platform, EX_, iface_data[i].ifindex, TRUE); + nmtstp_link_set_updown (platform, EX_, nmtstp_link_get (platform, -1, ifname2)->ifindex, TRUE); + + nm_sprintf_buf (s1, "192.168.7.%d", 100 + i); + nmtstp_ip4_address_add (platform, + EX_, + iface_data[i].ifindex, + nmtst_inet4_from_string (s1), + 24, + nmtst_inet4_from_string (s1), + 3600, + 3600, + 0, + NULL); + } + + order_len = 0; + for (try = 0; try < 5 * G_N_ELEMENTS (order_idx); try++) { + NMPObject o; + NMPlatformIP4Route *r; + guint idx; + const NMDedupMultiHeadEntry *head_entry; + NMPLookup lookup; + + nmp_object_stackinit (&o, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + r = NMP_OBJECT_CAST_IP4_ROUTE (&o); + r->network = nmtst_inet4_from_string ("192.168.9.0"); + r->plen = 24; + r->metric = 109; + + if ( order_len == 0 + || ( order_len < G_N_ELEMENTS (order_idx) + && nmtst_get_rand_int () % 2)) { +again_find_idx: + idx = nmtst_get_rand_int () % G_N_ELEMENTS (iface_data); + for (i = 0; i < order_len; i++) { + if (order_idx[i] == idx) + goto again_find_idx; + } + order_idx[order_len++] = idx; + + r->ifindex = iface_data[idx].ifindex; + g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r) == NM_PLATFORM_ERROR_SUCCESS); + } else { + i = nmtst_get_rand_int () % order_len; + idx = order_idx[i]; + for (i++; i < order_len; i++) + order_idx[i - 1] = order_idx[i]; + order_len--; + + r->ifindex = iface_data[idx].ifindex; + g_assert (nm_platform_ip_route_delete (platform, &o)); + } + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_IP4_ROUTE)); + for (j = 0; j < G_N_ELEMENTS (iface_data); j++) { + gboolean has; + NMDedupMultiIter iter; + const NMPObject *o_cached; + + has = FALSE; + for (k = 0; k < order_len; k++) { + if (order_idx[k] == j) { + g_assert (!has); + has = TRUE; + } + } + + nmp_cache_iter_for_each (&iter, head_entry, &o_cached) { + const NMPlatformIP4Route *r_cached = NMP_OBJECT_CAST_IP4_ROUTE (o_cached); + + if ( r_cached->ifindex != iface_data[j].ifindex + || r_cached->metric != 109) + continue; + + g_assert (has); + has = FALSE; + } + g_assert (!has); + } + } + + for (i = 0; i < G_N_ELEMENTS (iface_data); i++) + g_assert (nm_platform_link_delete (platform, iface_data[i].ifindex)); + + (void) TEST_IDX; + (void) IFINDEX; } /*****************************************************************************/ @@ -494,22 +766,19 @@ _nmtstp_init_tests (int *argc, char ***argv) void _nmtstp_setup_tests (void) { - SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); - - g_test_add_func ("/route/ip4", test_ip4_route); - g_test_add_func ("/route/ip6", test_ip6_route); - g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0); - g_test_add_func ("/route/ip4_options", test_ip4_route_options); - g_test_add_func ("/route/ip6_options", test_ip6_route_options); - - if (nmtstp_is_root_test ()) - g_test_add_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); +#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE) +#define add_test_func_data(testpath, test_func, arg) nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE) + add_test_func ("/route/ip4", test_ip4_route); + add_test_func ("/route/ip6", test_ip6_route); + add_test_func ("/route/ip4_metric0", test_ip4_route_metric0); + add_test_func ("/route/ip4_options", test_ip4_route_options); + add_test_func_data ("/route/ip6_options/1", test_ip6_route_options, GINT_TO_POINTER (1)); + add_test_func_data ("/route/ip6_options/2", test_ip6_route_options, GINT_TO_POINTER (2)); + add_test_func_data ("/route/ip6_options/3", test_ip6_route_options, GINT_TO_POINTER (3)); + + if (nmtstp_is_root_test ()) { + add_test_func_data ("/route/ip/1", test_ip, GINT_TO_POINTER (1)); + add_test_func ("/route/ip_route_get", test_ip_route_get); + add_test_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); + } } diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index 06eb7cb9..a5f25b02 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -46,6 +46,24 @@ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END +/*****************************************************************************/ + +static int +_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); +} +#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) + +static int +_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); +} +#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) + /***************************************************************************** * Copied from libnl3/genl: *****************************************************************************/ @@ -131,7 +149,7 @@ genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen) static int genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], - int maxtype, struct nla_policy *policy) + int maxtype, const struct nla_policy *policy) { struct genlmsghdr *ghdr; @@ -150,7 +168,7 @@ genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], static int probe_response (struct nl_msg *msg, void *arg) { - static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, .maxlen = GENL_NAMSIZ }, @@ -389,7 +407,7 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg) struct nlattr *tb[NL80211_ATTR_MAX + 1]; if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), - genlmsg_attrlen (gnlh, 0), NULL) < 0) + genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; if (!tb[NL80211_ATTR_IFTYPE]) @@ -529,7 +547,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct nlattr *bss[NL80211_BSS_MAX + 1]; - static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + static const struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { [NL80211_BSS_TSF] = { .type = NLA_U64 }, [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, [NL80211_BSS_BSSID] = { }, @@ -543,15 +561,15 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) guint32 status; if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), - genlmsg_attrlen (gnlh, 0), NULL) < 0) + genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; if (tb[NL80211_ATTR_BSS] == NULL) return NL_SKIP; if (nla_parse_nested (bss, NL80211_BSS_MAX, - tb[NL80211_ATTR_BSS], - bss_policy)) + tb[NL80211_ATTR_BSS], + bss_policy)) return NL_SKIP; if (bss[NL80211_BSS_STATUS] == NULL) @@ -665,7 +683,7 @@ nl80211_station_handler (struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; - static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + static const struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, @@ -678,7 +696,7 @@ nl80211_station_handler (struct nl_msg *msg, void *arg) [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, }; - static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + static const struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, @@ -871,7 +889,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) int rem_freq; int rem_band; int freq_idx; - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + static const struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, #ifdef NL80211_FREQUENCY_ATTR_NO_IR diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c index 1bc29ae8..c4d3c999 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/wifi-utils-wext.c @@ -97,8 +97,7 @@ wifi_wext_deinit (WifiData *parent) { WifiDataWext *wext = (WifiDataWext *) parent; - if (wext->fd >= 0) - close (wext->fd); + nm_close (wext->fd); } static gboolean @@ -757,7 +756,7 @@ wifi_wext_is_wifi (const char *iface) nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface); if (ioctl (fd, SIOCGIWNAME, &iwr) == 0) is_wifi = TRUE; - close (fd); + nm_close (fd); } return is_wifi; } |