From 4c49d78f4949c1bcbc2107ee42700f28d333633a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 14 Apr 2015 12:53:20 +0200 Subject: [PATCH 01/50] platform: return @now from _rtnl_addr_last_update_time_to_nm() Will be used later. --- src/platform/nm-linux-platform.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f1b863e1e9..cc7a5b75cb 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1069,15 +1069,18 @@ _timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) } static guint32 -_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr) +_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr, gint32 *out_now_nm) { guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr); struct timespec tp; gint64 now_nl, now_nm, result; /* timestamp is unset. Default to 1. */ - if (!last_update_time) + if (!last_update_time) { + if (out_now_nm) + *out_now_nm = 0; return 1; + } /* do all the calculations in milliseconds scale */ @@ -1088,6 +1091,9 @@ _rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr) result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl)); + if (out_now_nm) + *out_now_nm = now_nm / 1000; + /* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is * a good guess but fails in the following situations: * @@ -1143,7 +1149,7 @@ _init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr * timestamp to have any meaning beyond anchoring the relative durations * @lifetime and @preferred. */ - address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr); + address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr, NULL); /* We would expect @timestamp to be less then @a_valid. Just to be sure, * fix it up. */ From 619f660a3e1728a823add757e53b093c76869fc9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 3 May 2015 10:47:41 +0200 Subject: [PATCH 02/50] platform: add scope parameter to NMPlatformIP4Route Cache the scope as part of the NMPlatformIP4Route and no longer read it from libnl object when needed. Later there will be no more libnl objects around, and we need to scope when deleting an IPv4 route. --- src/platform/nm-fake-platform.c | 5 +++++ src/platform/nm-linux-platform.c | 3 ++- src/platform/nm-platform.c | 9 +++++++-- src/platform/nm-platform.h | 20 ++++++++++++++++++++ src/platform/tests/test-route.c | 7 +++++-- src/tests/test-route-manager.c | 9 +++++++++ 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index d5843a1edf..479e2fbf5c 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "gsystem-local-alloc.h" #include "NetworkManagerUtils.h" @@ -1196,6 +1197,9 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMPlatformIP4Route route; guint i; + guint8 scope; + + scope = gateway == 0 ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; memset (&route, 0, sizeof (route)); route.source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -1206,6 +1210,7 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, route.gateway = gateway; route.metric = metric; route.mss = mss; + route.scope_inv = nm_platform_route_scope_inv (scope); if (gateway) { for (i = 0; i < priv->ip4_routes->len; i++) { diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index cc7a5b75cb..1b547d97a8 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1305,6 +1305,7 @@ init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute) route->metric = rtnl_route_get_priority (rtnlroute); rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute)); + route->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (rtnlroute)); return TRUE; } @@ -3684,7 +3685,7 @@ build_rtnl_addr (NMPlatform *platform, /* Tighten scope (IPv4 only) */ if (family == AF_INET && ip4_is_link_local (addr)) - rtnl_addr_set_scope (rtnladdr, rtnl_str2scope ("link")); + rtnl_addr_set_scope (rtnladdr, RT_SCOPE_LINK); /* IPv4 Broadcast address */ if (family == AF_INET) { diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 7439b1ef42..c873b6aa48 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "gsystem-local-alloc.h" #include "NetworkManagerUtils.h" @@ -2646,6 +2647,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) { char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_scope[30]; g_return_val_if_fail (route, "(unknown)"); @@ -2654,11 +2656,13 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", + g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s%s%s", s_network, route->plen, s_gateway, str_dev, route->metric, route->mss, - source_to_string (route->source)); + source_to_string (route->source), + route->scope_inv ? " scope " : "", + route->scope_inv ? (rtnl_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : ""); return to_string_buffer; } @@ -2812,6 +2816,7 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route _CMP_FIELD (a, b, gateway); _CMP_FIELD (a, b, metric); _CMP_FIELD (a, b, mss); + _CMP_FIELD (a, b, scope_inv); return 0; } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index a4482d2458..3e3aafb5db 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -237,6 +237,10 @@ 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; }; G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); @@ -529,6 +533,22 @@ NMPlatform *nm_platform_try_get (void); /******************************************************************/ +/** + * nm_platform_route_scope_inv: + * @scope: the route scope, either its original value, or its inverse. + * + * This function is useful, because the constants such as RT_SCOPE_NOWHERE + * are 'int', so ~scope also gives an 'int'. This function gets the type + * casts to guint8 right. + * + * Returns: the bitwise inverse of the route scope. + * */ +static inline guint8 +nm_platform_route_scope_inv (guint8 scope) +{ + return (guint8) ~scope; +} + const char *nm_link_type_to_string (NMLinkType link_type); void nm_platform_set_error (NMPlatform *self, NMPlatformError error); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 70bd1a1040..fd86719958 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -1,5 +1,7 @@ #include "config.h" +#include + #include "test-common.h" #include "nm-test-utils.h" #include "NetworkManagerUtils.h" @@ -177,6 +179,7 @@ test_ip4_route (void) rts[0].gateway = INADDR_ANY; rts[0].metric = metric; rts[0].mss = mss; + rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK); rts[1].source = NM_IP_CONFIG_SOURCE_USER; rts[1].network = network; rts[1].plen = plen; @@ -184,6 +187,7 @@ test_ip4_route (void) rts[1].gateway = gateway; rts[1].metric = metric; rts[1].mss = mss; + rts[1].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); rts[2].source = NM_IP_CONFIG_SOURCE_USER; rts[2].network = 0; rts[2].plen = 0; @@ -191,8 +195,8 @@ test_ip4_route (void) rts[2].gateway = gateway; rts[2].metric = metric; rts[2].mss = mss; + rts[2].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); g_assert_cmpint (routes->len, ==, 3); - g_assert (!memcmp (routes->data, rts, sizeof (rts))); nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); @@ -288,7 +292,6 @@ test_ip6_route (void) rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); - g_assert (!memcmp (routes->data, rts, sizeof (rts))); nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index c7b60e68e0..5b1c5f9d5a 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -22,6 +22,7 @@ #include #include +#include #include "test-common.h" @@ -168,6 +169,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 20, .mss = 1000, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, { .source = NM_IP_CONFIG_SOURCE_USER, @@ -177,6 +179,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = nmtst_inet4_from_string ("6.6.6.1"), .metric = 21021, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), }, { .source = NM_IP_CONFIG_SOURCE_USER, @@ -186,6 +189,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 22, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, }; @@ -198,6 +202,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 20, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, { .source = NM_IP_CONFIG_SOURCE_USER, @@ -207,6 +212,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 21, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, { .source = NM_IP_CONFIG_SOURCE_USER, @@ -216,6 +222,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 22, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, }; @@ -228,6 +235,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 22, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, { .source = NM_IP_CONFIG_SOURCE_USER, @@ -237,6 +245,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .gateway = INADDR_ANY, .metric = 20, .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), }, }; From b307abc0106ec0c556d581949d1fe92d57874239 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 25 Apr 2015 16:42:26 +0200 Subject: [PATCH 03/50] platform: add flags parameter to NMPlatformLink --- src/platform/nm-linux-platform.c | 7 ++++--- src/platform/nm-platform.c | 7 +++++++ src/platform/nm-platform.h | 12 ++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1b547d97a8..c62d36d56f 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -941,9 +941,10 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin info->name[0] = '\0'; info->type = link_extract_type (platform, rtnllink); info->kind = g_intern_string (rtnl_link_get_type (rtnllink)); - info->up = !!(rtnl_link_get_flags (rtnllink) & IFF_UP); - info->connected = !!(rtnl_link_get_flags (rtnllink) & IFF_LOWER_UP); - info->arp = !(rtnl_link_get_flags (rtnllink) & IFF_NOARP); + info->flags = rtnl_link_get_flags (rtnllink); + info->up = NM_FLAGS_HAS (info->flags, IFF_UP); + info->connected = NM_FLAGS_HAS (info->flags, IFF_LOWER_UP); + info->arp = !NM_FLAGS_HAS (info->flags, IFF_NOARP); info->master = rtnl_link_get_master (rtnllink); info->parent = rtnl_link_get_link (rtnllink); info->mtu = rtnl_link_get_mtu (rtnllink); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index c873b6aa48..19587eeb36 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2440,6 +2440,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) { char master[20]; char parent[20]; + char str_flags[64]; char *driver, *udi; GString *str; @@ -2456,6 +2457,11 @@ nm_platform_link_to_string (const NMPlatformLink *link) if (link->connected) g_string_append (str, ",LOWER_UP"); + if (link->flags) { + rtnl_link_flags2str (link->flags, str_flags, sizeof (str_flags)); + g_string_append_printf (str, ";%s", str_flags); + } + if (link->master) g_snprintf (master, sizeof (master), " master %d", link->master); else @@ -2763,6 +2769,7 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); _CMP_FIELD (a, b, up); + _CMP_FIELD (a, b, flags); _CMP_FIELD (a, b, connected); _CMP_FIELD (a, b, arp); _CMP_FIELD (a, b, mtu); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 3e3aafb5db..bb558699c7 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -100,9 +100,21 @@ struct _NMPlatformLink { gboolean initialized; int master; int parent; + + /* IFF_* flags as u32. Note that ifi_flags in 'struct ifinfomsg' is declared as 'unsigned', + * but libnl stores the flag internally as u32. */ + guint32 flags; + + /* FIXME: @up is redundant to (@flags & IFF_UP) */ gboolean up; + + /* @connected is mostly identical to (@flags & IFF_UP). Except for bridge/bond masters, + * where we coerce the link as disconnect if it has no slaves. */ gboolean connected; + + /* FIXME: @arp is redundant to !(@flags & IFF_NOARP) */ gboolean arp; + guint mtu; }; From 3cdde18f52235e5bc9892a871a88ec84df63bf5d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 25 Apr 2015 16:42:26 +0200 Subject: [PATCH 04/50] platform: add vlan_id parameter to NMPlatformLink --- src/platform/nm-linux-platform.c | 3 +++ src/platform/nm-platform.c | 9 +++++++++ src/platform/nm-platform.h | 3 +++ 3 files changed, 15 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index c62d36d56f..a711ae2fa9 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -949,6 +949,9 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin info->parent = rtnl_link_get_link (rtnllink); info->mtu = rtnl_link_get_mtu (rtnllink); + if (info->type == NM_LINK_TYPE_VLAN) + info->vlan_id = rtnl_link_vlan_get_id (rtnllink); + udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); if (udev_device) { info->driver = nmp_utils_udev_get_driver (udev_device); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 19587eeb36..0d9ae57e36 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2442,6 +2442,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) char parent[20]; char str_flags[64]; char *driver, *udi; + char str_vlan[16]; GString *str; if (!link) @@ -2472,18 +2473,25 @@ nm_platform_link_to_string (const NMPlatformLink *link) else parent[0] = 0; + if (link->vlan_id) + g_snprintf (str_vlan, sizeof (str_vlan), " vlan %u", (guint) link->vlan_id); + else + str_vlan[0] = '\0'; + driver = link->driver ? g_strdup_printf (" driver '%s'", link->driver) : NULL; udi = link->udi ? g_strdup_printf (" udi '%s'", link->udi) : NULL; g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%d: %s%s <%s> mtu %d%s " "%s" /* link->type */ "%s%s" /* kind */ + "%s" /* vlan */ "%s%s", link->ifindex, link->name, parent, str->str, link->mtu, master, nm_link_type_to_string (link->type), link->type != NM_LINK_TYPE_UNKNOWN && link->kind ? " kind " : "", link->type != NM_LINK_TYPE_UNKNOWN && link->kind ? link->kind : "", + str_vlan, driver ? driver : "", udi ? udi : ""); g_string_free (str, TRUE); g_free (driver); @@ -2769,6 +2777,7 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); _CMP_FIELD (a, b, up); + _CMP_FIELD (a, b, vlan_id); _CMP_FIELD (a, b, flags); _CMP_FIELD (a, b, connected); _CMP_FIELD (a, b, arp); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index bb558699c7..8230ee9c40 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -101,6 +101,9 @@ struct _NMPlatformLink { int master; int parent; + /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ + guint16 vlan_id; + /* IFF_* flags as u32. Note that ifi_flags in 'struct ifinfomsg' is declared as 'unsigned', * but libnl stores the flag internally as u32. */ guint32 flags; From 36e77218a0d54b6f9be3a9d98efb145dda6b52ad Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Apr 2015 18:06:59 +0200 Subject: [PATCH 05/50] platform: add addr parameter to NMPlatformLink --- src/platform/nm-linux-platform.c | 12 ++++++++++++ src/platform/nm-platform.c | 13 +++++++++++++ src/platform/nm-platform.h | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index a711ae2fa9..0a2c81a895 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -928,6 +928,7 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin GUdevDevice *udev_device; const char *name; char *tmp; + struct nl_addr *nladdr; g_return_val_if_fail (rtnllink, FALSE); @@ -952,6 +953,17 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin if (info->type == NM_LINK_TYPE_VLAN) info->vlan_id = rtnl_link_vlan_get_id (rtnllink); + if ((nladdr = rtnl_link_get_addr (rtnllink))) { + unsigned int l = 0; + + l = nl_addr_get_len (nladdr); + if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (info->addr.data)); + memcpy (info->addr.data, nl_addr_get_binary_addr (nladdr), l); + info->addr.len = l; + } + } + udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); if (udev_device) { info->driver = nmp_utils_udev_get_driver (udev_device); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 0d9ae57e36..3c91f6d242 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -38,6 +38,8 @@ #include "nm-enum-types.h" #include "nm-core-internal.h" +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX); + #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate)) @@ -2759,6 +2761,14 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) 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), \ @@ -2783,9 +2793,12 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, arp); _CMP_FIELD (a, b, mtu); _CMP_FIELD_BOOL (a, b, initialized); + _CMP_FIELD (a, b, addr.len); _CMP_FIELD_STR_INTERNED (a, b, kind); _CMP_FIELD_STR0 (a, b, udi); _CMP_FIELD_STR_INTERNED (a, b, driver); + if (a->addr.len) + _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); return 0; } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 8230ee9c40..91245e5b0b 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -101,6 +101,12 @@ struct _NMPlatformLink { int master; int parent; + /* rtnl_link_get_addr() */ + struct { + guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ + guint8 len; + } addr; + /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ guint16 vlan_id; From 5b2b62ff51f21d7d8f72fec52988aa0822d8645c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Apr 2015 18:32:43 +0200 Subject: [PATCH 06/50] platform: add inet6_addr_get_mode_inv parameter to NMPlatformLink --- src/platform/nm-linux-platform.c | 9 +++++++++ src/platform/nm-platform.c | 1 + src/platform/nm-platform.h | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 0a2c81a895..1b5afda2e9 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -964,6 +964,15 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin } } +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + if (_support_user_ipv6ll_get ()) { + guint8 mode = 0; + + if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) == 0) + info->inet6_addr_gen_mode_inv = ~mode; + } +#endif + udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); if (udev_device) { info->driver = nmp_utils_udev_get_driver (udev_device); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 3c91f6d242..9a73db60fb 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2794,6 +2794,7 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, mtu); _CMP_FIELD_BOOL (a, b, initialized); _CMP_FIELD (a, b, addr.len); + _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); _CMP_FIELD_STR_INTERNED (a, b, kind); _CMP_FIELD_STR0 (a, b, udi); _CMP_FIELD_STR_INTERNED (a, b, driver); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 91245e5b0b..05811c5ee8 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -107,6 +107,11 @@ struct _NMPlatformLink { guint8 len; } addr; + /* The bitwise inverse of rtnl_link_inet6_get_addr_gen_mode(). It is inverse + * to have a default of 0 -- meaning: unspecified. That way, a struct + * initialized with memset(0) has and unset value.*/ + guint8 inet6_addr_gen_mode_inv; + /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ guint16 vlan_id; From 3ce4431695710b68cff697d5638283efb24c337e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Apr 2015 18:45:23 +0200 Subject: [PATCH 07/50] platform: add arptype parameter to NMPlatformLink --- src/platform/nm-linux-platform.c | 1 + src/platform/nm-platform.c | 1 + src/platform/nm-platform.h | 3 +++ 3 files changed, 5 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1b5afda2e9..9cb6392644 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -949,6 +949,7 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin info->master = rtnl_link_get_master (rtnllink); info->parent = rtnl_link_get_link (rtnllink); info->mtu = rtnl_link_get_mtu (rtnllink); + info->arptype = rtnl_link_get_arptype (rtnllink); if (info->type == NM_LINK_TYPE_VLAN) info->vlan_id = rtnl_link_vlan_get_id (rtnllink); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 9a73db60fb..9ed624ed29 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2793,6 +2793,7 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, arp); _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); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 05811c5ee8..390e7a1f88 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -101,6 +101,9 @@ struct _NMPlatformLink { int master; int parent; + /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */ + guint32 arptype; + /* rtnl_link_get_addr() */ struct { guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ From 32ab8c18f54fe0979b06ec50d0e4344184cc9bfd Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Apr 2015 21:05:13 +0200 Subject: [PATCH 08/50] platform: add inet6_token parameter to NMPlatformLink --- src/platform/nm-linux-platform.c | 22 ++++++++++++++++++++++ src/platform/nm-platform.c | 3 +++ src/platform/nm-platform.h | 7 +++++++ 3 files changed, 32 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 9cb6392644..d64b8f35e4 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -974,6 +974,28 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin } #endif +#if HAVE_LIBNL_INET6_TOKEN + if ((rtnl_link_inet6_get_token (rtnllink, &nladdr)) == 0) { + if ( nl_addr_get_family (nladdr) == AF_INET6 + && nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) { + struct in6_addr *addr; + NMUtilsIPv6IfaceId *iid = &info->inet6_token.iid; + + addr = nl_addr_get_binary_addr (nladdr); + iid->id_u8[7] = addr->s6_addr[15]; + iid->id_u8[6] = addr->s6_addr[14]; + iid->id_u8[5] = addr->s6_addr[13]; + iid->id_u8[4] = addr->s6_addr[12]; + iid->id_u8[3] = addr->s6_addr[11]; + iid->id_u8[2] = addr->s6_addr[10]; + iid->id_u8[1] = addr->s6_addr[9]; + iid->id_u8[0] = addr->s6_addr[8]; + info->inet6_token.is_valid = TRUE; + } + nl_addr_put (nladdr); + } +#endif + udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); if (udev_device) { info->driver = nmp_utils_udev_get_driver (udev_device); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 9ed624ed29..c98ed28d1b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2796,11 +2796,14 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, arptype); _CMP_FIELD (a, b, addr.len); _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); + _CMP_FIELD (a, b, inet6_token.is_valid); _CMP_FIELD_STR_INTERNED (a, b, kind); _CMP_FIELD_STR0 (a, b, udi); _CMP_FIELD_STR_INTERNED (a, b, driver); if (a->addr.len) _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); + if (a->inet6_token.is_valid) + _CMP_FIELD_MEMCMP (a, b, inet6_token.iid); return 0; } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 390e7a1f88..ec370b8733 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -29,6 +29,7 @@ #include #include "nm-types.h" +#include "NetworkManagerUtils.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) #define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform)) @@ -110,6 +111,12 @@ struct _NMPlatformLink { guint8 len; } addr; + /* rtnl_link_inet6_get_token() */ + struct { + NMUtilsIPv6IfaceId iid; + guint8 is_valid; + } inet6_token; + /* The bitwise inverse of rtnl_link_inet6_get_addr_gen_mode(). It is inverse * to have a default of 0 -- meaning: unspecified. That way, a struct * initialized with memset(0) has and unset value.*/ From 806109c245eeae5f5d7b4e56ca00970df47aa1b1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Apr 2015 15:39:36 +0200 Subject: [PATCH 09/50] platform/trivial: rename to_string_buffer variable --- src/platform/nm-platform.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index c98ed28d1b..5adc1b2090 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2435,7 +2435,7 @@ _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, g return buf; } -static char to_string_buffer[256]; +static char _nm_platform_to_string_buffer[256]; const char * nm_platform_link_to_string (const NMPlatformLink *link) @@ -2483,7 +2483,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) driver = link->driver ? g_strdup_printf (" driver '%s'", link->driver) : NULL; udi = link->udi ? g_strdup_printf (" udi '%s'", link->udi) : NULL; - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%d: %s%s <%s> mtu %d%s " + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%d: %s%s <%s> mtu %d%s " "%s" /* link->type */ "%s%s" /* kind */ "%s" /* vlan */ @@ -2498,7 +2498,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) g_string_free (str, TRUE); g_free (driver); g_free (udi); - return to_string_buffer; + return _nm_platform_to_string_buffer; } /** @@ -2551,14 +2551,14 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) now, str_pref, sizeof (str_pref)) ); str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", s_address, address->plen, str_lft_p, str_pref_p, str_time_p, str_peer ? str_peer : "", str_dev, str_label, source_to_string (address->source)); g_free (str_peer); - return to_string_buffer; + return _nm_platform_to_string_buffer; } /** @@ -2635,7 +2635,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) now, str_pref, sizeof (str_pref)) ); str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", s_address, address->plen, str_lft_p, str_pref_p, str_time_p, str_peer ? str_peer : "", str_dev, @@ -2643,7 +2643,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) source_to_string (address->source)); g_free (str_flags); g_free (str_peer); - return to_string_buffer; + return _nm_platform_to_string_buffer; } /** @@ -2672,14 +2672,14 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s%s%s", + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s%s%s", s_network, route->plen, s_gateway, str_dev, route->metric, route->mss, source_to_string (route->source), route->scope_inv ? " scope " : "", route->scope_inv ? (rtnl_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : ""); - return to_string_buffer; + return _nm_platform_to_string_buffer; } /** @@ -2707,12 +2707,12 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", s_network, route->plen, s_gateway, str_dev, route->metric, route->mss, source_to_string (route->source)); - return to_string_buffer; + return _nm_platform_to_string_buffer; } #define _CMP_POINTER(a, b) \ From 655a0e71d485b46d385453370be2f6c6760461ce Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Apr 2015 15:41:33 +0200 Subject: [PATCH 10/50] platform: expose internal buffer for to_string() functions I'd like to reuse this buffer for other to-string functions that are platform related. --- src/platform/nm-platform.c | 2 +- src/platform/nm-platform.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 5adc1b2090..0963e00161 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2435,7 +2435,7 @@ _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, g return buf; } -static char _nm_platform_to_string_buffer[256]; +char _nm_platform_to_string_buffer[256]; const char * nm_platform_link_to_string (const NMPlatformLink *link) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index ec370b8733..c6145c56d2 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -315,6 +315,7 @@ typedef struct { extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; +extern char _nm_platform_to_string_buffer[256]; typedef struct { int peer; From c34598fa233e339018b57fe93ab8cf731f8fea02 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 5 May 2015 01:25:19 +0200 Subject: [PATCH 11/50] platform: print more fields in nm_platform_link_to_string() --- src/platform/nm-platform.c | 92 ++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 0963e00161..a4f57a5983 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2442,27 +2442,30 @@ nm_platform_link_to_string (const NMPlatformLink *link) { char master[20]; char parent[20]; - char str_flags[64]; - char *driver, *udi; char str_vlan[16]; - GString *str; + GString *str_flags; + char str_addrmode[30]; + gs_free char *str_addr = NULL; + gs_free char *str_inet6_token = NULL; if (!link) return "(unknown link)"; - str = g_string_new (NULL); + str_flags = g_string_new (NULL); if (!link->arp) - g_string_append (str, "NOARP,"); + g_string_append (str_flags, "NOARP,"); if (link->up) - g_string_append (str, "UP"); + g_string_append (str_flags, "UP"); else - g_string_append (str, "DOWN"); + g_string_append (str_flags, "DOWN"); if (link->connected) - g_string_append (str, ",LOWER_UP"); + g_string_append (str_flags, ",LOWER_UP"); if (link->flags) { - rtnl_link_flags2str (link->flags, str_flags, sizeof (str_flags)); - g_string_append_printf (str, ";%s", str_flags); + char str_flags_buf[64]; + + rtnl_link_flags2str (link->flags, str_flags_buf, sizeof (str_flags_buf)); + g_string_append_printf (str_flags, ";%s", str_flags_buf); } if (link->master) @@ -2480,24 +2483,65 @@ nm_platform_link_to_string (const NMPlatformLink *link) else str_vlan[0] = '\0'; - driver = link->driver ? g_strdup_printf (" driver '%s'", link->driver) : NULL; - udi = link->udi ? g_strdup_printf (" udi '%s'", link->udi) : NULL; + if (link->inet6_addr_gen_mode_inv) { + switch ((guint8) ~link->inet6_addr_gen_mode_inv) { + case 0: + g_snprintf (str_addrmode, sizeof (str_addrmode), " addrgenmode eui64"); + break; + case 1: + g_snprintf (str_addrmode, sizeof (str_addrmode), " addrgenmode none"); + break; + default: + g_snprintf (str_addrmode, sizeof (str_addrmode), " addrgenmode %d", (int) (guint8) (~link->inet6_addr_gen_mode_inv)); + break; + } + } else + str_addrmode[0] = '\0'; - g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%d: %s%s <%s> mtu %d%s " - "%s" /* link->type */ - "%s%s" /* kind */ + if (link->addr.len) + str_addr = nm_utils_hwaddr_ntoa (link->addr.data, MIN (link->addr.len, sizeof (link->addr.data))); + if (link->inet6_token.is_valid) + str_inet6_token = nm_utils_hwaddr_ntoa (&link->inet6_token.iid, sizeof (link->inet6_token.iid)); + + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + "%d: " /* ifindex */ + "%s" /* name */ + "%s" /* parent */ + " <%s>" /* flags */ + " mtu %d" + "%s" /* master */ "%s" /* vlan */ - "%s%s", - link->ifindex, link->name, parent, str->str, + " arp %u" /* arptype */ + "%s%s" /* link->type */ + "%s%s" /* kind */ + "%s" /* is-in-udev */ + "%s" /* addr-gen-mode */ + "%s%s" /* addr */ + "%s%s" /* inet6_token */ + "%s%s" /* driver */ + "%s%s", /* udi */ + link->ifindex, + link->name, + parent, + str_flags->str, link->mtu, master, - nm_link_type_to_string (link->type), - link->type != NM_LINK_TYPE_UNKNOWN && link->kind ? " kind " : "", - link->type != NM_LINK_TYPE_UNKNOWN && link->kind ? link->kind : "", str_vlan, - driver ? driver : "", udi ? udi : ""); - g_string_free (str, TRUE); - g_free (driver); - g_free (udi); + link->arptype, + nm_link_type_to_string (link->type) ? " " : "", + str_if_set (nm_link_type_to_string (link->type), "???"), + link->kind ? (g_strcmp0 (nm_link_type_to_string (link->type), link->kind) ? "/" : "*") : "", + link->kind && g_strcmp0 (nm_link_type_to_string (link->type), link->kind) ? link->kind : "", + link->initialized ? " init" : " not-init", + str_addrmode, + str_addr ? " addr " : "", + str_addr ? str_addr : "", + str_inet6_token ? " inet6token " : "", + str_inet6_token ? str_inet6_token : "", + link->driver ? " driver " : "", + link->driver ? link->driver : "", + link->udi ? " udi " : "", + link->udi ? link->udi : ""); + g_string_free (str_flags, TRUE); return _nm_platform_to_string_buffer; } From 304abc909a07beb1a53e61739d57e2cac79ed9e0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 14 Apr 2015 16:39:51 +0200 Subject: [PATCH 12/50] platform: implement dispose() in NMLinuxPlatform --- src/platform/nm-linux-platform.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d64b8f35e4..41a84b8ade 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -4665,6 +4665,12 @@ constructed (GObject *_object) G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); } +static void +dispose (GObject *object) +{ + G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); +} + static void nm_linux_platform_finalize (GObject *object) { @@ -4698,6 +4704,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) /* virtual methods */ object_class->constructed = constructed; + object_class->dispose = dispose; object_class->finalize = nm_linux_platform_finalize; platform_class->sysctl_set = sysctl_set; From 64d918293bdf3e16c8a53d4146a5343ec6c9ffee Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 6 Apr 2015 20:01:05 +0200 Subject: [PATCH 13/50] platform: add _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL source Later we will need this flag to distinguish routes from kernel that have source RTPROT_KERNEL. This flag is still unused. --- src/nm-types.h | 4 ++++ src/platform/nm-linux-platform.c | 12 ++++++++---- src/platform/nm-platform.c | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/nm-types.h b/src/nm-types.h index 3fa587932e..ddcee4daba 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -51,6 +51,10 @@ typedef struct _NMSleepMonitor NMSleepMonitor; typedef enum { /* In priority order; higher number == higher priority */ NM_IP_CONFIG_SOURCE_UNKNOWN, + + /* platform internal flag used to mark routes with protocol RTPROT_KERNEL. */ + _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, + NM_IP_CONFIG_SOURCE_KERNEL, NM_IP_CONFIG_SOURCE_SHARED, NM_IP_CONFIG_SOURCE_IP4LL, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 41a84b8ade..d326108a06 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1277,6 +1277,7 @@ source_to_rtprot (NMIPConfigSource source) case NM_IP_CONFIG_SOURCE_UNKNOWN: return RTPROT_UNSPEC; case NM_IP_CONFIG_SOURCE_KERNEL: + case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: return RTPROT_KERNEL; case NM_IP_CONFIG_SOURCE_DHCP: return RTPROT_DHCP; @@ -1289,13 +1290,16 @@ source_to_rtprot (NMIPConfigSource source) } static NMIPConfigSource -rtprot_to_source (guint rtprot) +rtprot_to_source (guint rtprot, gboolean preserve_rtprot) { switch (rtprot) { case RTPROT_UNSPEC: return NM_IP_CONFIG_SOURCE_UNKNOWN; - case RTPROT_REDIRECT: case RTPROT_KERNEL: + if (preserve_rtprot) + return _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; + /* fall through */ + case RTPROT_REDIRECT: return NM_IP_CONFIG_SOURCE_KERNEL; case RTPROT_RA: return NM_IP_CONFIG_SOURCE_RDISC; @@ -1352,7 +1356,7 @@ init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute) } route->metric = rtnl_route_get_priority (rtnlroute); rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); - route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute)); + route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute), FALSE); route->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (rtnlroute)); return TRUE; @@ -1393,7 +1397,7 @@ init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) } route->metric = rtnl_route_get_priority (rtnlroute); rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); - route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute)); + route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute), FALSE); return TRUE; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index a4f57a5983..23498cf5a9 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2391,6 +2391,8 @@ static const char * source_to_string (NMIPConfigSource source) { switch (source) { + case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + return "rtprot-kernel"; case NM_IP_CONFIG_SOURCE_KERNEL: return "kernel"; case NM_IP_CONFIG_SOURCE_SHARED: From 2bea70e75031bad941f0e9562bd2293f72b0e277 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Apr 2015 15:15:34 +0200 Subject: [PATCH 14/50] glib-compat: add nm_g_hash_table_replace() compat function The newer version of g_hash_table_replace() is useful, because it saves an additional hash table lookup. --- include/nm-glib-compat.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/nm-glib-compat.h b/include/nm-glib-compat.h index 1dc939feac..c5674ea9e7 100644 --- a/include/nm-glib-compat.h +++ b/include/nm-glib-compat.h @@ -163,4 +163,21 @@ q_n##_quark (void) \ } #endif + +static inline gboolean +nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value) +{ + /* glib 2.40 added a return value indicating whether the key already existed + * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ +#if GLIB_CHECK_VERSION(2, 40, 0) + return g_hash_table_replace (hash, key, value); +#else + gboolean contained = g_hash_table_contains (hash, key); + + g_hash_table_replace (hash, key, value); + return !contained; +#endif +} + + #endif /* __NM_GLIB_COMPAT_H__ */ From 430658b17a706d12b0538e0f62fa8e1d593738e3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 3 May 2015 15:08:44 +0200 Subject: [PATCH 15/50] core: add NMRefString NMRefString is a simple, refcounted, immutable string. Increasing/decreasing the refcount does not affect const-ness. It can be used just like a regular 'const char *' pointer. The only difference is that you need special alloc/free functions. --- src/NetworkManagerUtils.c | 124 +++++++++++++++++++++++++++ src/NetworkManagerUtils.h | 6 ++ src/tests/test-general-with-expect.c | 52 +++++++++++ 3 files changed, 182 insertions(+) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index fdaa31cd77..7757643b29 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -2713,3 +2713,127 @@ nm_utils_setpgid (gpointer unused G_GNUC_UNUSED) pid = getpid (); setpgid (pid, pid); } + +/****************************************************************** + * NMRefString + ******************************************************************/ + +#ifdef NM_MORE_ASSERTS +#define NM_STRING_CANARY(s) (GPOINTER_TO_UINT (s) ^ ((guint) 30112031329)) +#endif + +typedef struct _NMString { +#ifdef NM_STRING_CANARY + guint _canary; +#endif + int ref_count; + char str[1]; +} _NMString; + +static inline _NMString * +_nm_ref_string_up_cast (NMRefString nmstr) +{ + _NMString *s; + + s = (_NMString *) (((char *) nmstr) - G_STRUCT_OFFSET (_NMString, str)); +#ifdef NM_STRING_CANARY + g_return_val_if_fail (s->_canary == NM_STRING_CANARY (s), NULL); +#endif + g_return_val_if_fail (s->ref_count > 0, NULL); + + nm_assert (s->str == nmstr); + + return s; +} + +NMRefString +nm_ref_string_new (const char *str) +{ + _NMString *s; + gsize len; + + if (!str) + return NULL; + + len = strlen (str) + 1; + + s = g_malloc (G_STRUCT_OFFSET (_NMString, str) + len); + s->ref_count = 1; +#ifdef NM_STRING_CANARY + s->_canary = NM_STRING_CANARY (s); +#endif + memcpy (s->str, str, len); + return s->str; +} + +NMRefString +nm_ref_string_ref (NMRefString nmstr) +{ + _NMString *s; + + if (!nmstr) + return NULL; + + s = _nm_ref_string_up_cast (nmstr); + g_return_val_if_fail (s, NULL); + + s->ref_count++; + return s->str; +} + +void +nm_ref_string_unref (NMRefString nmstr) +{ + _NMString *s; + + if (!nmstr) + return; + + s = _nm_ref_string_up_cast (nmstr); + g_return_if_fail (s); + + if (--s->ref_count <= 0) { +#ifdef NM_STRING_CANARY + s->_canary = 0; +#endif + g_free (s); + } +} + +NMRefString +nm_ref_string_replace (NMRefString nmstr, const char *str) +{ + _NMString *s, *s2; + gsize len; + + if (!nmstr) + return nm_ref_string_new (str); + if (!str) { + nm_ref_string_unref (nmstr); + return NULL; + } + + s = _nm_ref_string_up_cast (nmstr); + g_return_val_if_fail (s, NULL); + + if (strcmp (s->str, str) == 0) + return nmstr; + + if (s->ref_count == 1) { + len = strlen (str) + 1; + + s2 = g_realloc (s, G_STRUCT_OFFSET (_NMString, str) + len); + +#ifdef NM_STRING_CANARY + s2->_canary = NM_STRING_CANARY (s2); +#endif + memcpy (s2->str, str, len); + return s2->str; + } else { + s->ref_count--; + return nm_ref_string_new (str); + } +} + +/******************************************************************/ + diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 7624715264..c0ce323781 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -201,4 +201,10 @@ gboolean nm_utils_get_testing_initialized (void); NMUtilsTestFlags nm_utils_get_testing (void); void _nm_utils_set_testing (NMUtilsTestFlags flags); +typedef const char *NMRefString; +NMRefString nm_ref_string_new (const char *str); +NMRefString nm_ref_string_ref (NMRefString nmstr); +void nm_ref_string_unref (NMRefString nmstr); +NMRefString nm_ref_string_replace (NMRefString nmstr, const char *str); + #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 9491801d42..300ed2145d 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -34,6 +34,57 @@ /*******************************************/ +static void +test_nm_ref_string (void) +{ + const char *nm_str, *s1, *s2; + + nm_str = nm_ref_string_new ("hallo"); + + g_assert_cmpstr (nm_str, ==, "hallo"); + + nm_ref_string_ref (nm_str); + g_assert_cmpstr (nm_str, ==, "hallo"); + + nm_ref_string_unref (nm_str); + g_assert_cmpstr (nm_str, ==, "hallo"); + + nm_str = nm_ref_string_replace (nm_str, "hallo"); + g_assert_cmpstr (nm_str, ==, "hallo"); + + nm_str = nm_ref_string_replace (nm_str, "hallo2"); + g_assert_cmpstr (nm_str, ==, "hallo2"); + + nm_ref_string_unref (nm_str); + + /* replace() reallocs old memory if ref-count is 1. */ + s1 = nm_ref_string_new ("abcdef"); + g_assert_cmpstr (s1, ==, "abcdef"); + s2 = nm_ref_string_replace (s1, "ABC"); + g_assert_cmpstr (s2, ==, "ABC"); + nm_ref_string_unref (s2); + + /* replace() reallocs old memory if ref-count is 1. */ + s1 = nm_ref_string_new ("ABC"); + g_assert_cmpstr (s1, ==, "ABC"); + s2 = nm_ref_string_replace (s1, "abcdef"); + g_assert_cmpstr (s2, ==, "abcdef"); + nm_ref_string_unref (s2); + + /* replace allocates new memory if ref-count larger 1. */ + s1 = nm_ref_string_new ("ABC"); + g_assert_cmpstr (s1, ==, "ABC"); + nm_ref_string_ref (s1); + s2 = nm_ref_string_replace (s1, "abcdef"); + g_assert_cmpstr (s2, ==, "abcdef"); + g_assert_cmpstr (s1, ==, "ABC"); + g_assert (s1 != s2); + nm_ref_string_unref (s2); + nm_ref_string_unref (s1); +} + +/*******************************************/ + static void test_nm_utils_monotonic_timestamp_as_boottime (void) { @@ -501,6 +552,7 @@ main (int argc, char **argv) { nmtst_init_assert_logging (&argc, &argv, "DEBUG", "DEFAULT"); + g_test_add_func ("/general/nm_ref_string", test_nm_ref_string); g_test_add_func ("/general/nm_utils_monotonic_timestamp_as_boottime", test_nm_utils_monotonic_timestamp_as_boottime); g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); From f99723eda5512bdf7f45cb583c21fc1e9ebf3e79 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 9 Apr 2015 20:29:48 +0200 Subject: [PATCH 16/50] core: add NMMultiIndex class A class to do efficient lookup for multiple values based on a key. The values are opaque pointers (void*). These values can be associated with keys. The keys are an opaque type NMMultiIndexId with arbitrary hash/equal functions. Think of the keys being a set of buckets. A value can be associated with multiple keys, just like with a regular GHashTable (i.e. it can be in multiple buckets). But one key can also be associated with multiple values (i.e. one bucket can contain multiple values). Hence the name "multi". One bucket can only either contain a value or not. It cannot contain the same value multiple times. This is implemented as a hash of hashes with the outer keys being NMMultiIndexId. The inner hashes are the "buckets". This class will be used as an efficient lookup index to find all values that belong to a certain key (bucket). Later we will ask for example "Which IP4-Addresses are associated with a certain ifindex" and efficiently retrieve the cached result list. --- src/Makefile.am | 4 + src/nm-multi-index.c | 441 +++++++++++++++++++++++++++ src/nm-multi-index.h | 109 +++++++ src/tests/test-general-with-expect.c | 358 ++++++++++++++++++++++ 4 files changed, 912 insertions(+) create mode 100644 src/nm-multi-index.c create mode 100644 src/nm-multi-index.h diff --git a/src/Makefile.am b/src/Makefile.am index 4abd92a53b..388c6b3b66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -331,6 +331,8 @@ nm_sources = \ nm-auth-utils.h \ nm-manager.c \ nm-manager.h \ + nm-multi-index.c \ + nm-multi-index.h \ nm-policy.c \ nm-policy.h \ nm-properties-changed-signal.c \ @@ -498,6 +500,8 @@ libnm_iface_helper_la_SOURCES = \ nm-enum-types.h \ nm-logging.c \ nm-logging.h \ + nm-multi-index.c \ + nm-multi-index.h \ NetworkManagerUtils.c \ NetworkManagerUtils.h diff --git a/src/nm-multi-index.c b/src/nm-multi-index.c new file mode 100644 index 0000000000..34695add7e --- /dev/null +++ b/src/nm-multi-index.c @@ -0,0 +1,441 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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 of the License, 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) 2015 Red Hat, Inc. + */ + +#include "config.h" + +#include "nm-multi-index.h" + +#include + +#include "nm-glib-compat.h" +#include "nm-macros-internal.h" + + +struct NMMultiIndex { + NMMultiIndexFuncEqual equal_fcn; + NMMultiIndexFuncClone clone_fcn; + GHashTable *hash; +}; + +typedef struct { + GHashTable *index; + gpointer *values; +} ValuesData; + +/******************************************************************************************/ + +static ValuesData * +_values_data_create () +{ + ValuesData *values_data; + + values_data = g_slice_new (ValuesData); + values_data->index = g_hash_table_new (NULL, NULL); + values_data->values = NULL; + return values_data; +} + +static void +_values_data_destroy (ValuesData *values_data) +{ + if (values_data) { + g_free (values_data->values); + g_hash_table_unref (values_data->index); + g_slice_free (ValuesData, values_data); + } +} + +static void +_values_data_populate_array (ValuesData *values_data) +{ + guint i, len; + gpointer *values; + GHashTableIter iter; + + nm_assert (values_data); + nm_assert (values_data->index && g_hash_table_size (values_data->index) > 0); + + if (values_data->values) + return; + + len = g_hash_table_size (values_data->index); + values = g_new (gpointer, len + 1); + + g_hash_table_iter_init (&iter, values_data->index); + for (i = 0; g_hash_table_iter_next (&iter, &values[i], NULL); i++) + nm_assert (i < len); + nm_assert (i == len); + values[i] = NULL; + + values_data->values = values; +} + +/******************************************************************************************/ + +/** + * nm_multi_index_lookup(): + * @index: + * @id: + * @out_len: (allow-none): output the number of values + * that are returned. + * + * Returns: (transfer-none): %NULL if there are no values + * or a %NULL terminated array of pointers. + */ +void *const* +nm_multi_index_lookup (const NMMultiIndex *index, + const NMMultiIndexId *id, + guint *out_len) +{ + ValuesData *values_data; + + g_return_val_if_fail (index, NULL); + g_return_val_if_fail (id, NULL); + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) { + if (out_len) + *out_len = 0; + return NULL; + } + _values_data_populate_array (values_data); + if (out_len) + *out_len = g_hash_table_size (values_data->index); + return values_data->values; +} + +gboolean +nm_multi_index_contains (const NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + ValuesData *values_data; + + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (id, FALSE); + g_return_val_if_fail (value, FALSE); + + values_data = g_hash_table_lookup (index->hash, id); + return values_data + && g_hash_table_contains (values_data->index, value); +} + +const NMMultiIndexId * +nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, + gconstpointer value) +{ + GHashTableIter iter; + const NMMultiIndexId *id; + ValuesData *values_data; + + g_return_val_if_fail (index, NULL); + g_return_val_if_fail (value, NULL); + + /* reverse-lookup needs to iterate over all hash tables. It should + * still be fairly quick, if the number of hash tables is small. + * There is no O(1) reverse lookup implemented, because this access + * pattern is not what NMMultiIndex is here for. + * You are supposed to use NMMultiIndex by always knowing which @id + * a @value has. + */ + + g_hash_table_iter_init (&iter, index->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { + if (g_hash_table_contains (values_data->index, value)) + return id; + } + return NULL; +} + +void +nm_multi_index_foreach (const NMMultiIndex *index, + gconstpointer value, + NMMultiIndexFuncForeach foreach_func, + gpointer user_data) +{ + GHashTableIter iter; + const NMMultiIndexId *id; + ValuesData *values_data; + + g_return_if_fail (index); + g_return_if_fail (foreach_func); + + g_hash_table_iter_init (&iter, index->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { + if ( value + && !g_hash_table_contains (values_data->index, value)) + continue; + + _values_data_populate_array (values_data); + if (!foreach_func (id, values_data->values, g_hash_table_size (values_data->index), user_data)) + return; + } +} + +void +nm_multi_index_iter_init (NMMultiIndexIter *iter, + const NMMultiIndex *index, + gconstpointer value) +{ + g_return_if_fail (index); + g_return_if_fail (iter); + + g_hash_table_iter_init (&iter->_iter, index->hash); + iter->_index = index; + iter->_value = value; +} + +gboolean +nm_multi_index_iter_next (NMMultiIndexIter *iter, + const NMMultiIndexId **out_id, + void *const**out_values, + guint *out_len) +{ + const NMMultiIndexId *id; + ValuesData *values_data; + + g_return_val_if_fail (iter, FALSE); + + while (g_hash_table_iter_next (&iter->_iter, (gpointer *) &id, (gpointer *) &values_data)) { + if ( !iter->_value + || g_hash_table_contains (values_data->index, iter->_value)) { + _values_data_populate_array (values_data); + if (out_id) + *out_id = id; + if (out_values) + *out_values = values_data->values; + if (out_len) + *out_len = g_hash_table_size (values_data->index); + return TRUE; + } + } + return FALSE; +} + +/******************************************************************************************/ + +void +nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, + const NMMultiIndex *index, + const NMMultiIndexId *id) +{ + ValuesData *values_data; + + g_return_if_fail (index); + g_return_if_fail (iter); + g_return_if_fail (id); + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) + iter->_state = 1; + else { + iter->_state = 0; + g_hash_table_iter_init (&iter->_iter, values_data->index); + } +} + +gboolean +nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, + void **out_value) +{ + g_return_val_if_fail (iter, FALSE); + g_return_val_if_fail (iter->_state <= 1, FALSE); + + if (iter->_state == 0) + return g_hash_table_iter_next (&iter->_iter, out_value, NULL); + else { + iter->_state = 2; + return FALSE; + } +} + +/******************************************************************************************/ + +static gboolean +_do_add (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + ValuesData *values_data; + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) { + NMMultiIndexId *id_new; + + /* Contrary to GHashTable, we don't take ownership of the @id that was + * provided to nm_multi_index_add(). Instead we clone it via @clone_fcn + * when needed. + * + * The reason is, that we expect in most cases that there exists + * already a @id so that we don't need ownership of it (or clone it). + * By doing this, the caller can pass a stack allocated @id or + * reuse the @id for other insertions. + */ + id_new = index->clone_fcn (id); + if (!id_new) + g_return_val_if_reached (FALSE); + + values_data = _values_data_create (); + g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value); + + g_hash_table_insert (index->hash, id_new, values_data); + } else { + if (!nm_g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value)) + return FALSE; + g_clear_pointer (&values_data->values, g_free); + } + return TRUE; +} + +static gboolean +_do_remove (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + ValuesData *values_data; + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) + return FALSE; + + if (!g_hash_table_remove (values_data->index, value)) + return FALSE; + + if (g_hash_table_size (values_data->index) == 0) + g_hash_table_remove (index->hash, id); + else + g_clear_pointer (&values_data->values, g_free); + return TRUE; +} + +gboolean +nm_multi_index_add (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (id, FALSE); + g_return_val_if_fail (value, FALSE); + + return _do_add (index, id, value); +} + +gboolean +nm_multi_index_remove (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (value, FALSE); + + if (!id) + g_return_val_if_reached (FALSE); + return _do_remove (index, id, value); +} + +/** + * nm_multi_index_move: + * @index: + * @id_old: (allow-none): remove @value at @id_old + * @id_new: (allow-none): add @value under @id_new + * @value: the value to add + * + * Similar to a remove(), followed by an add(). The difference + * is, that we allow %NULL for both @id_old and @id_new. + * And the return value indicates whether @value was successfully + * removed *and* added. + * + * Returns: %TRUE, if the value was removed from @id_old and added + * as %id_new. %FALSE could mean, that @value was not added to @id_old + * before, or that that @value was already part of @id_new. */ +gboolean +nm_multi_index_move (NMMultiIndex *index, + const NMMultiIndexId *id_old, + const NMMultiIndexId *id_new, + gconstpointer value) +{ + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (value, FALSE); + + if (!id_old && !id_new) { + /* nothing to do, @value was and is not in @index. */ + return TRUE; + } if (!id_old) { + /* add @value to @index with @id_new */ + return _do_add (index, id_new, value); + } else if (!id_new) { + /* remove @value from @index with @id_old */ + return _do_remove (index, id_old, value); + } else if (index->equal_fcn (id_old, id_new)) { + if (_do_add (index, id_new, value)) { + /* we would expect, that @value is already in @index, + * Return %FALSE, if it wasn't. */ + return FALSE; + } + return TRUE; + } else { + gboolean did_remove; + + did_remove = _do_remove (index, id_old, value); + return _do_add (index, id_new, value) && did_remove; + } +} + +/******************************************************************************************/ + +guint +nm_multi_index_get_num_groups (const NMMultiIndex *index) +{ + g_return_val_if_fail (index, 0); + return g_hash_table_size (index->hash); +} + +NMMultiIndex * +nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, + NMMultiIndexFuncEqual equal_fcn, + NMMultiIndexFuncClone clone_fcn, + NMMultiIndexFuncDestroy destroy_fcn) +{ + NMMultiIndex *index; + + g_return_val_if_fail (hash_fcn, NULL); + g_return_val_if_fail (equal_fcn, NULL); + g_return_val_if_fail (clone_fcn, NULL); + g_return_val_if_fail (destroy_fcn, NULL); + + index = g_new (NMMultiIndex, 1); + index->equal_fcn = equal_fcn; + index->clone_fcn = clone_fcn; + + index->hash = g_hash_table_new_full ((GHashFunc) hash_fcn, + (GEqualFunc) equal_fcn, + (GDestroyNotify) destroy_fcn, + (GDestroyNotify) _values_data_destroy); + return index; +} + +void +nm_multi_index_free (NMMultiIndex *index) +{ + g_return_if_fail (index); + g_hash_table_unref (index->hash); + g_free (index); +} + diff --git a/src/nm-multi-index.h b/src/nm-multi-index.h new file mode 100644 index 0000000000..e41ef54e7c --- /dev/null +++ b/src/nm-multi-index.h @@ -0,0 +1,109 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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 of the License, 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) 2015 Red Hat, Inc. + */ + +#ifndef __NM_MULTI_INDEX__ +#define __NM_MULTI_INDEX__ + +#include + +G_BEGIN_DECLS + + +typedef struct { + char _dummy; +} NMMultiIndexId; + +typedef struct NMMultiIndex NMMultiIndex; + +typedef struct { + GHashTableIter _iter; + const NMMultiIndex *_index; + gconstpointer _value; +} NMMultiIndexIter; + +typedef struct { + GHashTableIter _iter; + guint _state; +} NMMultiIndexIdIter; + +typedef gboolean (*NMMultiIndexFuncEqual) (const NMMultiIndexId *id_a, const NMMultiIndexId *id_b); +typedef guint (*NMMultiIndexFuncHash) (const NMMultiIndexId *id); +typedef NMMultiIndexId *(*NMMultiIndexFuncClone) (const NMMultiIndexId *id); +typedef void (*NMMultiIndexFuncDestroy) (NMMultiIndexId *id); + +typedef gboolean (*NMMultiIndexFuncForeach) (const NMMultiIndexId *id, void *const* values, guint len, gpointer user_data); + + +NMMultiIndex *nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, + NMMultiIndexFuncEqual equal_fcn, + NMMultiIndexFuncClone clone_fcn, + NMMultiIndexFuncDestroy destroy_fcn); + +void nm_multi_index_free (NMMultiIndex *index); + +gboolean nm_multi_index_add (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value); + +gboolean nm_multi_index_remove (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value); + +gboolean nm_multi_index_move (NMMultiIndex *index, + const NMMultiIndexId *id_old, + const NMMultiIndexId *id_new, + gconstpointer value); + +guint nm_multi_index_get_num_groups (const NMMultiIndex *index); + +void *const*nm_multi_index_lookup (const NMMultiIndex *index, + const NMMultiIndexId *id, + guint *out_len); + +gboolean nm_multi_index_contains (const NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value); + +const NMMultiIndexId *nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, + gconstpointer value); + +void nm_multi_index_foreach (const NMMultiIndex *index, + gconstpointer value, + NMMultiIndexFuncForeach foreach_func, + gpointer user_data); + +void nm_multi_index_iter_init (NMMultiIndexIter *iter, + const NMMultiIndex *index, + gconstpointer value); +gboolean nm_multi_index_iter_next (NMMultiIndexIter *iter, + const NMMultiIndexId **out_id, + void *const**out_values, + guint *out_len); + +void nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, + const NMMultiIndex *index, + const NMMultiIndexId *id); +gboolean nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, + void **out_value); + +G_END_DECLS + +#endif /* __NM_MULTI_INDEX__ */ + diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 300ed2145d..e7a1453942 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -29,6 +29,7 @@ #include "NetworkManagerUtils.h" #include "nm-logging.h" +#include "nm-multi-index.h" #include "nm-test-utils.h" @@ -545,6 +546,362 @@ test_nm_ethernet_address_is_valid (void) /*******************************************/ +typedef struct { + union { + NMMultiIndexId id_base; + guint bucket; + }; +} NMMultiIndexIdTest; + +typedef struct { + guint64 buckets; + gpointer ptr_value; +} NMMultiIndexTestValue; + +static gboolean +_mi_value_bucket_has (const NMMultiIndexTestValue *value, guint bucket) +{ + g_assert (value); + g_assert (bucket < 64); + + return (value->buckets & (((guint64) 0x01) << bucket)) != 0; +} + +static gboolean +_mi_value_bucket_set (NMMultiIndexTestValue *value, guint bucket) +{ + g_assert (value); + g_assert (bucket < 64); + + if (_mi_value_bucket_has (value, bucket)) + return FALSE; + + value->buckets |= (((guint64) 0x01) << bucket); + return TRUE; +} + +static gboolean +_mi_value_bucket_unset (NMMultiIndexTestValue *value, guint bucket) +{ + g_assert (value); + g_assert (bucket < 64); + + if (!_mi_value_bucket_has (value, bucket)) + return FALSE; + + value->buckets &= ~(((guint64) 0x01) << bucket); + return TRUE; +} + +static guint +_mi_idx_hash (const NMMultiIndexIdTest *id) +{ + g_assert (id && id->bucket < 64); + return id->bucket; +} + +static gboolean +_mi_idx_equal (const NMMultiIndexIdTest *a, const NMMultiIndexIdTest *b) +{ + g_assert (a && a->bucket < 64); + g_assert (b && b->bucket < 64); + + return a->bucket == b->bucket; +} + +static NMMultiIndexIdTest * +_mi_idx_clone (const NMMultiIndexIdTest *id) +{ + NMMultiIndexIdTest *n; + + g_assert (id && id->bucket < 64); + + n = g_new0 (NMMultiIndexIdTest, 1); + n->bucket = id->bucket; + return n; +} + +static void +_mi_idx_destroy (NMMultiIndexIdTest *id) +{ + g_assert (id && id->bucket < 64); + g_free (id); +} + +static NMMultiIndexTestValue * +_mi_create_array (guint num_values) +{ + NMMultiIndexTestValue *array = g_new0 (NMMultiIndexTestValue, num_values); + guint i; + + g_assert (num_values > 0); + + for (i = 0; i < num_values; i++) { + array[i].buckets = 0; + array[i].ptr_value = GUINT_TO_POINTER (i + 1); + } + return array; +} + +typedef struct { + guint num_values; + guint num_buckets; + NMMultiIndexTestValue *array; + int test_idx; +} NMMultiIndexAssertData; + +static gboolean +_mi_assert_index_equals_array_cb (const NMMultiIndexIdTest *id, void *const* values, guint len, NMMultiIndexAssertData *data) +{ + guint i; + gboolean has_test_idx = FALSE; + + g_assert (id && id->bucket < 64); + g_assert (data); + g_assert (values); + g_assert (len > 0); + g_assert (values[len] == NULL); + g_assert (data->test_idx >= -1 || data->test_idx < data->num_buckets); + + g_assert (id->bucket < data->num_buckets); + + for (i = 0; i < data->num_values; i++) + g_assert (!_mi_value_bucket_has (&data->array[i], id->bucket)); + + for (i = 0; i < len; i++) { + guint vi = GPOINTER_TO_UINT (values[i]); + + g_assert (vi >= 1); + g_assert (vi <= data->num_values); + vi--; + if (data->test_idx == vi) + has_test_idx = TRUE; + g_assert (data->array[vi].ptr_value == values[i]); + if (!_mi_value_bucket_set (&data->array[vi], id->bucket)) + g_assert_not_reached (); + } + g_assert ((data->test_idx == -1 && !has_test_idx) || has_test_idx); + return TRUE; +} + +static void +_mi_assert_index_equals_array (guint num_values, guint num_buckets, int test_idx, const NMMultiIndexTestValue *array, const NMMultiIndex *index) +{ + NMMultiIndexAssertData data = { + .num_values = num_values, + .num_buckets = num_buckets, + .test_idx = test_idx, + }; + NMMultiIndexIter iter; + const NMMultiIndexIdTest *id; + void *const* values; + guint len; + NMMultiIndexTestValue *v; + + data.array = _mi_create_array (num_values); + v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; + nm_multi_index_foreach (index, v, (NMMultiIndexFuncForeach) _mi_assert_index_equals_array_cb, &data); + if (test_idx >= 0) + g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); + else + g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); + g_free (data.array); + + + data.array = _mi_create_array (num_values); + v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; + nm_multi_index_iter_init (&iter, index, v); + while (nm_multi_index_iter_next (&iter, (gpointer) &id, &values, &len)) + _mi_assert_index_equals_array_cb (id, values, len, &data); + if (test_idx >= 0) + g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); + else + g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); + g_free (data.array); +} + +typedef enum { + MI_OP_ADD, + MI_OP_REMOVE, + MI_OP_MOVE, +} NMMultiIndexOperation; + +static void +_mi_rebucket (GRand *rand, guint num_values, guint num_buckets, NMMultiIndexOperation op, guint bucket, guint bucket_old, guint array_idx, NMMultiIndexTestValue *array, NMMultiIndex *index) +{ + NMMultiIndexTestValue *v; + NMMultiIndexIdTest id, id_old; + const NMMultiIndexIdTest *id_reverse; + guint64 buckets_old; + guint i; + gboolean had_bucket, had_bucket_old; + + g_assert (array_idx < num_values); + g_assert (bucket < (int) num_buckets); + + v = &array[array_idx]; + + buckets_old = v->buckets; + if (op == MI_OP_MOVE) + had_bucket_old = _mi_value_bucket_has (v, bucket_old); + else + had_bucket_old = FALSE; + had_bucket = _mi_value_bucket_has (v, bucket); + + switch (op) { + + case MI_OP_ADD: + _mi_value_bucket_set (v, bucket); + id.bucket = bucket; + if (nm_multi_index_add (index, &id.id_base, v->ptr_value)) + g_assert (!had_bucket); + else + g_assert (had_bucket); + break; + + case MI_OP_REMOVE: + _mi_value_bucket_unset (v, bucket); + id.bucket = bucket; + if (nm_multi_index_remove (index, &id.id_base, v->ptr_value)) + g_assert (had_bucket); + else + g_assert (!had_bucket); + break; + + case MI_OP_MOVE: + + _mi_value_bucket_unset (v, bucket_old); + _mi_value_bucket_set (v, bucket); + + id.bucket = bucket; + id_old.bucket = bucket_old; + + if (nm_multi_index_move (index, &id_old.id_base, &id.id_base, v->ptr_value)) { + if (bucket == bucket_old) + g_assert (had_bucket_old && had_bucket); + else + g_assert (had_bucket_old && !had_bucket); + } else { + if (bucket == bucket_old) + g_assert (!had_bucket_old && !had_bucket); + else + g_assert (!had_bucket_old || had_bucket); + } + break; + + default: + g_assert_not_reached (); + } + +#if 0 + g_print (">>> rebucket: idx=%3u, op=%3s, bucket=%3i%c -> %3i%c, buckets=%08llx -> %08llx %s\n", array_idx, + op == MI_OP_ADD ? "ADD" : (op == MI_OP_REMOVE ? "REM" : "MOV"), + bucket_old, had_bucket_old ? '*' : ' ', + bucket, had_bucket ? '*' : ' ', + (long long unsigned) buckets_old, (long long unsigned) v->buckets, + buckets_old != v->buckets ? "(changed)" : "(unchanged)"); +#endif + + id_reverse = (const NMMultiIndexIdTest *) nm_multi_index_lookup_first_by_value (index, v->ptr_value); + if (id_reverse) + g_assert (_mi_value_bucket_has (v, id_reverse->bucket)); + else + g_assert (v->buckets == 0); + + for (i = 0; i < 64; i++) { + id.bucket = i; + if (nm_multi_index_contains (index, &id.id_base, v->ptr_value)) + g_assert (_mi_value_bucket_has (v, i)); + else + g_assert (!_mi_value_bucket_has (v, i)); + } + + _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); + _mi_assert_index_equals_array (num_values, num_buckets, array_idx, array, index); + _mi_assert_index_equals_array (num_values, num_buckets, g_rand_int_range (rand, 0, num_values), array, index); +} + +static void +_mi_test_run (guint num_values, guint num_buckets) +{ + NMMultiIndex *index = nm_multi_index_new ((NMMultiIndexFuncHash) _mi_idx_hash, + (NMMultiIndexFuncEqual) _mi_idx_equal, + (NMMultiIndexFuncClone) _mi_idx_clone, + (NMMultiIndexFuncDestroy) _mi_idx_destroy); + gs_free NMMultiIndexTestValue *array = _mi_create_array (num_values); + GRand *rand = nmtst_get_rand (); + guint i, i_rd, i_idx, i_bucket; + guint num_buckets_all = num_values * num_buckets; + + g_assert (array[0].ptr_value == GUINT_TO_POINTER (1)); + + _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); + + _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); + _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 0, 0, 0, array, index); + + if (num_buckets >= 3) { + _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); + _mi_rebucket (rand, num_values, num_buckets, MI_OP_MOVE, 2, 0, 0, array, index); + _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 2, 0, 0, array, index); + } + + g_assert (nm_multi_index_get_num_groups (index) == 0); + + /* randomly change the bucket of entries. */ + for (i = 0; i < 5 * num_values; i++) { + guint array_idx = g_rand_int_range (rand, 0, num_values); + guint bucket = g_rand_int_range (rand, 0, num_buckets); + NMMultiIndexOperation op = g_rand_int_range (rand, 0, MI_OP_MOVE + 1); + guint bucket_old = 0; + + if (op == MI_OP_MOVE) { + if ((g_rand_int (rand) % 2) && array[array_idx].buckets != 0) { + guint64 b; + + /* choose the highest (existing) bucket. */ + bucket_old = 0; + for (b = array[array_idx].buckets; b; b >>= 1) + bucket_old++; + } else { + /* choose a random bucket (even if the item is currently not in that bucket). */ + bucket_old = g_rand_int_range (rand, 0, num_buckets); + } + } + + _mi_rebucket (rand, num_values, num_buckets, op, bucket, bucket_old, array_idx, array, index); + } + + /* remove all elements from all buckets */ + i_rd = g_rand_int (rand); + for (i = 0; i < num_buckets_all; i++) { + i_rd = (i_rd + 101) % num_buckets_all; + i_idx = i_rd / num_buckets; + i_bucket = i_rd % num_buckets; + + if (_mi_value_bucket_has (&array[i_idx], i_bucket)) + _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, i_bucket, 0, i_idx, array, index); + } + + g_assert (nm_multi_index_get_num_groups (index) == 0); + nm_multi_index_free (index); +} + +static void +test_nm_multi_index (void) +{ + guint i, j; + + for (i = 1; i < 7; i++) { + for (j = 1; j < 6; j++) + _mi_test_run (i, j); + } + _mi_test_run (50, 3); + _mi_test_run (50, 18); +} + +/*******************************************/ + NMTST_DEFINE (); int @@ -557,6 +914,7 @@ main (int argc, char **argv) g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); g_test_add_func ("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid); + g_test_add_func ("/general/nm_multi_index", test_nm_multi_index); return g_test_run (); } From 359fe9f8c1a0d2d04a5adce55ab39a67de2c0a8b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 14 Apr 2015 22:34:01 +0200 Subject: [PATCH 17/50] platform: add nmp-object.h file --- src/Makefile.am | 4 +++ src/platform/nmp-object.c | 23 +++++++++++++++ src/platform/nmp-object.h | 29 +++++++++++++++++++ src/platform/tests/.gitignore | 2 ++ src/platform/tests/Makefile.am | 7 +++++ src/platform/tests/test-nmp-object.c | 43 ++++++++++++++++++++++++++++ 6 files changed, 108 insertions(+) create mode 100644 src/platform/nmp-object.c create mode 100644 src/platform/nmp-object.h create mode 100644 src/platform/tests/test-nmp-object.c diff --git a/src/Makefile.am b/src/Makefile.am index 388c6b3b66..3aa8b5f4f9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -227,6 +227,8 @@ nm_sources = \ platform/nm-platform.h \ platform/nm-platform-utils.c \ platform/nm-platform-utils.h \ + platform/nmp-object.c \ + platform/nmp-object.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h \ @@ -475,6 +477,8 @@ libnm_iface_helper_la_SOURCES = \ platform/nm-platform.h \ platform/nm-platform-utils.c \ platform/nm-platform-utils.h \ + platform/nmp-object.c \ + platform/nmp-object.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h \ diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c new file mode 100644 index 0000000000..8c90deaec0 --- /dev/null +++ b/src/platform/nmp-object.c @@ -0,0 +1,23 @@ +/* -*- 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) 2015 Red Hat, Inc. + */ + +#include "nmp-object.h" + + diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h new file mode 100644 index 0000000000..e1bb9ed7a8 --- /dev/null +++ b/src/platform/nmp-object.h @@ -0,0 +1,29 @@ +/* -*- 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) 2015 Red Hat, Inc. + */ + +#ifndef __NMP_OBJECT_H__ +#define __NMP_OBJECT_H__ + +#include "config.h" + +#include "nm-platform.h" + + +#endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/.gitignore b/src/platform/tests/.gitignore index cdd8a51f83..536f012fbd 100644 --- a/src/platform/tests/.gitignore +++ b/src/platform/tests/.gitignore @@ -8,5 +8,7 @@ /test-general /test-link-fake /test-link-linux +/test-nmp-object /test-route-fake /test-route-linux + diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 76b40f7ca3..54844ec14a 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -38,6 +38,7 @@ noinst_PROGRAMS = \ test-address-fake \ test-address-linux \ test-general \ + test-nmp-object \ test-route-fake \ test-route-linux \ test-cleanup-fake \ @@ -110,6 +111,11 @@ test_cleanup_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_cleanup_linux_LDADD = $(PLATFORM_LDADD) +test_nmp_object_SOURCES = \ + test-nmp-object.c +test_nmp_object_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + test_general_SOURCES = \ test-general.c test_general_LDADD = \ @@ -125,6 +131,7 @@ TESTS = \ test-general \ test-link-fake \ test-link-linux \ + test-nmp-object \ test-route-fake \ test-route-linux diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c new file mode 100644 index 0000000000..7c01d9850c --- /dev/null +++ b/src/platform/tests/test-nmp-object.c @@ -0,0 +1,43 @@ +/* -*- 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) 2015 Red Hat, Inc. + */ + +#include "nmp-object.h" + +#include "nm-logging.h" + +#include "nm-test-utils.h" + + +/******************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + int result; + + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + result = g_test_run (); + + return result; +} + From d1e7554a90ee9bae08ed44a287a0d59b1ad2ce2b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 14 Apr 2015 23:15:27 +0200 Subject: [PATCH 18/50] platform: move ObjectType to nmp-object.h --- src/platform/nm-linux-platform.c | 14 +------------- src/platform/nmp-object.h | 10 ++++++++++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d326108a06..264af4b42c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -60,6 +60,7 @@ #include "nm-logging.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" +#include "nmp-object.h" /* This is only included for the translation of VLAN flags */ #include "nm-setting-vlan.h" @@ -404,19 +405,6 @@ _support_kernel_extended_ifa_flags_get (void) * NMPlatform types and functions ******************************************************************/ -typedef enum { - OBJECT_TYPE_UNKNOWN, - OBJECT_TYPE_LINK, - OBJECT_TYPE_IP4_ADDRESS, - OBJECT_TYPE_IP6_ADDRESS, - OBJECT_TYPE_IP4_ROUTE, - OBJECT_TYPE_IP6_ROUTE, - __OBJECT_TYPE_LAST, - OBJECT_TYPE_MAX = __OBJECT_TYPE_LAST - 1, -} ObjectType; - -/******************************************************************/ - typedef struct { struct nl_sock *nlh; struct nl_sock *nlh_event; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index e1bb9ed7a8..fff607048e 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -25,5 +25,15 @@ #include "nm-platform.h" +typedef enum { /*< skip >*/ + OBJECT_TYPE_UNKNOWN, + OBJECT_TYPE_LINK, + OBJECT_TYPE_IP4_ADDRESS, + OBJECT_TYPE_IP6_ADDRESS, + OBJECT_TYPE_IP4_ROUTE, + OBJECT_TYPE_IP6_ROUTE, + __OBJECT_TYPE_LAST, + OBJECT_TYPE_MAX = __OBJECT_TYPE_LAST - 1, +} ObjectType; #endif /* __NMP_OBJECT_H__ */ From 53f98e7f9e58767e22fada651b12bf3f74aa76ed Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 14 Apr 2015 23:14:06 +0200 Subject: [PATCH 19/50] platform: implement NMPObject and NMPCache NMPObject is a simple "object" implemenation around NMPlatformObject. They are ref-counted and have a class-pointer. Several basic functions like equality, hash, to-string are implemented. NMPCache is can be used to store the NMPObject. Objects are indexed via their primary id, but there is also multi-lookup via NMCacheId and NMMultiIndex. Part of the implementation is inside "nm-linux-platform.c", because it depends on utility functions from there. --- src/nm-types.h | 3 + src/platform/nm-linux-platform.c | 398 +++++- src/platform/nm-platform.c | 8 +- src/platform/nm-platform.h | 6 +- src/platform/nmp-object.c | 1927 ++++++++++++++++++++++++++ src/platform/nmp-object.h | 344 +++++ src/platform/tests/test-nmp-object.c | 383 +++++ 7 files changed, 3064 insertions(+), 5 deletions(-) diff --git a/src/nm-types.h b/src/nm-types.h index ddcee4daba..2f36bee45a 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -52,6 +52,9 @@ typedef enum { /* In priority order; higher number == higher priority */ NM_IP_CONFIG_SOURCE_UNKNOWN, + /* platform internal flag used to mark routes with RTM_F_CLONED. */ + _NM_IP_CONFIG_SOURCE_RTM_F_CLONED, + /* platform internal flag used to mark routes with protocol RTPROT_KERNEL. */ _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 264af4b42c..ce49083310 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2012-2013 Red Hat, Inc. + * Copyright (C) 2012-2015 Red Hat, Inc. */ #include "config.h" @@ -436,7 +436,7 @@ nm_linux_platform_setup (void) /******************************************************************/ -static ObjectType +ObjectType _nlo_get_object_type (const struct nl_object *object) { const char *type_str; @@ -1011,6 +1011,86 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin return TRUE; } +gboolean +_nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +{ + NMPlatformLink *obj = (NMPlatformLink *) _obj; + NMPObjectLink *obj_priv = (NMPObjectLink *) _obj; + struct rtnl_link *nlo = (struct rtnl_link *) _nlo; + const char *name; + struct nl_addr *nladdr; + + nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); + + obj->ifindex = rtnl_link_get_ifindex (nlo); + + if (id_only) + return TRUE; + + name = rtnl_link_get_name (nlo); + if (name) + g_strlcpy (obj->name, name, sizeof (obj->name)); + obj->type = link_extract_type (platform, nlo); + obj->kind = g_intern_string (rtnl_link_get_type (nlo)); + obj->flags = rtnl_link_get_flags (nlo); + obj->up = NM_FLAGS_HAS (obj->flags, IFF_UP); + obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP); + obj->arp = !NM_FLAGS_HAS (obj->flags, IFF_NOARP); + obj->master = rtnl_link_get_master (nlo); + obj->parent = rtnl_link_get_link (nlo); + obj->mtu = rtnl_link_get_mtu (nlo); + obj->arptype = rtnl_link_get_arptype (nlo); + + if (obj->type == NM_LINK_TYPE_VLAN) + obj->vlan_id = rtnl_link_vlan_get_id (nlo); + + if ((nladdr = rtnl_link_get_addr (nlo))) { + unsigned int l = 0; + + l = nl_addr_get_len (nladdr); + if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->addr.data)); + memcpy (obj->addr.data, nl_addr_get_binary_addr (nladdr), l); + obj->addr.len = l; + } + } + +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + if (_support_user_ipv6ll_get ()) { + guint8 mode = 0; + + if (rtnl_link_inet6_get_addr_gen_mode (nlo, &mode) == 0) + obj->inet6_addr_gen_mode_inv = ~mode; + } +#endif + +#if HAVE_LIBNL_INET6_TOKEN + if ((rtnl_link_inet6_get_token (nlo, &nladdr)) == 0) { + if ( nl_addr_get_family (nladdr) == AF_INET6 + && nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) { + struct in6_addr *addr; + NMUtilsIPv6IfaceId *iid = &obj->inet6_token.iid; + + addr = nl_addr_get_binary_addr (nladdr); + iid->id_u8[7] = addr->s6_addr[15]; + iid->id_u8[6] = addr->s6_addr[14]; + iid->id_u8[5] = addr->s6_addr[13]; + iid->id_u8[4] = addr->s6_addr[12]; + iid->id_u8[3] = addr->s6_addr[11]; + iid->id_u8[2] = addr->s6_addr[10]; + iid->id_u8[1] = addr->s6_addr[9]; + iid->id_u8[0] = addr->s6_addr[8]; + obj->inet6_token.is_valid = TRUE; + } + nl_addr_put (nladdr); + } +#endif + + obj_priv->netlink.is_in_netlink = TRUE; + + return TRUE; +} + /* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break * the carrier detection. This hack makes nm-platform think they don't have the * IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all @@ -1194,6 +1274,66 @@ _init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr address->preferred = _get_remaining_time (address->timestamp, a_preferred); } +static guint32 +_extend_lifetime (guint32 lifetime, guint32 seconds) +{ + guint64 v; + + if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + || seconds == 0) + return lifetime; + + v = (guint64) lifetime + (guint64) seconds; + return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); +} + +/* The rtnl_addr object contains relative lifetimes @valid and @preferred + * that count in seconds, starting from the moment when the kernel constructed + * the netlink message. + * + * There is also a field rtnl_addr_last_update_time(), which is the absolute + * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address + * was modified (wrapping every 497 days). + * Immediately at the time when the address was last modified, #NOW and @last_update_time + * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. + * However, this is not true in general. As time goes by, whenever kernel sends a new address + * via netlink, the lifetimes keep counting down. + **/ +static void +_nlo_rtnl_addr_get_lifetimes (const struct rtnl_addr *rtnladdr, + guint32 *out_timestamp, + guint32 *out_lifetime, + guint32 *out_preferred) +{ + guint32 timestamp = 0; + gint32 now; + guint32 lifetime = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr); + guint32 preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr); + + if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT + || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + if (preferred > lifetime) + preferred = lifetime; + timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr, &now); + + if (now == 0) { + /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ + nm_assert (timestamp == 1); + now = nm_utils_get_monotonic_timestamp_s (); + } + if (timestamp < now) { + guint32 diff = now - timestamp; + + lifetime = _extend_lifetime (lifetime, diff); + preferred = _extend_lifetime (preferred, diff); + } else + nm_assert (timestamp == now); + } + *out_timestamp = timestamp; + *out_lifetime = lifetime; + *out_preferred = preferred; +} + static gboolean init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) { @@ -1229,6 +1369,44 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) return TRUE; } +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +{ + NMPlatformIP4Address *obj = (NMPlatformIP4Address *) _obj; + struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo; + struct nl_addr *nladdr = rtnl_addr_get_local (nlo); + struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo); + const char *label; + + if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) + g_return_val_if_reached (FALSE); + + obj->ifindex = rtnl_addr_get_ifindex (nlo); + obj->plen = rtnl_addr_get_prefixlen (nlo); + memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address)); + + if (id_only) + return TRUE; + + obj->source = NM_IP_CONFIG_SOURCE_KERNEL; + _nlo_rtnl_addr_get_lifetimes (nlo, + &obj->timestamp, + &obj->lifetime, + &obj->preferred); + if (nlpeer) { + if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) + g_warn_if_reached (); + else + memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); + } + label = rtnl_addr_get_label (nlo); + /* Check for ':'; we're only interested in labels used as interface aliases */ + if (label && strchr (label, ':')) + g_strlcpy (obj->label, label, sizeof (obj->label)); + + return TRUE; +} + static gboolean init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) { @@ -1258,6 +1436,41 @@ init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) return TRUE; } +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +{ + NMPlatformIP6Address *obj = (NMPlatformIP6Address *) _obj; + struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo; + struct nl_addr *nladdr = rtnl_addr_get_local (nlo); + struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo); + + if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) + g_return_val_if_reached (FALSE); + + obj->ifindex = rtnl_addr_get_ifindex (nlo); + obj->plen = rtnl_addr_get_prefixlen (nlo); + memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address)); + + if (id_only) + return TRUE; + + obj->source = NM_IP_CONFIG_SOURCE_KERNEL; + _nlo_rtnl_addr_get_lifetimes (nlo, + &obj->timestamp, + &obj->lifetime, + &obj->preferred); + obj->flags = rtnl_addr_get_flags (nlo); + + if (nlpeer) { + if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) + g_warn_if_reached (); + else + memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); + } + + return TRUE; +} + static guint source_to_rtprot (NMIPConfigSource source) { @@ -1350,6 +1563,61 @@ init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute) return TRUE; } +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +{ + NMPlatformIP4Route *obj = (NMPlatformIP4Route *) _obj; + struct rtnl_route *nlo = (struct rtnl_route *) _nlo; + struct nl_addr *dst, *gw; + struct rtnl_nexthop *nexthop; + + if (rtnl_route_get_type (nlo) != RTN_UNICAST || + rtnl_route_get_table (nlo) != RT_TABLE_MAIN || + rtnl_route_get_tos (nlo) != 0 || + rtnl_route_get_nnexthops (nlo) != 1) + return FALSE; + + nexthop = rtnl_route_nexthop_n (nlo, 0); + if (!nexthop) + g_return_val_if_reached (FALSE); + + dst = rtnl_route_get_dst (nlo); + if (!dst) + g_return_val_if_reached (FALSE); + + if (nl_addr_get_len (dst)) { + if (nl_addr_get_len (dst) != sizeof (obj->network)) + g_return_val_if_reached (FALSE); + memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); + } + obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); + obj->plen = nl_addr_get_prefixlen (dst); + obj->metric = rtnl_route_get_priority (nlo); + obj->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (nlo)); + + gw = rtnl_route_nh_get_gateway (nexthop); + if (gw) { + if (nl_addr_get_len (gw) != sizeof (obj->gateway)) + g_warn_if_reached (); + else + memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + } + rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); + if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) { + /* we must not straight way reject cloned routes, because we might have cached + * a non-cloned route. If we now receive an update of the route with the route + * being cloned, we must still return the object, so that we can remove the old + * one from the cache. + * + * This happens, because this route is not nmp_object_is_alive(). + * */ + obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; + } else + obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo), TRUE); + + return TRUE; +} + static gboolean init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) { @@ -1390,6 +1658,56 @@ init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) return TRUE; } +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +{ + NMPlatformIP6Route *obj = (NMPlatformIP6Route *) _obj; + struct rtnl_route *nlo = (struct rtnl_route *) _nlo; + struct nl_addr *dst, *gw; + struct rtnl_nexthop *nexthop; + + if (rtnl_route_get_type (nlo) != RTN_UNICAST || + rtnl_route_get_table (nlo) != RT_TABLE_MAIN || + rtnl_route_get_tos (nlo) != 0 || + rtnl_route_get_nnexthops (nlo) != 1) + return FALSE; + + nexthop = rtnl_route_nexthop_n (nlo, 0); + if (!nexthop) + g_return_val_if_reached (FALSE); + + dst = rtnl_route_get_dst (nlo); + if (!dst) + g_return_val_if_reached (FALSE); + + if (nl_addr_get_len (dst)) { + if (nl_addr_get_len (dst) != sizeof (obj->network)) + g_return_val_if_reached (FALSE); + memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); + } + obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); + obj->plen = nl_addr_get_prefixlen (dst); + obj->metric = rtnl_route_get_priority (nlo); + + if (id_only) + return TRUE; + + gw = rtnl_route_nh_get_gateway (nexthop); + if (gw) { + if (nl_addr_get_len (gw) != sizeof (obj->gateway)) + g_warn_if_reached (); + else + memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + } + rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); + if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) + obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; + else + obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo), TRUE); + + return TRUE; +} + static char to_string_buffer[255]; #define SET_AND_RETURN_STRING_BUFFER(...) \ @@ -2296,6 +2614,16 @@ build_rtnl_link (int ifindex, const char *name, NMLinkType type) return (struct nl_object *) rtnllink; } +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformLink *obj = (const NMPlatformLink *) _obj; + + return build_rtnl_link (obj->ifindex, + obj->name[0] ? obj->name : NULL, + obj->type); +} + static gboolean link_get_by_name (NMPlatform *platform, const char *name, NMPlatformLink *out_link) { @@ -3782,6 +4110,40 @@ build_rtnl_addr (NMPlatform *platform, return (struct nl_object *) rtnladdr_copy; } +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformIP4Address *obj = (const NMPlatformIP4Address *) _obj; + + return build_rtnl_addr (platform, + AF_INET, + obj->ifindex, + &obj->address, + obj->peer_address ? &obj->peer_address : NULL, + obj->plen, + obj->lifetime, + obj->preferred, + 0, + obj->label[0] ? obj->label : NULL); +} + +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformIP6Address *obj = (const NMPlatformIP6Address *) _obj; + + return build_rtnl_addr (platform, + AF_INET6, + obj->ifindex, + &obj->address, + !IN6_IS_ADDR_UNSPECIFIED (&obj->peer_address) ? &obj->peer_address : NULL, + obj->plen, + obj->lifetime, + obj->preferred, + 0, + NULL); +} + static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -4036,6 +4398,38 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source, return (struct nl_object *) rtnlroute; } +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformIP4Route *obj = (const NMPlatformIP4Route *) _obj; + + return build_rtnl_route (AF_INET, + obj->ifindex, + obj->source, + &obj->network, + obj->plen, + &obj->gateway, + NULL, + obj->metric, + obj->mss); +} + +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformIP6Route *obj = (const NMPlatformIP6Route *) _obj; + + return build_rtnl_route (AF_INET6, + obj->ifindex, + obj->source, + &obj->network, + obj->plen, + &obj->gateway, + NULL, + obj->metric, + obj->mss); +} + static gboolean ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 23498cf5a9..caad16e2c8 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2393,6 +2393,8 @@ source_to_string (NMIPConfigSource source) switch (source) { case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: return "rtprot-kernel"; + case _NM_IP_CONFIG_SOURCE_RTM_F_CLONED: + return "rtm-f-cloned"; case NM_IP_CONFIG_SOURCE_KERNEL: return "kernel"; case NM_IP_CONFIG_SOURCE_SHARED: @@ -2844,7 +2846,11 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); _CMP_FIELD (a, b, inet6_token.is_valid); _CMP_FIELD_STR_INTERNED (a, b, kind); - _CMP_FIELD_STR0 (a, b, udi); + + /* udi is not an interned string, but NMRefString. Hence, + * do a pointer comparison first. */ + _CMP_FIELD_STR_INTERNED (a, b, udi); + _CMP_FIELD_STR_INTERNED (a, b, driver); if (a->addr.len) _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index c6145c56d2..62247bc31c 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -91,8 +91,10 @@ struct _NMPlatformLink { /* NMPlatform initializes this field with a static string. */ const char *kind; - /* Beware: NMPlatform initializes this string with an allocated string. - * Handle it properly (i.e. don't keep a reference to it). */ + /* Beware: NMPlatform initializes this string with an allocated string + * (NMRefString). Handle it properly (i.e. don't keep a reference to it + * without incrementing the ref-counter). + * This property depends on @initialized. */ const char *udi; /* NMPlatform initializes this field with a static string. */ diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 8c90deaec0..38eef91452 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -20,4 +20,1931 @@ #include "nmp-object.h" +#include + +#include "nm-platform-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-logging.h" + +/*********************************************************************************************/ + +#define _LOG_DOMAIN LOGD_PLATFORM + +#define _LOG(level, domain, obj, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + const NMPObject *const __obj = (obj); \ + \ + _nm_log (__level, __domain, 0, \ + "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __obj, \ + (__obj ? NMP_OBJECT_GET_CLASS (__obj)->obj_type_name : "???") \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END +#define _LOG_LEVEL_ENABLED(level, domain) \ + ( nm_logging_enabled ((level), (domain)) ) + +#ifdef NM_MORE_LOGGING +#define _LOGT_ENABLED() _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN) +#define _LOGT(obj, ...) _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__) +#else +#define _LOGT_ENABLED() FALSE +#define _LOGT(obj, ...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__); } } G_STMT_END +#endif + +#define _LOGD(obj, ...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, obj, __VA_ARGS__) +#define _LOGI(obj, ...) _LOG (LOGL_INFO , _LOG_DOMAIN, obj, __VA_ARGS__) +#define _LOGW(obj, ...) _LOG (LOGL_WARN , _LOG_DOMAIN, obj, __VA_ARGS__) +#define _LOGE(obj, ...) _LOG (LOGL_ERR , _LOG_DOMAIN, obj, __VA_ARGS__) + +/*********************************************************************************************/ + +struct _NMPCache { + /* the cache contains only one hash table for all object types, and similarly + * it contains only one NMMultiIndex. + * This works, because different object types don't ever compare equal and + * because their index ids also don't overlap. + * + * For routes and addresses, the cache contains an address if (and only if) the + * object was reported via netlink. + * For links, the cache contain a link if it was reported by either netlink + * or udev. That means, a link object can be alive, even if it was already + * removed via netlink. + * + * This effectively merges the udev-device cache into the NMPCache. + */ + + GHashTable *idx_main; + NMMultiIndex *idx_multi; + + gboolean use_udev; +}; + +/******************************************************************/ + +static inline guint +_id_hash_ip6_addr (const struct in6_addr *addr) +{ + guint hash = (guint) 0x897da53981a13ULL; + int i; + + for (i = 0; i < sizeof (*addr); i++) + hash = (hash * 33) + ((const guint8 *) addr)[i]; + return hash; +} + +static const char * +_link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname) +{ + const char *driver = NULL; + + nm_assert (kind == g_intern_string (kind)); + + if (udev_device) { + driver = nmp_utils_udev_get_driver (udev_device); + if (driver) + return driver; + } + + if (kind) + return kind; + + if (ifname) { + char *d; + + if (nmp_utils_ethtool_get_driver_info (ifname, &d, NULL, NULL)) { + driver = d && d[0] ? g_intern_string (d) : NULL; + g_free (d); + if (driver) + return driver; + } + } + + return "unknown"; +} + +void +_nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) +{ + const char *driver = NULL; + const char *udi = NULL; + gboolean initialized = FALSE; + + nm_assert (NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK); + + /* The link contains internal fields that are combined by + * properties from netlink and udev. Update those properties */ + + /* When a link is not in netlink, it's udev fields don't matter. */ + if (obj->_link.netlink.is_in_netlink) { + driver = _link_get_driver (obj->_link.udev.device, + obj->link.kind, + obj->link.name); + if (obj->_link.udev.device) { + udi = g_udev_device_get_sysfs_path (obj->_link.udev.device); + initialized = TRUE; + } else if (!use_udev) { + /* If we don't use udev, we immediately mark the link as initialized. + * + * For that, we consult @use_udev argument, that is cached via + * nmp_cache_use_udev_get(). It is on purpose not to test + * for a writable /sys on every call. A minor reason for that is + * performance, but the real reason is reproducibility. + * + * If you want to support changing of whether udev is enabled, + * reset the value via nmp_cache_use_udev_set() carefully -- and + * possibly update the links in the cache accordingly. + * */ + initialized = TRUE; + } + } + + obj->link.driver = driver; + obj->link.udi = nm_ref_string_replace (obj->link.udi, udi); + obj->link.initialized = initialized; +} + +static void +_nmp_object_fixup_link_master_connected (NMPObject *obj, const NMPCache *cache) +{ + nm_assert (NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK); + + if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) + obj->link.connected = !obj->link.connected; +} + +/******************************************************************/ + +const NMPClass * +nmp_class_from_type (ObjectType obj_type) +{ + g_return_val_if_fail (obj_type > OBJECT_TYPE_UNKNOWN && obj_type <= OBJECT_TYPE_MAX, NULL); + + return &_nmp_classes[obj_type - 1]; +} + +/******************************************************************/ + +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++; + + _LOGT (obj, "ref: %d", 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); + _LOGT (obj, "%s: %d", + obj->_ref_count <= 1 ? "destroy" : "unref", + obj->_ref_count - 1); + 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) +{ + nm_ref_string_unref (obj->link.udi); + g_clear_object (&obj->_link.udev.device); +} + +static NMPObject * +_nmp_object_new_from_class (const NMPClass *klass) +{ + NMPObject *obj; + + nm_assert (klass); + nm_assert (klass->sizeof_data > 0); + nm_assert (klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data); + + obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object)); + obj->_class = klass; + obj->_ref_count = 1; + _LOGT (obj, "new"); + return obj; +} + +NMPObject * +nmp_object_new (ObjectType obj_type, const NMPlatformObject *plobj) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + NMPObject *obj; + + obj = _nmp_object_new_from_class (klass); + if (plobj) + memcpy (&obj->object, plobj, klass->sizeof_public); + return obj; +} + +NMPObject * +nmp_object_new_link (int ifindex) +{ + NMPObject *obj; + + obj = nmp_object_new (OBJECT_TYPE_LINK, NULL); + obj->link.ifindex = ifindex; + return obj; +} + +/******************************************************************/ + +static const NMPObject * +_nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) +{ + nm_assert (klass); + + memset (obj, 0, sizeof (NMPObject)); + obj->_class = klass; + obj->_ref_count = NMP_REF_COUNT_STACKINIT; + return obj; +} + +const NMPObject * +nmp_object_stackinit (NMPObject *obj, ObjectType obj_type, const NMPlatformObject *plobj) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + + _nmp_object_stackinit_from_class (obj, klass); + if (plobj) + memcpy (&obj->object, plobj, klass->sizeof_public); + return obj; +} + +const NMPObject * +nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) +{ + nm_assert (NMP_OBJECT_IS_VALID (src)); + nm_assert (obj); + + NMP_OBJECT_GET_CLASS (src)->cmd_obj_stackinit_id (obj, src); + return obj; +} + +const NMPObject * +nmp_object_stackinit_id_link (NMPObject *obj, int ifindex) +{ + nmp_object_stackinit (obj, OBJECT_TYPE_LINK, NULL); + 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, int plen) +{ + nmp_object_stackinit (obj, OBJECT_TYPE_IP4_ADDRESS, NULL); + obj->ip4_address.ifindex = ifindex; + obj->ip4_address.address = address; + obj->ip4_address.plen = plen; + 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); +} + +const NMPObject * +nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, int plen) +{ + nmp_object_stackinit (obj, OBJECT_TYPE_IP6_ADDRESS, NULL); + 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, int plen, guint32 metric) +{ + nmp_object_stackinit (obj, 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, int plen, guint32 metric) +{ + nmp_object_stackinit (obj, 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 * +nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) +{ + const NMPClass *klass; + char buf2[sizeof (_nm_platform_to_string_buffer)]; + char buf3[sizeof (_nm_platform_to_string_buffer)]; + const char *str; + + if (!buf) { + buf = _nm_platform_to_string_buffer; + buf_size = sizeof (_nm_platform_to_string_buffer); + } + + if (!obj) { + g_strlcpy (buf, "NULL", buf_size); + return buf; + } + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + + klass = NMP_OBJECT_GET_CLASS (obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_ID: + return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); + case NMP_OBJECT_TO_STRING_ALL: + g_strlcpy (buf2, NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object), sizeof (buf2)); + + if (NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK) { + g_snprintf (buf3, sizeof (buf3), + ",%cin-nl,%p", + obj->_link.netlink.is_in_netlink ? '+' : '-', + obj->_link.udev.device); + } else + buf3[0] = '\0'; + + g_snprintf (buf, buf_size, + "[%s,%p,%d,%ccache,%calive,%cvisible%s; %s]", + klass->obj_type_name, obj, obj->_ref_count, + obj->is_cached ? '+' : '-', + nmp_object_is_alive (obj) ? '+' : '-', + nmp_object_is_visible (obj) ? '+' : '-', + buf3, buf2); + return buf; + case NMP_OBJECT_TO_STRING_PUBLIC: + str = NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object); + if (str != buf) + g_strlcpy (buf, str, buf_size); + return buf; + default: + g_return_val_if_reached ("ERROR"); + } +} + +#define _vt_cmd_plobj_to_string_id(type, plat_type, ...) \ +static const char * \ +_vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsize buf_len) \ +{ \ + plat_type *const obj = (plat_type *) _obj; \ + char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ + \ + (void) buf1; \ + g_snprintf (buf, buf_len, \ + __VA_ARGS__); \ + return buf; \ +} +_vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); +_vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen); +_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s/%d", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1), obj->plen); +_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); + +int +nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) +{ + if (obj1 == obj2) + return 0; + if (!obj1) + return -1; + if (!obj2) + return 1; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), -1); + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), 1); + + if (NMP_OBJECT_GET_CLASS (obj1) != NMP_OBJECT_GET_CLASS (obj2)) + return NMP_OBJECT_GET_CLASS (obj1) < NMP_OBJECT_GET_CLASS (obj2) ? -1 : 1; + + return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object); +} + +gboolean +nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + const NMPClass *klass; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); + + if (obj1 == obj2) + return TRUE; + + klass = NMP_OBJECT_GET_CLASS (obj1); + + if (klass != NMP_OBJECT_GET_CLASS (obj2)) + return FALSE; + + return klass->cmd_obj_equal (obj1, obj2); +} + +static gboolean +_vt_cmd_obj_equal_plain (const NMPObject *obj1, const NMPObject *obj2) +{ + return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object) == 0; +} + +static gboolean +_vt_cmd_obj_equal_link (const NMPObject *obj1, const NMPObject *obj2) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj1); + + if (klass->cmd_plobj_cmp (&obj1->object, &obj2->object) != 0) + return FALSE; + if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink) + return FALSE; + if (obj1->_link.udev.device != obj2->_link.udev.device) + return FALSE; + return TRUE; +} + +/* @src is a const object, which is not entirely correct for link types, where + * we increase the ref count for src->_link.udev.device. + * Hence, nmp_object_copy() can violate the const promise of @src. + * */ +void +nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only) +{ + g_return_if_fail (NMP_OBJECT_IS_VALID (dst)); + g_return_if_fail (NMP_OBJECT_IS_VALID (src)); + g_return_if_fail (!NMP_OBJECT_IS_STACKINIT (dst)); + + if (src != dst) { + const NMPClass *klass = NMP_OBJECT_GET_CLASS (dst); + + g_return_if_fail (klass == NMP_OBJECT_GET_CLASS (src)); + + if (id_only) + klass->cmd_plobj_id_copy (&dst->object, &src->object); + else + klass->cmd_obj_copy (dst, src); + } +} + +#define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \ +static void \ +_vt_cmd_plobj_id_copy_##type (NMPlatformObject *_dst, const NMPlatformObject *_src) \ +{ \ + plat_type *const dst = (plat_type *) _dst; \ + const plat_type *const src = (const plat_type *) _src; \ + { cmd } \ +} +_vt_cmd_plobj_id_copy (link, NMPlatformLink, { + dst->ifindex = src->ifindex; +}); +_vt_cmd_plobj_id_copy (ip4_address, NMPlatformIP4Address, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + dst->address = src->address; +}); +_vt_cmd_plobj_id_copy (ip6_address, NMPlatformIP6Address, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + 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; +}); +_vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + dst->metric = src->metric; + dst->network = src->network; +}); + +static void +_vt_cmd_obj_copy_plain (NMPObject *dst, const NMPObject *src) +{ + memcpy (&dst->object, &src->object, NMP_OBJECT_GET_CLASS (dst)->sizeof_data); +} + +static void +_vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) +{ + if (dst->_link.udev.device != src->_link.udev.device) { + if (dst->_link.udev.device) + g_object_unref (dst->_link.udev.device); + if (src->_link.udev.device) + g_object_ref (src->_link.udev.device); + } + if (dst->link.udi != src->link.udi) { + nm_ref_string_unref (dst->link.udi); + nm_ref_string_ref (src->link.udi); + } + dst->_link = src->_link; +} + +/* Uses internally nmp_object_copy(), hence it also violates the const + * promise for @obj. + * */ +NMPObject * +nmp_object_clone (const NMPObject *obj, gboolean id_only) +{ + NMPObject *dst; + + if (!obj) + return NULL; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + + dst = _nmp_object_new_from_class (NMP_OBJECT_GET_CLASS (obj)); + nmp_object_copy (dst, obj, id_only); + return dst; +} + +gboolean +nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + const NMPClass *klass; + + if (obj1 == obj2) + return TRUE; + if (!obj1 || !obj2) + return FALSE; + + 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 (&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) \ +{ \ + 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); +_vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, + obj1->ifindex == obj2->ifindex + && obj1->plen == obj2->plen + && 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 + && obj1->network == obj2->network); +_vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route, + obj1->ifindex == obj2->ifindex + && obj1->plen == obj2->plen + && obj1->metric == obj2->metric + && IN6_ARE_ADDR_EQUAL( &obj1->network, &obj2->network)); + +guint +nmp_object_id_hash (const NMPObject *obj) +{ + if (!obj) + return 0; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0); + return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_id_hash (&obj->object); +} + +#define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \ +static guint \ +_vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ +{ \ + const plat_type *const obj = (const plat_type *) _obj; \ + guint hash; \ + { cmd; } \ + return hash; \ +} +_vt_cmd_plobj_id_hash (link, NMPlatformLink, { + /* libnl considers: + * .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY, + */ + hash = (guint) 3982791431; + hash = hash + ((guint) obj->ifindex); +}) +_vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { + /* libnl considers: + * .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + * ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN), + */ + hash = (guint) 3591309853; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + ((guint) obj->address); +}) +_vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { + hash = (guint) 2907861637; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + _id_hash_ip6_addr (&obj->address); +}) +_vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { + /* libnl considers: + * .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + * ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | + * ROUTE_ATTR_PRIO), + */ + hash = (guint) 2569857221; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + ((guint) obj->metric); + hash = hash * 33 + ((guint) obj->network); +}) +_vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { + hash = (guint) 3999787007; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + ((guint) obj->metric); + hash = hash * 33 + _id_hash_ip6_addr (&obj->network); +}) + +gboolean +nmp_object_is_alive (const NMPObject *obj) +{ + /* for convenience, allow NULL. */ + if (!obj) + return FALSE; + + return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_alive (obj); +} + +static gboolean +_vt_cmd_obj_is_alive_link (const NMPObject *obj) +{ + return obj->object.ifindex > 0 && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); +} + +static gboolean +_vt_cmd_obj_is_alive_ipx_address (const NMPObject *obj) +{ + return obj->object.ifindex > 0; +} + +static gboolean +_vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) +{ + /* We want to ignore routes that are RTM_F_CLONED but we still + * let nmp_object_from_nl() create such route objects, instead of + * returning NULL right away. + * + * The idea is, that if we have the same route (according to its id) + * in the cache with !RTM_F_CLONED, an update that changes the route + * to be RTM_F_CLONED must remove the instance. + * + * If nmp_object_from_nl() would just return NULL, we couldn't look + * into the cache to see if it contains a route that now disappears + * (because it is cloned). + * + * Instead we create a dead object, and nmp_cache_update_netlink() + * will remove the old version of the update. + **/ + return obj->object.ifindex > 0 && (obj->ip_route.source != _NM_IP_CONFIG_SOURCE_RTM_F_CLONED); +} + +gboolean +nmp_object_is_visible (const NMPObject *obj) + +{ + /* for convenience, allow NULL. */ + if (!obj) + return FALSE; + + return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_visible (obj); +} + +static gboolean +_vt_cmd_obj_is_visible_link (const NMPObject *obj) +{ + return obj->object.ifindex > 0 && obj->_link.netlink.is_in_netlink; +} + +static gboolean +_vt_cmd_obj_is_visible_ipx_address (const NMPObject *obj) +{ + return obj->object.ifindex > 0; +} + +static gboolean +_vt_cmd_obj_is_visible_ipx_route (const NMPObject *obj) +{ + NMIPConfigSource source = obj->ip_route.source; + + return obj->object.ifindex > 0 && (source != _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL && source != _NM_IP_CONFIG_SOURCE_RTM_F_CLONED); +} + +/******************************************************************/ + +/** + * nmp_object_from_nl: + * @platform: platform instance (needed to lookup sysctl) + * @nlo: + * @id_only: if %TRUE, only fill the id fields of the object and leave the + * other fields unset. This is useful to create a needle to lookup a matching + * item in the cache. + * @complete_from_cache: sometimes the netlink object doesn't contain all information. + * If true, look them up in the cache and preserve the original value. + * + * Convert a libnl object to a platform object. + * Returns: a NMPObject containing @nlo. If @id_only is %TRUE, only the id fields + * are defined. + **/ +NMPObject * +nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache) +{ + ObjectType obj_type = _nlo_get_object_type (nlo); + NMPObject *obj; + + if (obj_type == OBJECT_TYPE_UNKNOWN) + return NULL; + + obj = nmp_object_new (obj_type, NULL); + + if (!NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_init_from_nl (platform, &obj->object, nlo, id_only, complete_from_cache)) { + nmp_object_unref (obj); + return NULL; + } + return obj; +} + +struct nl_object * +nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only) +{ + return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_nl (platform, &obj->object, id_only); +} + +/******************************************************************/ + +gboolean +nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b) +{ + /* just memcmp() the entire id. This is potentially dangerous, because + * the struct is not __attribute__((packed)) and not all types have the + * same size. It is important, to memset() the entire struct to 0, + * not only the relevant fields. + * + * You anyway should use the nmp_cache_id_init_*() functions on a stack-allocated + * struct. */ + return memcmp (a, b, sizeof (NMPCacheId)) == 0; +} + +guint +nmp_cache_id_hash (const NMPCacheId *id) +{ + guint hash = 5381; + guint i; + + for (i = 0; i < sizeof (NMPCacheId); i++) + hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */ + return hash; +} + +NMPCacheId * +nmp_cache_id_clone (const NMPCacheId *id) +{ + NMPCacheId *id2; + + id2 = g_slice_new (NMPCacheId); + memcpy (id2, id, sizeof (NMPCacheId)); + return id2; +} + +void +nmp_cache_id_destroy (NMPCacheId *id) +{ + g_slice_free (NMPCacheId, id); +} + +/******************************************************************/ + +NMPCacheId _nmp_cache_id_static; + +NMPCacheId * +nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type) +{ + memset (id, 0, sizeof (NMPCacheId)); + id->_id_type = id_type; + return id; +} + +NMPCacheId * +nmp_cache_id_init_object_type (NMPCacheId *id, ObjectType obj_type) +{ + nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_OBJECT_TYPE); + id->object_type.obj_type = obj_type; + return id; +} + +NMPCacheId * +nmp_cache_id_init_links (NMPCacheId *id, gboolean visible_only) +{ + if (visible_only) + return nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY); + else + return nmp_cache_id_init_object_type (id, OBJECT_TYPE_LINK); +} + +NMPCacheId * +nmp_cache_id_init_addrroute_by_ifindex (NMPCacheId *id, ObjectType obj_type, int ifindex) +{ + nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX); + id->addrroute_by_ifindex.obj_type = obj_type; + id->addrroute_by_ifindex.ifindex = ifindex; + return id; +} + +NMPCacheId * +nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPCacheIdType id_type, gboolean is_v4, int ifindex) +{ + g_return_val_if_fail (NM_IN_SET (id_type, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT), NULL); + nmp_cache_id_init (id, id_type); + id->routes_visible.is_v4 = !!is_v4; + id->routes_visible.ifindex = ifindex; + return id; +} + +/******************************************************************/ + +static gboolean +_nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + + if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) { + *out_id = nmp_cache_id_init_object_type (id, klass->obj_type); + return TRUE; + } + return klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); +} + +static gboolean +_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY: + if (_vt_cmd_obj_is_visible_link (obj)) { + *out_id = nmp_cache_id_init_links (id, TRUE); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ip4_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_address (obj)) { + *out_id = nmp_cache_id_init_addrroute_by_ifindex (id, OBJECT_TYPE_IP4_ADDRESS, obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ip6_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_address (obj)) { + *out_id = nmp_cache_id_init_addrroute_by_ifindex (id, OBJECT_TYPE_IP6_ADDRESS, obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ip4_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_route (obj)) { + *out_id = nmp_cache_id_init_addrroute_by_ifindex (id, OBJECT_TYPE_IP4_ROUTE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL: + if (_vt_cmd_obj_is_visible_ipx_route (obj)) { + *out_id = nmp_cache_id_init_routes_visible (id, id_type, TRUE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + *out_id = nmp_cache_id_init_routes_visible (id, id_type, TRUE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + *out_id = nmp_cache_id_init_routes_visible (id, id_type, TRUE, obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ip6_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + *out_id = nmp_cache_id_init_addrroute_by_ifindex (id, OBJECT_TYPE_IP6_ROUTE, obj->object.ifindex); + return TRUE; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL: + if (_vt_cmd_obj_is_visible_ipx_route (obj)) { + *out_id = nmp_cache_id_init_routes_visible (id, id_type, FALSE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + *out_id = nmp_cache_id_init_routes_visible (id, id_type, FALSE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + *out_id = nmp_cache_id_init_routes_visible (id, id_type, FALSE, obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +/******************************************************************/ + +gboolean +nmp_cache_use_udev_detect () +{ + return access ("/sys", W_OK) == 0; +} + +gboolean +nmp_cache_use_udev_get (const NMPCache *cache) +{ + g_return_val_if_fail (cache, TRUE); + + return cache->use_udev; +} + +gboolean +nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev) +{ + g_return_val_if_fail (cache, FALSE); + + use_udev = !!use_udev; + if (use_udev == cache->use_udev) + return FALSE; + + cache->use_udev = use_udev; + return TRUE; +} + +/******************************************************************/ + +/** + * nmp_cache_link_connected_needs_toggle: + * @cache: the platform cache + * @master: the link object, that is checked whether its connected property + * needs to be toggled. + * @potential_slave: (allow-none): an additional link object that is treated + * as if it was inside @cache. If given, it shaddows a link in the cache + * with the same ifindex. + * @ignore_slave: (allow-none): if set, the check will pretend that @ignore_slave + * is not in the cache. + * + * NMPlatformLink has two connected flags: (master->link.flags&IFF_LOWER_UP) (as reported + * from netlink) and master->link.connected. For bond and bridge master, kernel reports + * those links as IFF_LOWER_UP if they have no slaves attached. We want to present instead + * a combined @connected flag that shows masters without slaves as down. + * + * Check if the connected flag of @master should be toggled according to the content + * of @cache (including @potential_slave). + * + * Returns: %TRUE, if @master->link.connected should be flipped/toggled. + **/ +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) != OBJECT_TYPE_LINK + || master->link.ifindex <= 0 + || !nmp_object_is_visible (master) + || !NM_IN_SET (master->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND)) + return FALSE; + + /* if native IFF_LOWER_UP is down, link.connected must also be down + * regardless of the slaves. */ + if (!NM_FLAGS_HAS (master->link.flags, IFF_LOWER_UP)) + return !!master->link.connected; + + if (potential_slave && NMP_OBJECT_GET_TYPE (potential_slave) != OBJECT_TYPE_LINK) + 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) { + is_lower_up = TRUE; + } else { + links = (const NMPlatformLink *const *) nmp_cache_lookup_multi (cache, nmp_cache_id_init_links (NMP_CACHE_ID_STATIC, FALSE), &len); + for (i = 0; i < len; i++) { + const NMPlatformLink *link = links[i]; + const NMPObject *obj = NMP_OBJECT_UP_CAST ((NMPlatformObject *) link); + + nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST ((NMPlatformObject *) link)) == 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) { + is_lower_up = TRUE; + break; + } + } + } + return !!master->link.connected != is_lower_up; +} + +/** + * nmp_cache_link_connected_needs_toggle_by_ifindex: + * @cache: + * @master_ifindex: the ifindex of a potential master that should be checked + * whether it needs toggling. + * @potential_slave: (allow-none): passed to nmp_cache_link_connected_needs_toggle(). + * It considers @potential_slave as being inside the cache, replacing an existing + * link with the same ifindex. + * @ignore_slave: (allow-onne): passed to nmp_cache_link_connected_needs_toggle(). + * + * The flag obj->link.connected depends on the state of other links in the + * @cache. See also nmp_cache_link_connected_needs_toggle(). Given an ifindex + * of a master, check if the cache contains such a master link that needs + * toogling of the connected flag. + * + * Returns: NULL if there is no master link with ifindex @master_ifindex that should be toggled. + * Otherwise, return the link object from inside the cache with the given ifindex. + * The connected flag of that master should be toggled. + */ +const NMPObject * +nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave) +{ + const NMPObject *master; + + if (master_ifindex > 0) { + master = nmp_cache_lookup_link (cache, master_ifindex); + if (nmp_cache_link_connected_needs_toggle (cache, master, potential_slave, ignore_slave)) + return master; + } + return NULL; +} + +/******************************************************************/ + +const NMPlatformObject *const * +nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len) +{ + return (const NMPlatformObject *const *) nm_multi_index_lookup (cache->idx_multi, + (const NMMultiIndexId *) cache_id, + out_len); +} + +GArray * +nmp_cache_lookup_multi_to_array (const NMPCache *cache, ObjectType obj_type, const NMPCacheId *cache_id) +{ + 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); + + objects = nmp_cache_lookup_multi (cache, cache_id, &len); + array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); + + 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; +} + +const NMPObject * +nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) +{ + g_return_val_if_fail (obj, NULL); + + return g_hash_table_lookup (cache->idx_main, obj); +} + +const NMPObject * +nmp_cache_lookup_link (const NMPCache *cache, int ifindex) +{ + NMPObject obj_needle; + + return nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); +} + +const NMPObject * +nmp_cache_lookup_link_full (const NMPCache *cache, + int ifindex, + const char *ifname, + gboolean visible_only, + NMLinkType link_type, + NMPObjectMatchFn match_fn, + gpointer user_data) +{ + NMPObject obj_needle; + const NMPObject *obj; + const NMPlatformObject *const *list; + guint i, len; + NMPCacheId cache_id; + + if (ifindex > 0) { + obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); + + if ( !obj + || (visible_only && !nmp_object_is_visible (obj)) + || (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) + || (ifname && strcmp (obj->link.name, ifname)) + || (match_fn && !match_fn (obj, user_data))) + return NULL; + return obj; + } else if (!ifname && !match_fn) + return NULL; + else { + list = nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (&cache_id, OBJECT_TYPE_LINK), &len); + for (i = 0; i < len; i++) { + obj = NMP_OBJECT_UP_CAST (list[i]); + + 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; + + return obj; + } + return NULL; + } +} + +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) +{ + NMPCacheIdType id_type; + + for (id_type = 0; id_type <= NMP_CACHE_ID_TYPE_MAX; 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 (); + } else { + if (!nm_multi_index_add (cache->idx_multi, &cache_id->base, &obj->object)) + g_assert_not_reached (); + } + } +} + +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 (!g_hash_table_add (cache->idx_main, obj)) + g_assert_not_reached (); + obj->is_cached = TRUE; + _nmp_cache_update_cache (cache, obj, FALSE); +} + +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 (); + + /* @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)); +} + +static void +_nmp_cache_update_update (NMPCache *cache, NMPObject *obj, const NMPObject *new) +{ + NMPCacheIdType id_type; + + nm_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (new)); + nm_assert (obj->is_cached); + nm_assert (!new->is_cached); + + for (id_type = 0; id_type <= NMP_CACHE_ID_TYPE_MAX; id_type++) { + NMPCacheId cache_id_storage_obj, cache_id_storage_new; + const NMPCacheId *cache_id_obj, *cache_id_new; + + if (!_nmp_object_init_cache_id (obj, id_type, &cache_id_storage_obj, &cache_id_obj)) + 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 (); + } + nmp_object_copy (obj, new, FALSE); +} + +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) +{ + NMPObject *old; + + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + 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; + 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) { + /* We found an identical object, but we only delete it if it's the same pointer as + * @obj. */ + 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); + 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) +{ + if (NMP_OBJECT_GET_TYPE (obj_needle) == OBJECT_TYPE_LINK) { + NMPObject *old; + auto_nmp_obj NMPObject *obj = NULL; + + /* 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); + + if (!old->_link.netlink.is_in_netlink) { + nm_assert (old->_link.udev.device); + 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); + return NMP_CACHE_OPS_REMOVED; + } + + obj = nmp_object_clone (old, FALSE); + obj->_link.netlink.is_in_netlink = FALSE; + + _nmp_object_fixup_link_master_connected (obj, cache); + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + + 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; + } else + return nmp_cache_remove (cache, obj_needle, FALSE, out_obj, out_was_visible, pre_hook, user_data); +} + +/** + * nmp_cache_update_netlink: + * @cache: the platform cache + * @obj: 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: + * + * Returns: how the cache changed. + **/ +NMPCacheOpsType +nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + + nm_assert (NMP_OBJECT_IS_VALID (obj)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj)); + nm_assert (!obj->is_cached); + + /* 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) != OBJECT_TYPE_LINK || + ( !obj->_link.udev.device + && !obj->link.driver + && !obj->link.udi)); + + old = g_hash_table_lookup (cache->idx_main, obj); + + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + + if (!old) { + if (!nmp_object_is_alive (obj)) + return NMP_CACHE_OPS_UNCHANGED; + + if (NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK) { + _nmp_object_fixup_link_master_connected (obj, cache); + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + } + + if (out_obj) + *out_obj = nmp_object_ref (obj); + + if (pre_hook) + pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); + _nmp_cache_update_add (cache, 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. Actually, we expect the user to + * create a new instance with nmp_object_from_nl(). That is what nmp_cache_update_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) == 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); + + /* Merge the netlink parts with what we have from udev. */ + g_clear_object (&obj->_link.udev.device); + obj->_link.udev.device = old->_link.udev.device ? g_object_ref (old->_link.udev.device) : NULL; + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + } + } else + is_alive = nmp_object_is_alive (obj); + + 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 (nmp_object_equal (old, obj)) + return NMP_CACHE_OPS_UNCHANGED; + + 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; + } +} + +NMPCacheOpsType +nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + auto_nmp_obj NMPObject *obj = NULL; + + old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + + if (!old) { + if (!udev_device) + return NMP_CACHE_OPS_UNCHANGED; + + obj = nmp_object_new (OBJECT_TYPE_LINK, NULL); + obj->link.ifindex = ifindex; + obj->_link.udev.device = g_object_ref (udev_device); + + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + + nm_assert (nmp_object_is_alive (obj)); + + if (out_obj) + *out_obj = nmp_object_ref (obj); + + if (pre_hook) + pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); + _nmp_cache_update_add (cache, 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); + + if (old->_link.udev.device == udev_device) + return NMP_CACHE_OPS_UNCHANGED; + + if (!udev_device && !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); + return NMP_CACHE_OPS_REMOVED; + } + + obj = nmp_object_clone (old, FALSE); + + g_clear_object (&obj->_link.udev.device); + obj->_link.udev.device = udev_device ? g_object_ref (udev_device) : NULL; + + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + + nm_assert (nmp_object_is_alive (obj)); + + 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; + } +} + +NMPCacheOpsType +nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + auto_nmp_obj NMPObject *obj = NULL; + + old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + + if (!old) { + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + + return NMP_CACHE_OPS_UNCHANGED; + } + + 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_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; + + nm_assert (nmp_object_is_alive (obj)); + + 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; +} + +/******************************************************************/ + +NMPCache * +nmp_cache_new () +{ + 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); + cache->use_udev = nmp_cache_use_udev_detect (); + return cache; +} + +void +nmp_cache_free (NMPCache *cache) +{ + GHashTableIter iter; + NMPObject *obj; + + /* 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; + } + + nm_multi_index_free (cache->idx_multi); + g_hash_table_unref (cache->idx_main); + + g_free (cache); +} + +/******************************************************************/ + +void +ASSERT_nmp_cache_is_consistent (const NMPCache *cache) +{ +#ifdef 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)) { + NMPCacheIdType id_type; + + g_assert (NMP_OBJECT_IS_VALID (obj)); + g_assert (nmp_object_is_alive (obj)); + + for (id_type = 0; id_type <= NMP_CACHE_ID_TYPE_MAX; 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[OBJECT_TYPE_MAX] = { + [OBJECT_TYPE_LINK - 1] = { + .obj_type = OBJECT_TYPE_LINK, + .sizeof_data = sizeof (NMPObjectLink), + .sizeof_public = sizeof (NMPlatformLink), + .obj_type_name = "link", + .nl_type = "route/link", + .addr_family = AF_UNSPEC, + .rtm_gettype = RTM_GETLINK, + .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link, + .cmd_obj_equal = _vt_cmd_obj_equal_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_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_link, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_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_to_string_id = _vt_cmd_plobj_to_string_id_link, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_link_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp, + }, + [OBJECT_TYPE_IP4_ADDRESS - 1] = { + .obj_type = OBJECT_TYPE_IP4_ADDRESS, + .sizeof_data = sizeof (NMPObjectIP4Address), + .sizeof_public = sizeof (NMPlatformIP4Address), + .obj_type_name = "ip4-address", + .nl_type = "route/addr", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETADDR, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ip4_address, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip4_address, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_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_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip4_address_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp, + }, + [OBJECT_TYPE_IP6_ADDRESS - 1] = { + .obj_type = OBJECT_TYPE_IP6_ADDRESS, + .sizeof_data = sizeof (NMPObjectIP6Address), + .sizeof_public = sizeof (NMPlatformIP6Address), + .obj_type_name = "ip6-address", + .nl_type = "route/addr", + .addr_family = AF_INET6, + .rtm_gettype = RTM_GETADDR, + .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ip6_address, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip6_address, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_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_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip6_address_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp + }, + [OBJECT_TYPE_IP4_ROUTE - 1] = { + .obj_type = OBJECT_TYPE_IP4_ROUTE, + .sizeof_data = sizeof (NMPObjectIP4Route), + .sizeof_public = sizeof (NMPlatformIP4Route), + .obj_type_name = "ip4-route", + .nl_type = "route/route", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ip4_route, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip4_route, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_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_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip4_route_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, + }, + [OBJECT_TYPE_IP6_ROUTE - 1] = { + .obj_type = OBJECT_TYPE_IP6_ROUTE, + .sizeof_data = sizeof (NMPObjectIP6Route), + .sizeof_public = sizeof (NMPlatformIP6Route), + .obj_type_name = "ip6-route", + .nl_type = "route/route", + .addr_family = AF_INET6, + .rtm_gettype = RTM_GETROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ip6_route, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip6_route, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_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_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip6_route_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, + }, +}; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index fff607048e..99f0eaf0c8 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -24,6 +24,12 @@ #include "config.h" #include "nm-platform.h" +#include "nm-multi-index.h" +#include "nm-macros-internal.h" + +#include +#include + typedef enum { /*< skip >*/ OBJECT_TYPE_UNKNOWN, @@ -36,4 +42,342 @@ typedef enum { /*< skip >*/ OBJECT_TYPE_MAX = __OBJECT_TYPE_LAST - 1, } ObjectType; +typedef enum { /*< skip >*/ + NMP_OBJECT_TO_STRING_ID, + NMP_OBJECT_TO_STRING_PUBLIC, + NMP_OBJECT_TO_STRING_ALL, +} NMPObjectToStringMode; + +typedef enum { /*< skip >*/ + NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, + NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, + NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, + NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, +} NMPCacheOpsType; + +/* The NMPCacheIdType are the different index types. + * + * An object of a certain object-type, can be candidate to being + * indexed by a certain NMPCacheIdType or not. For example, all + * objects are indexed via an index of type NMP_CACHE_ID_TYPE_OBJECT_TYPE, + * but only route objects are indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL. + * + * Of one index type, there can be different indexes or not. + * For example, there is only one single instance of + * NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY, because this instance is + * applicable for all link objects. + * But there are different instances of NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX + * type index, which differ in v4/v6, the ifindex, and whether the index + * is for routes or address instances. + * + * But one object, can only be indexed by one particular index of one + * type. For example, a certain address instance is only indexed by + * the index NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX with matching v4/v6 + * and ifindex. + * */ +typedef enum { /*< skip >*/ + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + + NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY, + + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, + + /* three indeces for the visibile routes. */ + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL, + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, + + __NMP_CACHE_ID_TYPE_MAX, + NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, +} NMPCacheIdType; + +typedef struct _NMPObject NMPObject; + +typedef struct { + union { + NMMultiIndexId base; + guint8 _id_type; /* NMPCacheIdType as guint8 */ + struct { + /* NMP_CACHE_ID_TYPE_OBJECT_TYPE */ + guint8 _id_type; + guint8 obj_type; /* ObjectType as guint8 */ + } object_type; + struct { + /* NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY */ + guint8 _id_type; + + /* the @_global_id is only defined by it's type and has no arguments. + * It is used by NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY. There is only + * one single occurence of an index of this type. */ + } _global_id; + struct { + /* NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX */ + guint8 _id_type; + guint8 obj_type; /* ObjectType as guint8 */ + int ifindex; + } addrroute_by_ifindex; + struct { + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */ + guint8 _id_type; + guint8 is_v4; + int ifindex; + } routes_visible; + }; +} NMPCacheId; + +extern NMPCacheId _nmp_cache_id_static; +#define NMP_CACHE_ID_STATIC (&_nmp_cache_id_static) + +typedef struct { + ObjectType obj_type; + int addr_family; + int rtm_gettype; + int sizeof_data; + int sizeof_public; + const char *obj_type_name; + const char *nl_type; + const char *signal_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); + + gboolean (*cmd_obj_equal) (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); + + /* functions that operate on NMPlatformObject */ + gboolean (*cmd_plobj_init_from_nl) (NMPlatform *platform, NMPlatformObject *obj, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache); + struct nl_object *(*cmd_plobj_to_nl) (NMPlatform *platform, const NMPlatformObject *obj, gboolean id_only); + 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); + const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size); + const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj); + int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); +} NMPClass; + +extern const NMPClass _nmp_classes[OBJECT_TYPE_MAX]; + +typedef struct { + NMPlatformLink _public; + + struct { + guint8 is_in_netlink; + } netlink; + + struct { + GUdevDevice *device; + } udev; +} NMPObjectLink; + +typedef struct { + NMPlatformIP4Address _public; +} NMPObjectIP4Address; + +typedef struct { + NMPlatformIP4Route _public; +} NMPObjectIP4Route; + +typedef struct { + NMPlatformIP6Address _public; +} NMPObjectIP6Address; + +typedef struct { + NMPlatformIP6Route _public; +} NMPObjectIP6Route; + +struct _NMPObject { + const NMPClass *_class; + int _ref_count; + guint8 is_cached; + union { + NMPlatformObject object; + + NMPlatformLink link; + NMPObjectLink _link; + + NMPlatformIPAddress ip_address; + NMPlatformIPXAddress ipx_address; + NMPlatformIP4Address ip4_address; + NMPlatformIP6Address ip6_address; + NMPObjectIP4Address _ip4_address; + NMPObjectIP6Address _ip6_address; + + NMPlatformIPRoute ip_route; + NMPlatformIPXRoute ipx_route; + NMPlatformIP4Route ip4_route; + NMPlatformIP6Route ip6_route; + NMPObjectIP4Route _ip4_route; + NMPObjectIP6Route _ip6_route; + }; +}; + +static inline gboolean +NMP_CLASS_IS_VALID (const NMPClass *klass) +{ + return klass >= &_nmp_classes[0] + && klass <= &_nmp_classes[G_N_ELEMENTS (_nmp_classes)] + && ((((char *) klass) - ((char *) NULL)) % (&_nmp_classes[1] - &_nmp_classes[0])) == 0; +} + +#define NMP_REF_COUNT_STACKINIT (G_MAXINT) + +static inline NMPObject * +NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) +{ + NMPObject *obj; + + 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))); + return obj; +} +#define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST ((const NMPlatformObject *) (plobj))) + +static inline gboolean +NMP_OBJECT_IS_VALID (const NMPObject *obj) +{ + nm_assert (!obj || ( obj + && obj->_ref_count > 0 + && NMP_CLASS_IS_VALID (obj->_class))); + + /* There isn't really much to check. Either @obj is NULL, or we must + * assume that it points to valid memory. */ + return obj != NULL; +} + +static inline gboolean +NMP_OBJECT_IS_STACKINIT (const NMPObject *obj) +{ + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + + return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT; +} + +static inline const NMPClass * +NMP_OBJECT_GET_CLASS (const NMPObject *obj) +{ + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + return obj->_class; +} + +static inline ObjectType +NMP_OBJECT_GET_TYPE (const NMPObject *obj) +{ + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + + return obj ? obj->_class->obj_type : OBJECT_TYPE_UNKNOWN; +} + + + +const NMPClass *nmp_class_from_type (ObjectType obj_type); + +NMPObject *nmp_object_ref (NMPObject *object); +void nmp_object_unref (NMPObject *object); +NMPObject *nmp_object_new (ObjectType obj_type, const NMPlatformObject *plob); +NMPObject *nmp_object_new_link (int ifindex); + +const NMPObject *nmp_object_stackinit (NMPObject *obj, ObjectType obj_type, const NMPlatformObject *plobj); +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, int plen); +const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, int plen); +const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, int plen, guint32 metric); +const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, int plen, guint32 metric); + +const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); +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); +guint nmp_object_id_hash (const NMPObject *obj); +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); + +#define auto_nmp_obj __attribute__((cleanup(_nmp_auto_obj_cleanup))) +static inline void +_nmp_auto_obj_cleanup (NMPObject **pobj) +{ + nmp_object_unref (*pobj); +} + +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); + +NMPCacheId *nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type); +NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, ObjectType obj_type); +NMPCacheId *nmp_cache_id_init_links (NMPCacheId *id, gboolean visible_only); +NMPCacheId *nmp_cache_id_init_addrroute_by_ifindex (NMPCacheId *id, ObjectType obj_type, int ifindex); +NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPCacheIdType id_type, gboolean is_v4, int ifindex); + +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, ObjectType 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); + +const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, + int ifindex, + const char *ifname, + gboolean visible_only, + 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_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); + +gboolean nmp_cache_use_udev_detect (void); +gboolean nmp_cache_use_udev_get (const NMPCache *cache); +gboolean nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev); + +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, GUdevDevice *udev_device, 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 (void); +void nmp_cache_free (NMPCache *cache); + +NMPObject *nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache); +struct nl_object *nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only); + +/* the following functions are currently implemented inside nm-linux-platform, because + * they depend on utility functions there. */ +ObjectType _nlo_get_object_type (const struct nl_object *nlo); +gboolean _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); + #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 7c01d9850c..b922599cb3 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -24,6 +24,366 @@ #include "nm-test-utils.h" +struct { + GList *udev_devices; +} global; + +/******************************************************************/ + +static gboolean +_nmp_object_id_equal (const NMPObject *a, const NMPObject *b) +{ + gboolean a_b = nmp_object_id_equal (a, b); + + g_assert (NM_IN_SET (a_b, FALSE, TRUE) && a_b == nmp_object_id_equal (b, a)); + return a_b; +} +#define nmp_object_id_equal _nmp_object_id_equal + +static gboolean +_nmp_object_equal (const NMPObject *a, const NMPObject *b) +{ + gboolean a_b = nmp_object_equal (a, b); + + g_assert (NM_IN_SET (a_b, FALSE, TRUE) && a_b == nmp_object_equal (b, a)); + return a_b; +} +#define nmp_object_equal _nmp_object_equal + +/******************************************************************/ + +static void +_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains) +{ + const NMPlatformObject *const *objects; + guint i, len; + gboolean found; + + g_assert (cache_id); + g_assert (NMP_OBJECT_IS_VALID (obj)); + + g_assert (nmp_cache_lookup_obj (cache, obj) == obj); + + objects = nmp_cache_lookup_multi (cache, cache_id, &len); + + g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len])); + + found = FALSE; + for (i = 0; i < len; i++) { + NMPObject *o; + + g_assert (objects[i]); + o = NMP_OBJECT_UP_CAST (objects[i]); + g_assert (NMP_OBJECT_IS_VALID (o)); + + if (obj == o) { + g_assert (!found); + found = TRUE; + } + } + + g_assert (!!contains == found); +} + +/******************************************************************/ + +typedef struct { + NMPCache *cache; + NMPCacheOpsType expected_ops_type; + const NMPObject *obj_clone; + NMPObject *new_clone; + gboolean was_visible; + gboolean called; +} _NMPCacheUpdateData; + +static void +_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +{ + _NMPCacheUpdateData *data = user_data; + + g_assert (data); + g_assert (!data->called); + g_assert (data->cache == cache); + + g_assert_cmpint (data->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)); + 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)); + 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)); + 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) +{ + NMPCacheOpsType ops_type; + NMPObject *obj2; + gboolean was_visible; + auto_nmp_obj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); + auto_nmp_obj NMPObject *new_clone = NULL; + 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 = g_object_ref (obj_old->_link.udev.device); + _nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache)); + + 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); + + 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); + + 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; + else + nmp_object_unref (obj2); + if (out_was_visible) + *out_was_visible = was_visible; +} + +static const NMPlatformLink pl_link_2 = { + .ifindex = 2, + .name = "eth0", + .type = NM_LINK_TYPE_ETHERNET, +}; + +static const NMPlatformLink pl_link_3 = { + .ifindex = 3, + .name = "wlan0", + .type = NM_LINK_TYPE_WIFI, +}; + +static void +test_cache_link () +{ + NMPCache *cache; + NMPObject *obj1, *obj2; + NMPObject objs1; + gboolean was_visible; + NMPCacheId cache_id_storage; + GUdevDevice *udev_device_2 = g_list_nth_data (global.udev_devices, 0); + GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0); + NMPCacheOpsType ops_type; + + cache = nmp_cache_new (); + + nmp_cache_use_udev_set (cache, g_rand_int_range (nmtst_get_rand (), 0, 2)); + + /* if we have a link, and don't set is_in_netlink, adding it has no effect. */ + obj1 = nmp_object_new (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); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (!obj2); + g_assert (!was_visible); + g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); + nmp_object_unref (obj1); + + /* Only when setting @is_in_netlink the link is added. */ + obj1 = nmp_object_new (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); + 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_links (&cache_id_storage, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* updating the same link with identical value, has no effect. */ + obj1 = nmp_object_new (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); + 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); + + /* remove the link from netlink */ + obj1 = nmp_object_new (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); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (was_visible); + g_assert (!nmp_cache_lookup_obj (cache, obj1)); + 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); + + udev_device_2 = NULL; + 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); + 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_links (&cache_id_storage, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE); + nmp_object_unref (obj2); + } + + /* add it in netlink too. */ + obj1 = nmp_object_new (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); + 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_links (&cache_id_storage, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* remove again from netlink. */ + obj1 = nmp_object_new (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); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (was_visible); + 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_links (&cache_id_storage, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE); + } else { + g_assert (nmp_cache_lookup_obj (cache, obj1) == 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)); + } + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* 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); + 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_links (&cache_id_storage, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE); + g_assert_cmpint (obj2->link.initialized, ==, FALSE); + nmp_object_unref (obj2); + + /* add it in netlink too. */ + obj1 = nmp_object_new (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); + 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_links (&cache_id_storage, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, 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); + + /* remove UDEV. */ + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL); + 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_links (&cache_id_storage, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, 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); + } + + nmp_cache_free (cache); +} /******************************************************************/ @@ -33,11 +393,34 @@ int main (int argc, char **argv) { int result; + gs_unref_object GUdevClient *udev_client = NULL; nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + udev_client = g_udev_client_new ((const char *[]) { "net", NULL }); + { + gs_unref_object GUdevEnumerator *udev_enumerator = g_udev_enumerator_new (udev_client); + + g_udev_enumerator_add_match_subsystem (udev_enumerator, "net"); + + /* Demand that the device is initialized (udev rules ran, + * device has a stable name now) in case udev is running + * (not in a container). */ + if (access ("/sys", W_OK) == 0) + g_udev_enumerator_add_match_is_initialized (udev_enumerator); + + global.udev_devices = g_udev_enumerator_execute (udev_enumerator); + } + + g_test_add_func ("/nmp-object/cache_link", test_cache_link); + result = g_test_run (); + while (global.udev_devices) { + g_object_unref (global.udev_devices->data); + global.udev_devices = g_list_remove (global.udev_devices, global.udev_devices->data); + } + return result; } From f268dca0f1e3cc7acd06d3806b8668e99a9b6df1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 6 Apr 2015 18:29:36 +0200 Subject: [PATCH 20/50] platform: add new platform caching to nm-linux-platform.c It is not yet used, only add new code beside the existing implementation. It will be used later. --- src/platform/nm-linux-platform.c | 1143 ++++++++++++++++++++++++++++++ 1 file changed, 1143 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index ce49083310..add4ac9493 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -109,7 +109,25 @@ #define warning(...) _LOG (LOGL_WARN , _LOG_DOMAIN, NULL, __VA_ARGS__) #define error(...) _LOG (LOGL_ERR , _LOG_DOMAIN, NULL, __VA_ARGS__) +NM_PRAGMA_WARNING_DISABLE ("-Wunused") +NM_PRAGMA_WARNING_DISABLE ("-Wunused-function") + +/****************************************************************** + * Forward declarations and enums + ******************************************************************/ + +typedef enum { + DELAYED_ACTION_TYPE_REFRESH_ALL, + DELAYED_ACTION_TYPE_REFRESH_LINK, + DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, + DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, + DELAYED_ACTION_TYPE_MASTER_CONNECTED, +} DelayedActionType; + static gboolean tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props); +static void do_refresh_object (NMPlatform *platform, const NMPObject *obj_needle, NMPlatformReason reason, gboolean handle_all_delayed_actions, const NMPObject **out_obj); +static void do_refresh_all (NMPlatform *platform, ObjectType obj_type, int ifindex, NMPlatformReason reason); +static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); /****************************************************************** * libnl unility functions and wrappers @@ -405,18 +423,30 @@ _support_kernel_extended_ifa_flags_get (void) * NMPlatform types and functions ******************************************************************/ +typedef struct { + DelayedActionType action_type; + gpointer user_data; +} DelayedActionData; + typedef struct { struct nl_sock *nlh; struct nl_sock *nlh_event; struct nl_cache *link_cache; struct nl_cache *address_cache; struct nl_cache *route_cache; + NMPCache *cache; GIOChannel *event_channel; guint event_id; GUdevClient *udev_client; GHashTable *udev_devices; + struct { + GArray *list; + gint is_handling; + guint idle_id; + } delayed_action; + GHashTable *wifi_data; } NMLinuxPlatformPrivate; @@ -587,6 +617,75 @@ get_kernel_object (struct nl_sock *sock, struct nl_object *needle) } } +static NMPObject * +kernel_get_object (NMPlatform *platform, struct nl_sock *sock, const NMPObject *needle) +{ + auto_nl_object struct nl_object *nlo = NULL; + struct nl_object *nlo_needle; + NMPObject *obj; + int nle; + struct nl_cache *cache; + + switch (NMP_OBJECT_GET_TYPE (needle)) { + case OBJECT_TYPE_LINK: + nle = rtnl_link_get_kernel (sock, needle->link.ifindex, + needle->link.name[0] ? needle->link.name : NULL, + (struct rtnl_link **) &nlo); + switch (nle) { + case -NLE_SUCCESS: + _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); + obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); + _LOGD ("kernel-get-link: succeeds for ifindex=%d, name=%s: %s", + needle->link.ifindex, needle->link.name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + return obj; + case -NLE_NODEV: + _LOGD ("kernel-get-link: succeeds for ifindex=%d, name=%s: NO DEVICE", + needle->link.ifindex, needle->link.name); + return NULL; + default: + _LOGD ("kernel-get-link: fails for ifindex=%d, name=%s: %s (%d)", + needle->link.ifindex, needle->link.name, + nl_geterror (nle), nle); + return NULL; + } + case OBJECT_TYPE_IP4_ADDRESS: + case OBJECT_TYPE_IP6_ADDRESS: + case OBJECT_TYPE_IP4_ROUTE: + case OBJECT_TYPE_IP6_ROUTE: + /* FIXME: every time we refresh *one* object, we request an + * entire dump. */ + nle = nl_cache_alloc_and_fill (nl_cache_ops_lookup (NMP_OBJECT_GET_CLASS (needle)->nl_type), + sock, &cache); + if (nle) { + _LOGE ("kernel-get-%s: fails for %s: %s (%d)", + NMP_OBJECT_GET_CLASS (needle)->obj_type_name, + nmp_object_to_string (needle, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), nle); + return NULL; + } + + nlo_needle = nmp_object_to_nl (platform, needle, TRUE); + nlo = nl_cache_search (cache, nlo_needle); + nl_object_put (nlo_needle); + nl_cache_free (cache); + + if (!nlo) { + _LOGD ("kernel-get-%s: had no results for %s", + NMP_OBJECT_GET_CLASS (needle)->obj_type_name, + nmp_object_to_string (needle, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + return NULL; + } + obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); + _LOGD ("kernel-get-%s: succeeds: %s", + NMP_OBJECT_GET_CLASS (needle)->obj_type_name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + return obj; + default: + g_return_val_if_reached (NULL); + } +} + /* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */ static int add_kernel_object (struct nl_sock *sock, struct nl_object *object) @@ -1991,6 +2090,467 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor } } +static void +do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason) +{ + gboolean is_visible; + + 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)); + + 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; + } + + _LOGT ("emit signal %s %s: %s (%ld)", + NMP_OBJECT_GET_CLASS (obj)->signal_type, + nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0), + (long) reason); + + g_signal_emit_by_name (platform, NMP_OBJECT_GET_CLASS (obj)->signal_type, obj->object.ifindex, &obj->object, (NMPlatformSignalChangeType) cache_op, reason); +} + +/******************************************************************/ + +static const char * +delayed_action_to_string (DelayedActionType action_type) +{ + static const char *lookup[] = { + [DELAYED_ACTION_TYPE_REFRESH_ALL] = "refresh-all", + [DELAYED_ACTION_TYPE_REFRESH_LINK] = "refresh-link", + [DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX] = "refres-addresses-for-ifindex", + [DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX] = "refesh-routes-for-ifindex", + [DELAYED_ACTION_TYPE_MASTER_CONNECTED] = "master-connected", + }; + if ( ((gssize) action_type) >= 0 + && ((gssize) action_type) < G_N_ELEMENTS (lookup) + && lookup[action_type]) + return lookup[action_type]; + g_return_val_if_reached ("unknown"); +} + +#define _LOGT_delayed_action(data, operation) \ + _LOGT ("delayed-action: %s %s (%d) [%p / %d]", ""operation, delayed_action_to_string ((data)->action_type), (int) (data)->action_type, (data)->user_data, GPOINTER_TO_INT ((data)->user_data)) + +static void +delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + auto_nmp_obj NMPObject *obj_cache = NULL; + gboolean was_visible; + 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, NM_PLATFORM_REASON_INTERNAL); +} + +static void +delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex) +{ + NMPObject needle; + + if (ifindex <= 0) + return; + do_refresh_object (platform, nmp_object_stackinit_id_link (&needle, ifindex), NM_PLATFORM_REASON_INTERNAL, FALSE, NULL); +} + +static void +delayed_action_handle_REFRESH_ADDRESSES_FOR_IFINDEX (NMPlatform *platform, int ifindex) +{ + if (ifindex > 0) + do_refresh_all (platform, OBJECT_TYPE_IP4_ADDRESS, ifindex, NM_PLATFORM_REASON_INTERNAL); +} + +static void +delayed_action_handle_REFRESH_ROUTES_FOR_IFINDEX (NMPlatform *platform, int ifindex) +{ + if (ifindex > 0) + do_refresh_all (platform, OBJECT_TYPE_IP4_ROUTE, ifindex, NM_PLATFORM_REASON_INTERNAL); +} + +static void +delayed_action_handle_REFRESH_ALL (NMPlatform *platform) +{ + do_refresh_all (platform, OBJECT_TYPE_LINK, 0, NM_PLATFORM_REASON_INTERNAL); + do_refresh_all (platform, OBJECT_TYPE_IP4_ADDRESS, 0, NM_PLATFORM_REASON_INTERNAL); + do_refresh_all (platform, OBJECT_TYPE_IP4_ROUTE, 0, NM_PLATFORM_REASON_INTERNAL); +} + +static const DelayedActionData * +delayed_action_find (GArray *array, DelayedActionType action_type, gconstpointer *user_data, gboolean consider_user_data) +{ + guint i; + + for (i = 0; i < array->len; i++) { + const DelayedActionData *d = &g_array_index (array, DelayedActionData, i); + + if ( d->action_type == action_type + && (!consider_user_data || d->user_data == user_data)) + return d; + } + return NULL; +} + +static guint +delayed_action_prune_all (GArray *array, DelayedActionType action_type, gconstpointer *user_data, gboolean consider_user_data) +{ + guint i, pruned = 0; + + for (i = 0; i < array->len; ) { + const DelayedActionData *d = &g_array_index (array, DelayedActionData, i); + + if ( action_type == d->action_type + && (!consider_user_data || user_data == d->user_data)) { + g_array_remove_index_fast (array, i); + pruned++; + } else + i++; + } + return pruned; +} + +static gboolean +delayed_action_handle_one (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const DelayedActionData *data, *data2; + int ifindex; + + if (priv->delayed_action.list->len == 0) { + nm_clear_g_source (&priv->delayed_action.idle_id); + return FALSE; + } + + /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. + * This type of action is entirely cache-internal and is here to resolve a + * cache inconsistency. It should be fixed right away. */ + if ((data = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_MASTER_CONNECTED, NULL, FALSE))) { + /* It is possible that we also have a REFREH_LINK action scheduled that would refresh + * this link anyway. Still, handle the potential cache-inconsistency first and possibly + * reload the link later. */ + _LOGT_delayed_action (data, "handle"); + ifindex = GPOINTER_TO_INT (data->user_data); + delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); + delayed_action_handle_MASTER_CONNECTED (platform, ifindex); + return TRUE; + } + + if ((data = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL, FALSE))) { + _LOGT_delayed_action (data, "handle"); + + /* all our delayed actions are plain data. We can just flush the array. */ + g_array_set_size (priv->delayed_action.list, 0); + delayed_action_prune_all (priv->delayed_action.list, data->action_type, NULL, FALSE); + delayed_action_handle_REFRESH_ALL (platform); + return TRUE; + } + + data = &g_array_index (priv->delayed_action.list, DelayedActionData, 0); + + switch (data->action_type) { + + case DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX: + ifindex = GPOINTER_TO_INT (data->user_data); + + /* We should reload routes for @ifindex. Check if we also should reload + * the link too, that has preference. Reloading link can trigger a reloading of routes. */ + data2 = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex), TRUE); + if (data2) { + _LOGT_delayed_action (data2, "handle"); + delayed_action_prune_all (priv->delayed_action.list, data2->action_type, data2->user_data, TRUE); + delayed_action_handle_REFRESH_LINK (platform, ifindex); + break; + } + + /* If we should reload routes but also have to reload addresses for the same @ifindex, + * reload the addresses first. Reloading addreses can trigger a reload or routes. */ + data2 = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, GINT_TO_POINTER (ifindex), TRUE); + if (data2) { + _LOGT_delayed_action (data2, "handle"); + delayed_action_prune_all (priv->delayed_action.list, data2->action_type, data2->user_data, TRUE); + delayed_action_handle_REFRESH_ADDRESSES_FOR_IFINDEX (platform, ifindex); + break; + } + + _LOGT_delayed_action (data, "handle"); + delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); + delayed_action_handle_REFRESH_ROUTES_FOR_IFINDEX (platform, ifindex); + break; + + + case DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX: + ifindex = GPOINTER_TO_INT (data->user_data); + + /* We should reload addresses for @ifindex. Check if we also should reload + * the link too, that has preference. Reloading link can trigger a reloading of addresses. */ + data2 = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex), TRUE); + if (data2) { + _LOGT_delayed_action (data2, "handle"); + delayed_action_prune_all (priv->delayed_action.list, data2->action_type, data2->user_data, TRUE); + delayed_action_handle_REFRESH_LINK (platform, ifindex); + break; + } + + _LOGT_delayed_action (data, "handle"); + delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); + delayed_action_handle_REFRESH_ADDRESSES_FOR_IFINDEX (platform, ifindex); + break; + + + case DELAYED_ACTION_TYPE_REFRESH_LINK: + _LOGT_delayed_action (data, "handle"); + ifindex = GPOINTER_TO_INT (data->user_data); + delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); + delayed_action_handle_REFRESH_LINK (platform, ifindex); + break; + + + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +delayed_action_handle_all (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + gboolean any = FALSE; + + nm_clear_g_source (&priv->delayed_action.idle_id); + priv->delayed_action.is_handling++; + while (delayed_action_handle_one (platform)) + any = TRUE; + priv->delayed_action.is_handling--; + return any; +} + +static gboolean +delayed_action_handle_idle (gpointer user_data) +{ + NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0; + delayed_action_handle_all (user_data); + return G_SOURCE_REMOVE; +} + +static void +delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionData data = { + .action_type = action_type, + .user_data = user_data, + }; + + _LOGT_delayed_action (&data, "schedule"); + + if (priv->delayed_action.is_handling == 0 && priv->delayed_action.idle_id == 0) + priv->delayed_action.idle_id = g_idle_add (delayed_action_handle_idle, platform); + + g_array_append_val (priv->delayed_action.list, data); +} + +/******************************************************************/ + +static void +cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +{ + NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPClass *klass; + char str_buf[sizeof (_nm_platform_to_string_buffer)]; + char str_buf2[sizeof (_nm_platform_to_string_buffer)]; + + 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)); + + 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))); + + _LOGT ("update-cache-%s: %s: %s%s%s", + klass->obj_type_name, + (ops_type == NMP_CACHE_OPS_UPDATED + ? "UPDATE" + : (ops_type == 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)) + : "")); + + switch (klass->obj_type) { + case 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)); + } + { + /* check whether we are about to change a master link that needs toggling connected state. */ + if (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)); + } + { + int ifindex = 0; + + /* if we remove a link (from netlink), we must refresh the addresses and routes */ + if (ops_type == NMP_CACHE_OPS_REMOVED) + ifindex = old->link.ifindex; + else if ( ops_type == NMP_CACHE_OPS_UPDATED + && !new->_link.netlink.is_in_netlink + && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) + ifindex = new->link.ifindex; + + if (ifindex > 0) { + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, GINT_TO_POINTER (ifindex)); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, GINT_TO_POINTER (ifindex)); + } + } + { + /* if a link goes down, we must refresh routes */ + if ( ops_type == NMP_CACHE_OPS_UPDATED + && old->_link.netlink.is_in_netlink + && NM_FLAGS_HAS (old->link.flags, IFF_LOWER_UP) + && new->_link.netlink.is_in_netlink + && !NM_FLAGS_HAS (new->link.flags, IFF_LOWER_UP)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, GINT_TO_POINTER (new->link.ifindex)); + } + { + /* on enslave/release, we also refresh the master. */ + 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.flags, IFF_LOWER_UP) : 2) + != (old && old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (old->link.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; + + if (ifindex1 > 0) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); + if (ifindex2 > 0 && ifindex1 != ifindex2) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2)); + } + + } + break; + case OBJECT_TYPE_IP4_ADDRESS: + case 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) { + /* Theoretically, it might suffice to reload only IPv4 or IPv6 separately. Don't + * do that optimizatoin, as our current implementation with nl_cache_alloc_and_fill() + * anyway dumps all addresses. */ + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, + GINT_TO_POINTER (old->ip_address.ifindex)); + } + } + default: + break; + } +} + +static NMPCacheOpsType +cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPObject *obj_cache; + gboolean was_visible; + NMPCacheOpsType cache_op; + + cache_op = nmp_cache_remove_netlink (priv->cache, obj_needle, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); + + if (out_obj_cache) + *out_obj_cache = obj_cache; + else + nmp_object_unref (obj_cache); + if (out_was_visible) + *out_was_visible = was_visible; + + return cache_op; +} + +static NMPCacheOpsType +cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPObject *obj_cache; + gboolean was_visible; + NMPCacheOpsType cache_op; + + /* This is basically a convenience method to call nmp_cache_update() and do_emit_signal() + * at once. */ + + cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, reason); + + if (out_obj_cache) + *out_obj_cache = obj_cache; + else + nmp_object_unref (obj_cache); + if (out_was_visible) + *out_was_visible = was_visible; + + return cache_op; +} + +/******************************************************************/ + static struct nl_object * build_rtnl_link (int ifindex, const char *name, NMLinkType type); static gboolean @@ -2068,6 +2628,191 @@ refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed return TRUE; } +static void +do_refresh_object (NMPlatform *platform, const NMPObject *obj_needle, NMPlatformReason reason, gboolean handle_all_delayed_actions, const NMPObject **out_obj) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + auto_nmp_obj NMPObject *obj_kernel = NULL; + auto_nmp_obj NMPObject *obj_cache = NULL; + NMPCacheOpsType cache_op; + const NMPClass *klass; + char str_buf[sizeof (_nm_platform_to_string_buffer)]; + char str_buf2[sizeof (_nm_platform_to_string_buffer)]; + + obj_kernel = kernel_get_object (platform, priv->nlh, obj_needle); + + klass = NMP_OBJECT_GET_CLASS (obj_needle); + if (klass->obj_type == OBJECT_TYPE_LINK) { + int ifindex = obj_kernel ? obj_kernel->link.ifindex : obj_needle->link.ifindex; + + if (ifindex >= 0) + delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex), TRUE); + } + + if (obj_kernel) + cache_op = cache_update_netlink (platform, obj_kernel, &obj_cache, NULL, reason); + else if ( klass->obj_type != OBJECT_TYPE_LINK + || obj_needle->link.ifindex > 0) + cache_op = cache_remove_netlink (platform, obj_needle, &obj_cache, NULL, reason); + else { + /* The needle has no ifindex and kernel_get_object() could not resolve the instance. + * For example infiniband_partition_add() does this. + * + * We cannot lookup this instance by name. */ + cache_op = NMP_CACHE_OPS_UNCHANGED; + } + + _LOGD ("refresh-%s: refreshed %s: %s", + klass->obj_type_name, + nmp_object_to_string (obj_needle, NMP_OBJECT_TO_STRING_ID, str_buf, sizeof (str_buf)), + nmp_object_to_string (obj_cache, NMP_OBJECT_TO_STRING_PUBLIC, str_buf2, sizeof (str_buf2))); + + if (handle_all_delayed_actions) { + if (delayed_action_handle_all (platform)) { + /* The object might have changed. Lookup again. */ + if (out_obj) { + /* We only lookup links by ifindex, not ifname. */ + if ( klass->obj_type != OBJECT_TYPE_LINK + || obj_needle->link.ifindex > 0) { + *out_obj = nmp_cache_lookup_obj (priv->cache, obj_needle); + } else + *out_obj = NULL; + } + return; + } + } + + if (out_obj) { + /* @out_obj has a somewhat unexpected meaning. It is not the same instance as returned by + * cache_update_netlink(). On the contrary, it is the object inside the cache (if it exists). + * + * As the cache owns the object already, we don't pass ownership (a reference). + * + * The effect is identical to lookup nmp_cache_lookup_obj() after do_refresh_object(). + * + * Here we don't do lookup by name. If @obj_needle only has an ifname, but no ifindex, + * the lookup cannot succeed. */ + if (cache_op == NMP_CACHE_OPS_REMOVED) + *out_obj = NULL; + else + *out_obj = obj_cache; + } +} + +static void +do_refresh_all (NMPlatform *platform, ObjectType obj_type, int ifindex, NMPlatformReason reason) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint i, j, len; + gs_unref_hashtable GHashTable *alive_list = NULL; + gs_unref_ptrarray GPtrArray *prune_list = NULL; + NMPCacheOpsType cache_op; + struct nl_object *nlo; + struct nl_cache *cache; + int nle; + const NMPClass *klass; + + /* we can only reload v4 and v6 together. Coerce the obj_type to v4. */ + if (obj_type == OBJECT_TYPE_IP6_ADDRESS) + obj_type = OBJECT_TYPE_IP4_ADDRESS; + else if (obj_type == OBJECT_TYPE_IP6_ROUTE) + obj_type = OBJECT_TYPE_IP4_ROUTE; + + if (obj_type == OBJECT_TYPE_LINK) + ifindex = 0; + + if (obj_type == OBJECT_TYPE_IP4_ADDRESS) { + if (ifindex > 0) + delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, GINT_TO_POINTER (ifindex), TRUE); + else + delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, NULL, FALSE); + } else if (obj_type == OBJECT_TYPE_IP4_ROUTE) { + if (ifindex > 0) + delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, GINT_TO_POINTER (ifindex), TRUE); + else + delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, NULL, FALSE); + } else if (obj_type == OBJECT_TYPE_LINK) + delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, FALSE); + + klass = nmp_class_from_type (obj_type); + + if (obj_type == OBJECT_TYPE_LINK) + nle = rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &cache); + else { + nle = nl_cache_alloc_and_fill (nl_cache_ops_lookup (klass->nl_type), + priv->nlh, &cache); + } + if (nle || !cache) { + error ("refresh-all for %s on interface %d failed: %s (%d)", + klass->obj_type_name, + ifindex, + nl_geterror (nle), nle); + return; + } + + alive_list = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); + + for (nlo = nl_cache_get_first (cache); nlo; nlo = nl_cache_get_next (nlo)) { + auto_nmp_obj NMPObject *obj_kernel = NULL; + NMPObject *obj_cache; + + if (obj_type == OBJECT_TYPE_LINK) + _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); + + obj_kernel = nmp_object_from_nl (platform, nlo, FALSE, TRUE); + if (!obj_kernel) + continue; + + if (ifindex > 0 && obj_kernel->object.ifindex != ifindex) + continue; + + cache_op = cache_update_netlink (platform, obj_kernel, &obj_cache, NULL, reason); + if (!obj_cache) + continue; + + if (cache_op == NMP_CACHE_OPS_REMOVED) + nmp_object_unref (obj_cache); + else + g_hash_table_add (alive_list, obj_cache); + } + nl_cache_free (cache); + + prune_list = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + /* Find all the objects that are not in the alive list and must be pruned. */ + for (j = 0; j < 2; j++) { + NMPCacheId cache_id; + const NMPlatformObject *const *list; + ObjectType obj_type_j; + + obj_type_j = obj_type; + if (j == 1) { + if (obj_type == OBJECT_TYPE_LINK) + break; + if (obj_type == OBJECT_TYPE_IP4_ADDRESS) + obj_type_j = OBJECT_TYPE_IP6_ADDRESS; + else + obj_type_j = OBJECT_TYPE_IP6_ROUTE; + } + + if (ifindex > 0) + nmp_cache_id_init_addrroute_by_ifindex (&cache_id, obj_type_j, ifindex); + else + nmp_cache_id_init_object_type (&cache_id, obj_type_j); + + list = nmp_cache_lookup_multi (priv->cache, &cache_id, &len); + for (i = 0; i < len; i++) { + NMPObject *o = NMP_OBJECT_UP_CAST (list[i]); + + if (!g_hash_table_lookup (alive_list, o)) + g_ptr_array_add (prune_list, nmp_object_ref (o)); + } + } + + for (i = 0; i < prune_list->len; i++) + cache_remove_netlink (platform, prune_list->pdata[i], NULL, NULL, reason); +} + /* Decreases the reference count if @obj for convenience */ static gboolean add_object (NMPlatform *platform, struct nl_object *obj) @@ -2108,6 +2853,48 @@ add_object (NMPlatform *platform, struct nl_object *obj) return refresh_object (platform, object, FALSE, NM_PLATFORM_REASON_INTERNAL); } +static gboolean +kernel_add_object (NMPlatform *platform, ObjectType obj_type, const struct nl_object *nlo) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int nle; + + g_return_val_if_fail (nlo, FALSE); + + switch (obj_type) { + case OBJECT_TYPE_LINK: + nle = rtnl_link_add (priv->nlh, (struct rtnl_link *) nlo, NLM_F_CREATE); + break; + case OBJECT_TYPE_IP4_ADDRESS: + case OBJECT_TYPE_IP6_ADDRESS: + nle = rtnl_addr_add (priv->nlh, (struct rtnl_addr *) nlo, NLM_F_CREATE | NLM_F_REPLACE); + break; + case OBJECT_TYPE_IP4_ROUTE: + case OBJECT_TYPE_IP6_ROUTE: + nle = rtnl_route_add (priv->nlh, (struct rtnl_route *) nlo, NLM_F_CREATE | NLM_F_REPLACE); + break; + default: + g_return_val_if_reached (-NLE_INVAL); + } + + _LOGT ("kernel-add-%s: returned %s (%d)", + nmp_class_from_type (obj_type)->obj_type_name, nl_geterror (nle), -nle); + + switch (nle) { + case -NLE_SUCCESS: + return -NLE_SUCCESS; + case -NLE_EXIST: + /* NLE_EXIST is considered equivalent to success to avoid race conditions. You + * never know when something sends an identical object just before + * NetworkManager. */ + if (obj_type != OBJECT_TYPE_LINK) + return -NLE_SUCCESS; + /* fall-through */ + default: + return nle; + } +} + /* Decreases the reference count if @obj for convenience */ static gboolean delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refresh_object) @@ -2174,6 +2961,160 @@ out: return result; } +static int +kernel_delete_object (NMPlatform *platform, ObjectType object_type, const struct nl_object *object) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int nle; + + switch (object_type) { + case OBJECT_TYPE_LINK: + nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); + break; + case OBJECT_TYPE_IP4_ADDRESS: + case OBJECT_TYPE_IP6_ADDRESS: + nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0); + break; + case OBJECT_TYPE_IP4_ROUTE: + case OBJECT_TYPE_IP6_ROUTE: + nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0); + break; + default: + g_assert_not_reached (); + } + + switch (nle) { + case -NLE_SUCCESS: + return NLE_SUCCESS; + case -NLE_OBJ_NOTFOUND: + _LOGT ("kernel-delete-%s: failed with \"%s\" (%d), meaning the object was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return -NLE_SUCCESS; + case -NLE_FAILURE: + if (object_type == OBJECT_TYPE_IP6_ADDRESS) { + /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */ + _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return NLE_SUCCESS; + } + break; + case -NLE_NOADDR: + if (object_type == OBJECT_TYPE_IP4_ADDRESS || object_type == OBJECT_TYPE_IP6_ADDRESS) { + _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return -NLE_SUCCESS; + } + break; + default: + break; + } + _LOGT ("kernel-delete-%s: failed with %s (%d)", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return nle; +} + +static int +kernel_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean *complete_from_cache) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nl_msg *msg; + int nle; + const int nlflags = 0; + int ifindex; + + ifindex = rtnl_link_get_ifindex (nlo); + + g_return_val_if_fail (ifindex > 0, FALSE); + + /* Previously, we were using rtnl_link_change(), which builds a request based + * on the diff with an original link instance. + * + * The diff only reused ifi_family, ifi_index, ifi_flags, and name from + * the original link (see rtnl_link_build_change_request()). + * + * We don't do that anymore as we don't have an "orig" netlink instance that + * we can use. Instead the caller must ensure to properly initialize @nlo, + * especially it must set family, ifindex (or ifname) and flags. + * ifname should be set *only* if the caller wishes to change the name. + * + * @complete_from_cache is a convenience to copy the link flags over the link inside + * the platform cache. */ + + if (*complete_from_cache) { + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) { + _LOGT ("kernel-change-link: failure changing link %d: cannot complete link", ifindex); + *complete_from_cache = FALSE; + return -NLE_INVAL; + } + + rtnl_link_set_flags (nlo, obj_cache->link.flags); + + /* If the caller wants to rename the link, he should explicitly set + * rtnl_link_set_name(). In all other cases, it should leave the name + * unset. Unfortunately, there is not public API in libnl to modify the + * attribute mask and clear (link->ce_mask = ~LINK_ATTR_IFNAME), so we + * require the caller to do the right thing -- i.e. don't set the name. + */ + } + + /* We don't use rtnl_link_change() because we have no original rtnl_link object + * at hand. We also don't use rtnl_link_add() because that doesn't have the + * hack to retry with RTM_SETLINK. Reimplement a mix of both. */ + + nle = rtnl_link_build_add_request (nlo, nlflags, &msg); + if (nle < 0) { + _LOGT ("kernel-change-link: failure changing link %d: cannot construct message (%s, %d)", + ifindex, nl_geterror (nle), -nle); + return nle; + } + +retry: + nle = nl_send_auto_complete (priv->nlh, msg); + if (nle < 0) + goto errout; + + nle = nl_wait_for_ack(priv->nlh); + if (nle == -NLE_OPNOTSUPP && nlmsg_hdr (msg)->nlmsg_type == RTM_NEWLINK) { + nlmsg_hdr (msg)->nlmsg_type = RTM_SETLINK; + goto retry; + } + +errout: + nlmsg_free(msg); + + /* NLE_EXIST is considered equivalent to success to avoid race conditions. You + * never know when something sends an identical object just before + * NetworkManager. + * + * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find + * firmware for the device, especially on nm_platform_link_set_up (). + * This is basically the same check as in the original code and could + * potentially be improved. + */ + switch (nle) { + case -NLE_SUCCESS: + _LOGT ("kernel-change-link: success changing link %d", ifindex); + break; + case -NLE_EXIST: + _LOGT ("kernel-change-link: success changing link %d: %s (%d)", + ifindex, nl_geterror (nle), -nle); + break; + case -NLE_OBJ_NOTFOUND: + _LOGT ("kernel-change-link: failure changing link %d: firmware not found (%s, %d)", + ifindex, nl_geterror (nle), -nle); + break; + default: + _LOGT ("kernel-change-link: failure changing link %d: netlink error (%s, %d)", + ifindex, nl_geterror (nle), -nle); + break; + } + + return nle; +} + static void ref_object (struct nl_object *obj, void *data) { @@ -2549,6 +3490,20 @@ sysctl_get (NMPlatform *platform, const char *path) /******************************************************************/ +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)) { + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + return NULL; + } + + return obj_cache; +} + static GArray * link_get_all (NMPlatform *platform) { @@ -2624,6 +3579,135 @@ _nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj obj->type); } +static gboolean +do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, const NMPObject **out_obj) +{ + NMPObject obj_needle; + int nle; + + nle = kernel_add_object (platform, OBJECT_TYPE_LINK, (const struct nl_object *) nlo); + if (nle < 0) { + _LOGE ("do-add-link: failure adding link '%s': %s", name, nl_geterror (nle)); + return FALSE; + } + _LOGD ("do-add-link: success adding link '%s'", name); + + nmp_object_stackinit_id_link (&obj_needle, 0); + g_strlcpy (obj_needle.link.name, name, sizeof (obj_needle.link.name)); + + do_refresh_object (platform, &obj_needle, NM_PLATFORM_REASON_INTERNAL, TRUE, out_obj); + + /* Return true, because kernel_add_object() succeeded. This doesn't indicate that the + * object is now actuall in the cache, because there could be a race. + * + * For that, you'd have to look at @out_obj. */ + return TRUE; +} + +static gboolean +do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, NMLinkType expected_link_type, NMPlatformLink *out_link) +{ + const NMPObject *obj; + + do_add_link (platform, name, nlo, &obj); + if (!obj || (expected_link_type != NM_LINK_TYPE_NONE && expected_link_type != obj->link.type)) + return FALSE; + if (out_link) + *out_link = obj->link; + return TRUE; +} + +static gboolean +do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +{ + int nle; + + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), + OBJECT_TYPE_IP4_ADDRESS, OBJECT_TYPE_IP6_ADDRESS, + OBJECT_TYPE_IP4_ROUTE, OBJECT_TYPE_IP6_ROUTE)); + + nle = kernel_add_object (platform, NMP_OBJECT_GET_CLASS (obj_id)->obj_type, (const struct nl_object *) nlo); + if (nle < 0) { + _LOGW ("do-add-%s: failure adding %s '%s': %s (%d)", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + 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; + } + _LOGD ("do-add-%s: success adding object %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + + do_refresh_object (platform, obj_id, NM_PLATFORM_REASON_INTERNAL, TRUE, NULL); + + /* The return value doesn't say, whether the object is in the platform cache after adding + * it. That would be wrong, because do_refresh_object() is not in sync with kernel_add_object() + * and there could be a race. + * Instead the return value says, whether kernel_add_object() succeeded. */ + return TRUE; +} + + +static gboolean +do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +{ + auto_nl_object struct nl_object *nlo_free = NULL; + int nle; + + if (!nlo) + nlo = nlo_free = nmp_object_to_nl (platform, obj_id, FALSE); + + nle = kernel_delete_object (platform, NMP_OBJECT_GET_TYPE (obj_id), nlo); + if (nle < 0) + _LOGE ("do-delete-%s: failure deleting '%s': %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); + else + _LOGD ("do-delete-%s: success deleting '%s'", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + + /* also on failure refresh the cache. */ + do_refresh_object (platform, obj_id, NM_PLATFORM_REASON_INTERNAL, TRUE, NULL); + + /* This doesn't say, whether the object is still in the platform cache. Since + * our delete operation is not in sync with do_refresh_object(), that would + * be racy. Instead, just return whether kernel_delete_object() succeeded. */ + return nle >= 0; +} + +static gboolean +do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache) +{ + int nle; + int ifindex; + NMPObject obj_needle; + gboolean complete_from_cache2 = complete_from_cache; + + ifindex = rtnl_link_get_ifindex (nlo); + if (ifindex <= 0) + g_return_val_if_reached (FALSE); + + nle = kernel_change_link (platform, nlo, &complete_from_cache2); + + switch (nle) { + case -NLE_SUCCESS: + _LOGD ("do-change-link: success changing link %d", ifindex); + break; + case -NLE_EXIST: + _LOGD ("do-change-link: success changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); + break; + case -NLE_OBJ_NOTFOUND: + platform->error = NM_PLATFORM_ERROR_NO_FIRMWARE; + /* fall-through */ + default: + if (complete_from_cache != complete_from_cache2) { + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + _LOGD ("do-change-link: failure changing link %d: link does not exist in cache", ifindex); + } else + _LOGE ("do-change-link: failure changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); + return FALSE; + } + + do_refresh_object (platform, nmp_object_stackinit_id_link (&obj_needle, ifindex), NM_PLATFORM_REASON_INTERNAL, TRUE, NULL); + return TRUE; +} + static gboolean link_get_by_name (NMPlatform *platform, const char *name, NMPlatformLink *out_link) { @@ -3977,6 +5061,17 @@ _address_match (struct rtnl_addr *addr, int family, int ifindex) (ifindex == 0 || rtnl_addr_get_ifindex (addr) == ifindex); } +static GArray * +ipx_address_get_all (NMPlatform *platform, int ifindex, gboolean is_v4) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + ObjectType obj_type = is_v4 ? OBJECT_TYPE_IP4_ADDRESS : OBJECT_TYPE_IP6_ADDRESS; + + return nmp_cache_lookup_multi_to_array (priv->cache, + obj_type, + nmp_cache_id_init_addrroute_by_ifindex (NMP_CACHE_ID_STATIC, obj_type, ifindex)); +} + static GArray * ip4_address_get_all (NMPlatform *platform, int ifindex) { @@ -4277,6 +5372,26 @@ _route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean in return rtnl_route_nh_get_ifindex (nexthop) == ifindex; } +static GArray * +ipx_route_get_all (NMPlatform *platform, int ifindex, gboolean is_v4, NMPlatformGetRouteMode mode) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPCacheIdType id_type; + + if (mode == NM_PLATFORM_GET_ROUTE_MODE_ALL) + id_type = NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL; + else if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) + id_type = NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT; + else if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) + id_type = NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT; + else + g_return_val_if_reached (NULL); + + return nmp_cache_lookup_multi_to_array (priv->cache, + is_v4 ? OBJECT_TYPE_IP4_ROUTE : OBJECT_TYPE_IP6_ROUTE, + nmp_cache_id_init_routes_visible (NMP_CACHE_ID_STATIC, id_type, is_v4, ifindex)); +} + static GArray * ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) { @@ -4847,6 +5962,18 @@ setup_socket (gboolean event, gpointer user_data) /******************************************************************/ +static void +cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_device) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + auto_nmp_obj NMPObject *obj_cache = NULL; + gboolean was_visible; + NMPCacheOpsType cache_op; + + cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); +} + static void udev_device_added (NMPlatform *platform, GUdevDevice *udev_device) @@ -4957,6 +6084,10 @@ handle_udev_event (GUdevClient *client, static void nm_linux_platform_init (NMLinuxPlatform *platform) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + priv->cache = nmp_cache_new (); + priv->delayed_action.list = g_array_new (FALSE, FALSE, sizeof (DelayedActionData)); } static void @@ -5054,6 +6185,13 @@ constructed (GObject *_object) static void dispose (GObject *object) { + NMPlatform *platform = NM_PLATFORM (object); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + g_array_set_size (priv->delayed_action.list, 0); + + nm_clear_g_source (&priv->delayed_action.idle_id); + G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } @@ -5062,6 +6200,10 @@ nm_linux_platform_finalize (GObject *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); + nmp_cache_free (priv->cache); + + g_array_unref (priv->delayed_action.list); + /* Free netlink resources */ g_source_remove (priv->event_id); g_io_channel_unref (priv->event_channel); @@ -5196,3 +6338,4 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; } + From 470bcefa5f259cb752660ac0069afe22dee8052e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 5 May 2015 02:30:25 +0200 Subject: [PATCH 21/50] platform: use new platform caching Switch platform caching implementation. Instead of caching libnl objects, cache our own types. Don't remove yet the now obsolete functions. Advantage: * Performance - as we now cache our native NMPlatformObject instances, we no longer have to convert libnl objects every time we access the platform cache. - for most cases, access is now O(1) because we can lookup the object in a hash table. Note that ip4_address_get_all() still has to create a copy of the result (O(n)), but as the caller is about to use those elements, he cannot do better then O(n) anyway. * We cache our own native types and have full control over them. We cannot extend the libnl objects, which has many short-commings: - _rtnl_addr_hack_lifetimes_rel_to_abs() to convert the timestamps to absolute values (and back). - hack_empty_master_iff_lower_up() would modify the internal flag, but it looses the original value. That means, we can only hack the state before putting a link into the cache, but we cannot revert that change, when a slave in the cache changes state. That was previously solved by always refetching the master when a slave changed. Now we can re-evaluate the connected state (DELAYED_ACTION_TYPE_MASTER_CONNECTED). - we implement functions like equality, to-string as most suitable for us. Before we needed hacks like nm_nl_object_diff(), nm_nl_cache_search(), route_search_cache(). - we can extend our objects with exactly those properties we care, and possibly additional properties that are not representable in the libnl objects. - we no longer cache RTM_F_CLONED routes and they get rejected early on as we receive them. - In the future, maybe it'd be interesting the make platform objects immutable (and ref-counted) and expose them directly. * Previous implementation did not order the refresh of objects but called check_cache_items(). Now, those actions are delayed and combined in an attempt to reduce the overall number of reloads. Realize how expensive a check_cache_items() for addresses and routes was: it would iterate all addresses/routes and call refresh_object(). The latter obtains a full dump of *all* objects again, and ignores all but the needle. Note that we probably still schedule some delayed actions that are not needed. Later we can optimize that further (related bug bgo #747985). While some of these points could also have been implemented with caching of libnl objects, that would have become hard to maintain. https://bugzilla.gnome.org/show_bug.cgi?id=747981 --- src/platform/nm-fake-platform.c | 58 ++- src/platform/nm-linux-platform.c | 809 ++++++++++++------------------ src/platform/tests/test-address.c | 4 +- src/platform/tests/test-link.c | 38 +- src/platform/tests/test-route.c | 8 +- src/tests/test-route-manager.c | 8 +- 6 files changed, 410 insertions(+), 515 deletions(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 479e2fbf5c..4069650dbe 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -27,6 +27,7 @@ #include #include "gsystem-local-alloc.h" +#include "nm-utils.h" #include "NetworkManagerUtils.h" #include "nm-fake-platform.h" #include "nm-logging.h" @@ -312,19 +313,23 @@ link_changed (NMPlatform *platform, NMFakePlatformLink *device) g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); if (device->link.master) { + gboolean connected = FALSE; + NMFakePlatformLink *master = link_get (platform, device->link.master); - g_return_if_fail (master != device); + g_return_if_fail (master && master != device); - master->link.connected = FALSE; for (i = 0; i < priv->links->len; i++) { NMFakePlatformLink *slave = &g_array_index (priv->links, NMFakePlatformLink, i); if (slave && slave->link.master == master->link.ifindex && slave->link.connected) - master->link.connected = TRUE; + connected = TRUE; } - link_changed (platform, master); + if (master->link.connected != connected) { + master->link.connected = connected; + link_changed (platform, master); + } } } @@ -332,27 +337,33 @@ static gboolean link_set_up (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); + gboolean up, connected; if (!device) return FALSE; - device->link.up = TRUE; + up = TRUE; + connected = TRUE; switch (device->link.type) { case NM_LINK_TYPE_DUMMY: case NM_LINK_TYPE_VLAN: - device->link.connected = TRUE; break; case NM_LINK_TYPE_BRIDGE: case NM_LINK_TYPE_BOND: case NM_LINK_TYPE_TEAM: - device->link.connected = FALSE; + connected = FALSE; break; default: - device->link.connected = FALSE; + connected = FALSE; g_error ("Unexpected device type: %d", device->link.type); } - link_changed (platform, device); + if ( device->link.up != up + || device->link.connected != connected) { + device->link.up = up; + device->link.connected = connected; + link_changed (platform, device); + } return TRUE; } @@ -365,10 +376,12 @@ link_set_down (NMPlatform *platform, int ifindex) if (!device) return FALSE; - device->link.up = FALSE; - device->link.connected = FALSE; + if (device->link.up || device->link.connected) { + device->link.up = FALSE; + device->link.connected = FALSE; - link_changed (platform, device); + link_changed (platform, device); + } return TRUE; } @@ -566,12 +579,21 @@ static gboolean link_enslave (NMPlatform *platform, int master, int slave) { NMFakePlatformLink *device = link_get (platform, slave); + NMFakePlatformLink *master_device = link_get (platform, master); g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (master_device, FALSE); - device->link.master = master; + if (device->link.master != master) { + device->link.master = master; - link_changed (platform, device); + if (NM_IN_SET (master_device->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) { + device->link.up = TRUE; + device->link.connected = TRUE; + } + + link_changed (platform, device); + } return TRUE; } @@ -1223,8 +1245,8 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, break; } if (i == priv->ip4_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: error adding %s: Network Unreachable", - nm_platform_ip4_route_to_string (&route)); + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", + route.ifindex, nm_utils_inet4_ntop (route.network, NULL), route.plen, route.metric); return FALSE; } } @@ -1290,8 +1312,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, break; } if (i == priv->ip6_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: error adding %s: Network Unreachable", - nm_platform_ip6_route_to_string (&route)); + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", + route.ifindex, nm_utils_inet6_ntop (&route.network, NULL), route.plen, route.metric); return FALSE; } } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index add4ac9493..c077d9cc03 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -909,15 +909,34 @@ read_devtype (const char *sysfs_path) } static NMLinkType -link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink) +link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean complete_from_cache, const char **out_kind) { const char *rtnl_type, *ifname; int i, arptype; - if (!rtnllink) + if (!rtnllink) { + if (out_kind) + *out_kind = NULL; return NM_LINK_TYPE_NONE; + } rtnl_type = rtnl_link_get_type (rtnllink); + if (!rtnl_type && complete_from_cache) { + int ifindex = rtnl_link_get_ifindex (rtnllink); + const NMPObject *obj; + + /* Sometimes we get netlink messages with the link type unset. + * In this case, look it up in the cache. */ + if (ifindex > 0) { + obj = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); + if (obj && obj->_link.netlink.is_in_netlink && obj->link.kind) { + rtnl_type = obj->link.kind; + _LOGT ("link_extract_type(): complete kind from cache: ifindex=%d, kind=%s", ifindex, rtnl_type); + } + } + } + if (out_kind) + *out_kind = rtnl_type; if (rtnl_type) { for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { if (g_strcmp0 (rtnl_type, linktypes[i].rtnl_type) == 0) @@ -1027,7 +1046,7 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin g_strlcpy (info->name, name, sizeof (info->name)); else info->name[0] = '\0'; - info->type = link_extract_type (platform, rtnllink); + info->type = link_extract_type (platform, rtnllink, FALSE, NULL); info->kind = g_intern_string (rtnl_link_get_type (rtnllink)); info->flags = rtnl_link_get_flags (rtnllink); info->up = NM_FLAGS_HAS (info->flags, IFF_UP); @@ -1118,6 +1137,7 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob struct rtnl_link *nlo = (struct rtnl_link *) _nlo; const char *name; struct nl_addr *nladdr; + const char *kind; nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); @@ -1129,8 +1149,8 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob name = rtnl_link_get_name (nlo); if (name) g_strlcpy (obj->name, name, sizeof (obj->name)); - obj->type = link_extract_type (platform, nlo); - obj->kind = g_intern_string (rtnl_link_get_type (nlo)); + obj->type = link_extract_type (platform, nlo, complete_from_cache, &kind); + obj->kind = g_intern_string (kind); obj->flags = rtnl_link_get_flags (nlo); obj->up = NM_FLAGS_HAS (obj->flags, IFF_UP); obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP); @@ -3208,120 +3228,30 @@ static int event_notification (struct nl_msg *msg, gpointer user_data) { NMPlatform *platform = NM_PLATFORM (user_data); - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_cache *cache; - auto_nl_object struct nl_object *object = NULL; - auto_nl_object struct nl_object *cached_object = NULL; - auto_nl_object struct nl_object *kernel_object = NULL; + auto_nl_object struct nl_object *nlo = NULL; + auto_nmp_obj NMPObject *obj = NULL; int event; - int nle; - ObjectType type; event = nlmsg_hdr (msg)->nlmsg_type; if (_support_kernel_extended_ifa_flags_still_undecided () && event == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); - nl_msg_parse (msg, ref_object, &object); - if (!object) + nl_msg_parse (msg, ref_object, &nlo); + if (!nlo) return NL_OK; - type = _nlo_get_object_type (object); + obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - if (type == OBJECT_TYPE_LINK) - _support_user_ipv6ll_detect ((struct rtnl_link *) object); + if ( (obj && NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK) + || (!obj && _nlo_get_object_type (nlo) == OBJECT_TYPE_LINK)) + _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - if (type == OBJECT_TYPE_LINK) { - const char *name = rtnl_link_get_name ((struct rtnl_link *) object); + _LOGD ("event-notification: type %d: %s", event, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + if (obj) + do_refresh_object (platform, obj, NM_PLATFORM_REASON_EXTERNAL, TRUE, NULL); - debug ("netlink event (type %d) for link: %s (%d, family %d)", - event, name ? name : "(unknown)", - rtnl_link_get_ifindex ((struct rtnl_link *) object), - rtnl_link_get_family ((struct rtnl_link *) object)); - } else - debug ("netlink event (type %d)", event); - } - - cache = choose_cache_by_type (platform, type); - cached_object = nm_nl_cache_search (cache, object); - kernel_object = get_kernel_object (priv->nlh, object); - - hack_empty_master_iff_lower_up (platform, kernel_object); - - /* Removed object */ - switch (event) { - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - /* Ignore inconsistent deletion - * - * Quick external deletion and addition can be occasionally - * seen as just a change. - */ - if (kernel_object) - return NL_OK; - /* Ignore internal deletion */ - if (!cached_object) - return NL_OK; - - nl_cache_remove (cached_object); - announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); - if (event == RTM_DELLINK) { - int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) cached_object); - - g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); - } - - return NL_OK; - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - /* Ignore inconsistent addition or change (kernel will send a good one) - * - * Quick sequence of RTM_NEWLINK notifications can be occasionally - * collapsed to just one addition or deletion, depending of whether we - * already have the object in cache. - */ - if (!kernel_object) - return NL_OK; - - /* Ignore unsupported object types (e.g. AF_PHONET family addresses) */ - if (type == OBJECT_TYPE_UNKNOWN) - return NL_OK; - - /* Handle external addition */ - if (!cached_object) { - nle = nl_cache_add (cache, kernel_object); - if (nle) { - error ("netlink cache error: %s", nl_geterror (nle)); - return NL_OK; - } - announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL); - return NL_OK; - } - /* Ignore non-change - * - * This also catches notifications for internal addition or change, unless - * another action occured very soon after it. - */ - if (!nm_nl_object_diff (type, kernel_object, cached_object)) - return NL_OK; - - /* Handle external change */ - nl_cache_remove (cached_object); - nle = nl_cache_add (cache, kernel_object); - if (nle) { - error ("netlink cache error: %s", nl_geterror (nle)); - return NL_OK; - } - announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL); - - return NL_OK; - default: - error ("Unknown netlink event: %d", event); - return NL_OK; - } + return NL_OK; } /******************************************************************/ @@ -3508,27 +3438,32 @@ static GArray * link_get_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *links = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformLink), nl_cache_nitems (priv->link_cache)); - NMPlatformLink device; - struct nl_object *object; - for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - if (init_link (platform, &device, (struct rtnl_link *) object)) - g_array_append_val (links, device); - } - - return links; + return nmp_cache_lookup_multi_to_array (priv->cache, + OBJECT_TYPE_LINK, + nmp_cache_id_init_links (NMP_CACHE_ID_STATIC, TRUE)); } static gboolean _nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; - NMPlatformLink tmp = { 0 }; + const NMPObject *obj; - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - return (rtnllink && init_link (platform, l ? l : &tmp, rtnllink)); + obj = cache_lookup_link (platform, ifindex); + if (obj && l) + *l = obj->link; + return !!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); } static gboolean @@ -3537,22 +3472,23 @@ _nm_platform_link_get_by_address (NMPlatform *platform, size_t length, NMPlatformLink *l) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_object *object; + const NMPObject *obj; + struct _nm_platform_link_get_by_address_data d = { + .address = address, + .length = length, + }; - for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; - struct nl_addr *nladdr; - gconstpointer hwaddr; + if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) + return FALSE; + if (!address) + return FALSE; - nladdr = rtnl_link_get_addr (rtnl_link); - if (nladdr && (nl_addr_get_len (nladdr) == length)) { - hwaddr = nl_addr_get_binary_addr (nladdr); - if (hwaddr && memcmp (hwaddr, address, length) == 0) - return init_link (platform, l, rtnl_link); - } - } - return FALSE; + 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); + if (obj && l) + *l = obj->link; + return !!obj; } static struct nl_object * @@ -3731,7 +3667,7 @@ link_add (NMPlatform *platform, size_t address_len, NMPlatformLink *out_link) { - struct nl_object *l; + auto_nl_object struct nl_object *l = NULL; if (type == NM_LINK_TYPE_BOND) { /* When the kernel loads the bond module, either via explicit modprobe @@ -3757,10 +3693,7 @@ link_add (NMPlatform *platform, rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); } - if (!add_object (platform, l)) - return FALSE; - - return link_get_by_name (platform, name, out_link); + return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link); } static struct rtnl_link * @@ -3819,71 +3752,80 @@ static gboolean link_delete (NMPlatform *platform, int ifindex) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex); + NMPObject obj_needle; + const NMPObject *obj; - if (!rtnllink) { + obj = nmp_cache_lookup_link (priv->cache, ifindex); + if (!obj || !obj->_link.netlink.is_in_netlink) { platform->error = NM_PLATFORM_ERROR_NOT_FOUND; return FALSE; } - return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE), TRUE); + nmp_object_stackinit_id_link (&obj_needle, ifindex); + return do_delete_object (platform, &obj_needle, NULL); } static int link_get_ifindex (NMPlatform *platform, const char *ifname) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj; - return rtnl_link_name2i (priv->link_cache, ifname); + g_return_val_if_fail (ifname, 0); + + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + if (!obj) + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + return obj ? obj->link.ifindex : 0; } static const char * link_get_name (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj = cache_lookup_link (platform, ifindex); - return rtnllink ? rtnl_link_get_name (rtnllink) : NULL; + return obj ? obj->link.name : NULL; } static NMLinkType link_get_type (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj = cache_lookup_link (platform, ifindex); - return link_extract_type (platform, rtnllink); + return obj ? obj->link.type : NM_LINK_TYPE_NONE; } static const char * link_get_type_name (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - NMLinkType link_type; - const char *l; + const NMPObject *obj = cache_lookup_link (platform, ifindex); - if (!rtnllink) + if (!obj) return NULL; - link_type = link_extract_type (platform, rtnllink); - if (link_type != NM_LINK_TYPE_UNKNOWN) { + 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 (link_type); + return nm_link_type_to_string (obj->link.type); } - /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ - l = rtnl_link_get_type (rtnllink); - return l ? g_intern_string (l) : "unknown"; + return str_if_set (obj->link.kind, "unknown"); } static gboolean link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *managed) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GUdevDevice *udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex)); + const NMPObject *link; + GUdevDevice *udev_device = NULL; + + link = nmp_cache_lookup_link (priv->cache, ifindex); + if (link) + udev_device = link->_link.udev.device; if (udev_device && g_udev_device_get_property (udev_device, "NM_UNMANAGED")) { *managed = g_udev_device_get_property_as_boolean (udev_device, "NM_UNMANAGED"); @@ -3896,20 +3838,19 @@ link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *managed) static guint32 link_get_flags (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj = cache_lookup_link (platform, ifindex); - if (!rtnllink) - return IFF_NOARP; - - return rtnl_link_get_flags (rtnllink); + return obj ? obj->link.flags : IFF_NOARP; } static gboolean link_refresh (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (ifindex, NULL); + NMPObject obj_needle; + const NMPObject *obj_cache; - return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL); + do_refresh_object (platform, nmp_object_stackinit_id_link (&obj_needle, ifindex), NM_PLATFORM_REASON_EXTERNAL, TRUE, &obj_cache); + return !!obj_cache; } static gboolean @@ -3921,7 +3862,9 @@ link_is_up (NMPlatform *platform, int ifindex) static gboolean link_is_connected (NMPlatform *platform, int ifindex) { - return !!(link_get_flags (platform, ifindex) & IFF_LOWER_UP); + const NMPObject *obj = cache_lookup_link (platform, ifindex); + + return obj ? obj->link.connected : FALSE; } static gboolean @@ -3934,20 +3877,25 @@ static gboolean link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value) { auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + const NMPObject *obj_cache; + char buf[256]; + obj_cache = cache_lookup_link (platform, ifindex); + if (!obj_cache) + return FALSE; + + rtnl_link_set_flags (change, obj_cache->link.flags); if (value) rtnl_link_set_flags (change, flags); else rtnl_link_unset_flags (change, flags); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - char buf[512]; + _LOGD ("link: change %d: flags %s '%s' (%d)", ifindex, + value ? "set" : "unset", + rtnl_link_flags2str (flags, buf, sizeof (buf)), + flags); - rtnl_link_flags2str (flags, buf, sizeof (buf)); - debug ("link: change %d: flags %s '%s' (%d)", ifindex, value ? "set" : "unset", buf, flags); - } - - return link_change (platform, ifindex, change); + return do_change_link (platform, change, FALSE); } static gboolean @@ -3977,52 +3925,26 @@ link_set_noarp (NMPlatform *platform, int ifindex) static gboolean link_get_ipv6_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId *iid) { -#if HAVE_LIBNL_INET6_TOKEN - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - struct nl_addr *nladdr; - struct in6_addr *addr; + const NMPObject *obj = cache_lookup_link (platform, ifindex); - if (rtnllink && - (rtnl_link_inet6_get_token (rtnllink, &nladdr)) == 0) { - if (nl_addr_get_family (nladdr) != AF_INET6 || - nl_addr_get_len (nladdr) != sizeof (struct in6_addr)) { - nl_addr_put (nladdr); - return FALSE; - } + /* Always lookup @obj, because it properly sets nm_platform_get_error(). + * Otherwise, we could save this '#ifndef HAVE_LIBNL_INET6_TOKEN' */ + if (!obj || !obj->link.inet6_token.is_valid) + return FALSE; - addr = nl_addr_get_binary_addr (nladdr); - iid->id_u8[7] = addr->s6_addr[15]; - iid->id_u8[6] = addr->s6_addr[14]; - iid->id_u8[5] = addr->s6_addr[13]; - iid->id_u8[4] = addr->s6_addr[12]; - iid->id_u8[3] = addr->s6_addr[11]; - iid->id_u8[2] = addr->s6_addr[10]; - iid->id_u8[1] = addr->s6_addr[9]; - iid->id_u8[0] = addr->s6_addr[8]; - nl_addr_put (nladdr); - return TRUE; - } -#endif - return FALSE; + *iid = obj->link.inet6_token.iid; + return TRUE; } static gboolean link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (_support_user_ipv6ll_get ()) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - uint8_t mode = 0; + const NMPObject *obj = cache_lookup_link (platform, ifindex); - if (rtnllink) { - if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) != 0) { - /* Default to "disabled" on error */ - return FALSE; - } - return mode == IN6_ADDR_GEN_MODE_NONE; - } - } -#endif + /* Always lookup @obj, because it properly sets nm_platform_get_error(). + * Otherwise, we could save this '#ifndef HAVE_LIBNL_INET6_ADDR_GEN_MODE' */ + if (obj && obj->link.inet6_addr_gen_mode_inv) + return (~obj->link.inet6_addr_gen_mode_inv) == IN6_ADDR_GEN_MODE_NONE; return FALSE; } @@ -4031,14 +3953,14 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable { #if HAVE_LIBNL_INET6_ADDR_GEN_MODE if (_support_user_ipv6ll_get ()) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + auto_nl_object struct rtnl_link *nlo = _nm_rtnl_link_alloc (ifindex, NULL); guint8 mode = enabled ? IN6_ADDR_GEN_MODE_NONE : IN6_ADDR_GEN_MODE_EUI64; char buf[32]; - rtnl_link_inet6_set_addr_gen_mode (change, mode); + rtnl_link_inet6_set_addr_gen_mode (nlo, mode); debug ("link: change %d: set IPv6 address generation mode to %s", ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf))); - return link_change (platform, ifindex, change); + return do_change_link (platform, nlo, TRUE); } #endif return FALSE; @@ -4062,13 +3984,15 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) static gboolean link_supports_vlans (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj; + + obj = cache_lookup_link (platform, ifindex); /* Only ARPHRD_ETHER links can possibly support VLANs. */ - if (!rtnllink || rtnl_link_get_arptype (rtnllink) != ARPHRD_ETHER) + if (!obj || obj->link.arptype != ARPHRD_ETHER) return FALSE; - return nmp_utils_ethtool_supports_vlans (rtnl_link_get_name (rtnllink)); + return nmp_utils_ethtool_supports_vlans (obj->link.name); } static gboolean @@ -4076,36 +4000,31 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size { auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, length); + gs_free char *mac = NULL; rtnl_link_set_addr (change, nladdr); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - char *mac = nm_utils_hwaddr_ntoa (address, length); - - debug ("link: change %d: address %s (%lu bytes)", ifindex, mac, (unsigned long) length); - g_free (mac); - } - - return link_change (platform, ifindex, change); + _LOGD ("link: change %d: address %s (%lu bytes)", ifindex, + (mac = nm_utils_hwaddr_ntoa (address, length)), + (unsigned long) length); + return do_change_link (platform, change, TRUE); } static gconstpointer link_get_address (NMPlatform *platform, int ifindex, size_t *length) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - struct nl_addr *nladdr; - size_t l = 0; + const NMPObject *obj = cache_lookup_link (platform, ifindex); gconstpointer a = NULL; + guint8 l = 0; - if (rtnllink && - (nladdr = rtnl_link_get_addr (rtnllink))) { - l = nl_addr_get_len (nladdr); - if (l > NM_UTILS_HWADDR_LEN_MAX) { + if (obj && obj->link.addr.len > 0) { + if (obj->link.addr.len > NM_UTILS_HWADDR_LEN_MAX) { if (length) *length = 0; g_return_val_if_reached (NULL); - } else if (l > 0) - a = nl_addr_get_binary_addr (nladdr); + } + a = obj->link.addr.data; + l = obj->link.addr.len; } if (length) @@ -4130,15 +4049,15 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) rtnl_link_set_mtu (change, mtu); debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu); - return link_change (platform, ifindex, change); + return do_change_link (platform, change, TRUE); } static guint32 link_get_mtu (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj = cache_lookup_link (platform, ifindex); - return rtnllink ? rtnl_link_get_mtu (rtnllink) : 0; + return obj ? obj->link.mtu : 0; } static char * @@ -4192,8 +4111,7 @@ vlan_add (NMPlatform *platform, guint32 vlan_flags, NMPlatformLink *out_link) { - struct nl_object *object = build_rtnl_link (0, name, NM_LINK_TYPE_VLAN); - struct rtnl_link *rtnllink = (struct rtnl_link *) object; + auto_nl_object struct rtnl_link *rtnllink = (struct rtnl_link *) build_rtnl_link (0, name, NM_LINK_TYPE_VLAN); unsigned int kernel_flags; kernel_flags = 0; @@ -4211,55 +4129,50 @@ vlan_add (NMPlatform *platform, debug ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)", name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags); - if (!add_object (platform, object)) - return FALSE; - - return link_get_by_name (platform, name, out_link); + return do_add_link_with_lookup (platform, name, rtnllink, NM_LINK_TYPE_VLAN, out_link); } static gboolean vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj = cache_lookup_link (platform, ifindex); + int p = 0, v = 0; + if (obj) { + p = obj->link.parent; + v = obj->link.vlan_id; + } if (parent) - *parent = rtnllink ? rtnl_link_get_link (rtnllink) : 0; + *parent = p; if (vlan_id) - *vlan_id = rtnllink ? rtnl_link_vlan_get_id (rtnllink) : 0; - - return !!rtnllink; + *vlan_id = v; + return !!obj; } static gboolean vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) { - /* We have to use link_get() because a "blank" rtnl_link won't have the - * right data structures to be able to call rtnl_link_vlan_set_ingress_map() - * on it. (Likewise below in vlan_set_egress_map().) - */ - auto_nl_object struct rtnl_link *change = link_get (platform, ifindex); + auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); - if (!change) - return FALSE; + rtnl_link_set_type (change, "vlan"); rtnl_link_vlan_set_ingress_map (change, from, to); debug ("link: change %d: vlan ingress map %d -> %d", ifindex, from, to); - return link_change (platform, ifindex, change); + return do_change_link (platform, change, TRUE); } static gboolean vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) { - auto_nl_object struct rtnl_link *change = link_get (platform, ifindex); + auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); - if (!change) - return FALSE; + rtnl_link_set_type (change, "vlan"); rtnl_link_vlan_set_egress_map (change, from, to); debug ("link: change %d: vlan egress map %d -> %d", ifindex, from, to); - return link_change (platform, ifindex, change); + return do_change_link (platform, change, TRUE); } static gboolean @@ -4270,7 +4183,7 @@ link_enslave (NMPlatform *platform, int master, int slave) rtnl_link_set_master (change, master); debug ("link: change %d: enslave to master %d", slave, master); - return link_change (platform, slave, change); + return do_change_link (platform, change, TRUE); } static gboolean @@ -4282,9 +4195,9 @@ link_release (NMPlatform *platform, int master, int slave) static int link_get_master (NMPlatform *platform, int slave) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, slave); + const NMPObject *obj = cache_lookup_link (platform, slave); - return rtnllink ? rtnl_link_get_master (rtnllink) : 0; + return obj ? obj->link.master : 0; } static char * @@ -4377,30 +4290,36 @@ slave_get_option (NMPlatform *platform, int slave, const char *option) static gboolean infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatformLink *out_link) { - const char *parent_name; - char *path, *id; - gboolean success; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj_parent; + const NMPObject *obj; + NMPObject obj_needle; + gs_free char *path = NULL; + gs_free char *id = NULL; + gs_free char *ifname = NULL; - parent_name = nm_platform_link_get_name (platform, parent); - g_return_val_if_fail (parent_name != NULL, FALSE); + obj_parent = nmp_cache_lookup_link (priv->cache, parent); + if (!obj_parent || !obj_parent->link.name[0]) + g_return_val_if_reached (FALSE); - path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (parent_name)); + ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key); + + path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name)); id = g_strdup_printf ("0x%04x", p_key); - success = nm_platform_sysctl_set (platform, path, id); - g_free (id); - g_free (path); + if (!nm_platform_sysctl_set (platform, path, id)) + return FALSE; - if (success) { - gs_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key); - auto_nl_object struct rtnl_link *rtnllink = NULL; + nmp_object_stackinit_id_link (&obj_needle, 0); + g_strlcpy (obj_needle.link.name, ifname, sizeof (obj_needle.link.name)); - rtnllink = (struct rtnl_link *) build_rtnl_link (0, ifname, NM_LINK_TYPE_INFINIBAND); - success = refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL); - if (success) - success = link_get_by_name (platform, ifname, out_link); - } + do_refresh_object (platform, &obj_needle, NM_PLATFORM_REASON_INTERNAL, TRUE, &obj); - return success; + if (!obj || obj->link.type != NM_LINK_TYPE_INFINIBAND) + return FALSE; + + if (out_link) + *out_link = obj->link; + return TRUE; } typedef struct { @@ -4462,21 +4381,21 @@ static gboolean infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, const char **mode) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; + const NMPObject *obj; IpoibInfo info = { -1, NULL }; - rtnllink = link_get (platform, ifindex); - if (!rtnllink) + obj = cache_lookup_link (platform, ifindex); + if (!obj) return FALSE; if (parent) - *parent = rtnl_link_get_link (rtnllink); + *parent = obj->link.parent; if (nm_rtnl_link_parse_info_data (priv->nlh, ifindex, infiniband_info_data_parser, &info) != 0) { - const char *iface = rtnl_link_get_name (rtnllink); + const char *iface = obj->link.name; char *path, *contents = NULL; /* Fall back to reading sysfs */ @@ -4647,20 +4566,20 @@ static gboolean macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; int err; + const NMPObject *obj; - rtnllink = link_get (platform, ifindex); - if (!rtnllink) + obj = cache_lookup_link (platform, ifindex); + if (!obj) return FALSE; - props->parent_ifindex = rtnl_link_get_link (rtnllink); + props->parent_ifindex = obj->link.parent; err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, macvlan_info_data_parser, props); if (err != 0) { warning ("(%s) could not read properties: %s", - rtnl_link_get_name (rtnllink), nl_geterror (err)); + obj->link.name, nl_geterror (err)); } return (err == 0); } @@ -5075,41 +4994,13 @@ ipx_address_get_all (NMPlatform *platform, int ifindex, gboolean is_v4) static GArray * ip4_address_get_all (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *addresses; - NMPlatformIP4Address address; - struct nl_object *object; - - addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address)); - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - if (_address_match ((struct rtnl_addr *) object, AF_INET, ifindex)) { - if (init_ip4_address (&address, (struct rtnl_addr *) object)) - g_array_append_val (addresses, address); - } - } - - return addresses; + return ipx_address_get_all (platform, ifindex, TRUE); } static GArray * ip6_address_get_all (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *addresses; - NMPlatformIP6Address address; - struct nl_object *object; - - addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Address)); - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - if (_address_match ((struct rtnl_addr *) object, AF_INET6, ifindex)) { - if (init_ip6_address (&address, (struct rtnl_addr *) object)) - g_array_append_val (addresses, address); - } - } - - return addresses; + return ipx_address_get_all (platform, ifindex, FALSE); } #define IPV4LL_NETWORK (htonl (0xA9FE0000L)) @@ -5249,10 +5140,16 @@ ip4_address_add (NMPlatform *platform, guint32 preferred, const char *label) { - return add_object (platform, build_rtnl_addr (platform, AF_INET, ifindex, &addr, - peer_addr ? &peer_addr : NULL, - plen, lifetime, preferred, 0, - label)); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; + + nlo = build_rtnl_addr (platform, AF_INET, ifindex, &addr, + peer_addr ? &peer_addr : NULL, + plen, lifetime, preferred, 0, + label); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen), + nlo); } static gboolean @@ -5265,22 +5162,35 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint flags) { - return add_object (platform, build_rtnl_addr (platform, AF_INET6, ifindex, &addr, - IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, - plen, lifetime, preferred, flags, - NULL)); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; + + nlo = build_rtnl_addr (platform, AF_INET6, ifindex, &addr, + IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, + plen, lifetime, preferred, flags, + NULL); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen), + nlo); } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { - return delete_object (platform, build_rtnl_addr (platform, AF_INET, ifindex, &addr, peer_address ? &peer_address : NULL, plen, 0, 0, 0, NULL), TRUE); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + obj_needle.ip4_address.peer_address = peer_address; + return do_delete_object (platform, &obj_needle, NULL); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return delete_object (platform, build_rtnl_addr (platform, AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); + return do_delete_object (platform, &obj_needle, NULL); } static gboolean @@ -5295,53 +5205,47 @@ ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer static gboolean ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { - return ip_address_exists (platform, AF_INET, ifindex, &addr, plen); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + return nmp_object_is_visible (nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle)); } static gboolean ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return ip_address_exists (platform, AF_INET6, ifindex, &addr, plen); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); + return nmp_object_is_visible (nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle)); } static gboolean ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPlatformIP4Address addr_candidate; - NMPlatformIP4Route route_candidate; - struct nl_object *object; guint32 device_network; + NMPObject obj_needle; - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - if (_address_match ((struct rtnl_addr *) object, AF_INET, 0)) { - if (init_ip4_address (&addr_candidate, (struct rtnl_addr *) object)) - if ( addr_candidate.plen == address->plen - && addr_candidate.address == address->address) { - /* If we already have the same address installed on any interface, - * we back off. - * Perform this check first, as we expect to have significantly less - * addresses to search. */ - return FALSE; - } - } + if (nmp_cache_lookup_obj (priv->cache, + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, address->address, address->plen))) { + /* If we already have the same address installed on any interface, + * we back off. */ + return FALSE; } device_network = nm_utils_ip4_address_clear_host_address (address->address, address->plen); - for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { - if (_route_match ((struct rtnl_route *) object, AF_INET, 0, TRUE)) { - if (init_ip4_route (&route_candidate, (struct rtnl_route *) object)) { - if ( route_candidate.network == device_network - && route_candidate.plen == address->plen - && ( route_candidate.metric == 0 - || route_candidate.metric == device_route_metric)) { - /* There is already any route with metric 0 or the metric we want to install - * for the same subnet. */ - return FALSE; - } - } - } +check_for_route: + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, device_network, address->plen, device_route_metric); + if (nmp_cache_lookup_obj (priv->cache, &obj_needle)) { + /* There is already a route with metric 0 or the metric we want to install + * for the same subnet. */ + return FALSE; + } + if (device_route_metric != 0) { + device_route_metric = 0; + goto check_for_route; } return TRUE; @@ -5395,59 +5299,13 @@ ipx_route_get_all (NMPlatform *platform, int ifindex, gboolean is_v4, NMPlatform static GArray * ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *routes; - NMPlatformIP4Route route; - struct nl_object *object; - - g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL); - - routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - - for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { - if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex, FALSE)) { - if (_rtnl_route_is_default ((struct rtnl_route *) object)) { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) - continue; - } else { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) - continue; - } - if (init_ip4_route (&route, (struct rtnl_route *) object)) - g_array_append_val (routes, route); - } - } - - return routes; + return ipx_route_get_all (platform, ifindex, TRUE, mode); } static GArray * ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *routes; - NMPlatformIP6Route route; - struct nl_object *object; - - g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL); - - routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - - for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { - if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex, FALSE)) { - if (_rtnl_route_is_default ((struct rtnl_route *) object)) { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) - continue; - } else { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) - continue; - } - if (init_ip6_route (&route, (struct rtnl_route *) object)) - g_array_append_val (routes, route); - } - } - - return routes; + return ipx_route_get_all (platform, ifindex, FALSE, mode); } static void @@ -5550,7 +5408,13 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss) { - return add_object (platform, build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss)); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; + + nlo = build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric), + nlo); } static gboolean @@ -5558,9 +5422,15 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss) { + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; + metric = nm_utils_ip6_route_metric_normalize (metric); - return add_object (platform, build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss)); + nlo = build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric), + nlo); } static struct rtnl_route * @@ -5622,15 +5492,16 @@ refresh_route (NMPlatform *platform, int family, int ifindex, const void *networ static gboolean ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); in_addr_t gateway = 0; - struct rtnl_route *cached_object; - struct nl_object *route = build_rtnl_route (AF_INET, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN, &network, plen, &gateway, NULL, metric, 0); + auto_nl_object struct nl_object *nlo = build_rtnl_route (AF_INET, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN, &network, plen, &gateway, NULL, metric, 0); uint8_t scope = RT_SCOPE_NOWHERE; - struct nl_cache *cache; + const NMPObject *obj; + NMPObject obj_needle; - g_return_val_if_fail (route, FALSE); + g_return_val_if_fail (nlo, FALSE); - cache = choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE); + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); if (metric == 0) { /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. @@ -5639,17 +5510,12 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * * Instead, re-fetch the route from kernel, and if that fails, there is nothing to do. * On success, there is still a race that we might end up deleting the wrong route. */ - if (!refresh_object (platform, (struct nl_object *) route, FALSE, _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)) { - rtnl_route_put ((struct rtnl_route *) route); - return TRUE; - } - } - /* when deleting an IPv4 route, several fields of the provided route must match. - * Lookup in the cache so that we hopefully get the right values. */ - cached_object = (struct rtnl_route *) nl_cache_search (cache, route); - if (!cached_object) - cached_object = route_search_cache (cache, AF_INET, ifindex, &network, plen, metric); + do_refresh_object (platform, &obj_needle, NM_PLATFORM_REASON_INTERNAL, TRUE, &obj); + if (!obj) + return TRUE; + } else + obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) { /* When searching for a matching IPv4 route to delete, the kernel @@ -5666,8 +5532,8 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE. **/ - if (cached_object) - scope = rtnl_route_get_scope (cached_object); + if (obj) + scope = nm_platform_route_scope_inv (obj->ip4_route.scope_inv); if (scope == RT_SCOPE_NOWHERE) { /* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope. @@ -5676,7 +5542,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen scope = RT_SCOPE_UNIVERSE; } } - rtnl_route_set_scope ((struct rtnl_route *) route, scope); + rtnl_route_set_scope ((struct rtnl_route *) nlo, scope); /* we only support routes with TOS zero. As such, delete_route() is also only able to delete * routes with tos==0. build_rtnl_route() already initializes tos properly. */ @@ -5688,19 +5554,23 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * pref_src: NULL */ - rtnl_route_put (cached_object); - return delete_object (platform, route, FALSE) && refresh_route (platform, AF_INET, ifindex, &network, plen, metric); + return do_delete_object (platform, &obj_needle, nlo); } static gboolean ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { struct in6_addr gateway = IN6ADDR_ANY_INIT; + auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_needle; metric = nm_utils_ip6_route_metric_normalize (metric); - return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0), FALSE) && - refresh_route (platform, AF_INET6, ifindex, &network, plen, metric); + nlo = build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0); + + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); + + return do_delete_object (platform, &obj_needle, nlo); } static gboolean @@ -5720,15 +5590,21 @@ ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network static gboolean ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { - return ip_route_exists (platform, AF_INET, ifindex, &network, plen, metric); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); + return nmp_object_is_visible (nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle)); } static gboolean ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { + NMPObject obj_needle; + metric = nm_utils_ip6_route_metric_normalize (metric); - return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, metric); + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); + return nmp_object_is_visible (nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle)); } /******************************************************************/ @@ -5918,7 +5794,8 @@ event_handler (GIOChannel *channel, nle = -NLE_AGAIN; } while (nle != -NLE_AGAIN); nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data); - cache_repopulate_all (platform); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); + delayed_action_handle_all (platform); break; default: error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); @@ -5978,8 +5855,6 @@ static void udev_device_added (NMPlatform *platform, GUdevDevice *udev_device) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; const char *ifname; int ifindex; @@ -6005,49 +5880,37 @@ udev_device_added (NMPlatform *platform, return; } - g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex), - g_object_ref (udev_device)); + cache_update_link_udev (platform, ifindex, udev_device); +} - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (!rtnllink) { - debug ("(%s): udev-add: interface not known via netlink; ignoring ifindex %d...", ifname, ifindex); - return; - } - - announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL); +static gboolean +_udev_device_removed_match_link (const NMPObject *obj, gpointer udev_device) +{ + return obj->_link.udev.device == udev_device; } static void udev_device_removed (NMPlatform *platform, GUdevDevice *udev_device) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int ifindex = 0; if (g_udev_device_get_property (udev_device, "IFINDEX")) ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); else { - GHashTableIter iter; - gpointer key, value; + const NMPObject *obj; - /* This should not happen, but just to be sure. - * If we can't get IFINDEX, go through the devices and - * compare the pointers. - */ - g_hash_table_iter_init (&iter, priv->udev_devices); - while (g_hash_table_iter_next (&iter, &key, &value)) { - if ((GUdevDevice *)value == udev_device) { - ifindex = GPOINTER_TO_INT (key); - break; - } - } + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udev_device); + if (obj) + ifindex = obj->link.ifindex; } debug ("udev-remove: IFINDEX=%d", ifindex); if (ifindex <= 0) return; - g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); + cache_update_link_udev (platform, ifindex, NULL); } static void @@ -6136,21 +5999,9 @@ constructed (GObject *_object) (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), event_handler, platform); - cache_repopulate_all (platform); - -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (G_UNLIKELY (_support_user_ipv6ll == 0)) { - struct nl_object *object; - - /* Initial check for user IPv6LL support once the link cache is allocated - * and filled. If there are no links in the cache yet then we'll check - * when a new link shows up in announce_object(). - */ - object = nl_cache_get_first (priv->link_cache); - if (object) - _support_user_ipv6ll_detect ((struct rtnl_link *) object); - } -#endif + _LOGD ("populate platform cache"); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); + delayed_action_handle_all (platform); /* Set up udev monitoring */ priv->udev_client = g_udev_client_new (udev_subsys); diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index c3bc0dcb2b..37a7c3bc58 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -75,7 +75,7 @@ test_ip4_address (void) /* Add address again (aka update) */ g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); no_error (); - accept_signal (address_changed); + accept_signals (address_changed, 0, 1); /* Test address listing */ addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); @@ -131,7 +131,7 @@ test_ip6_address (void) /* Add address again (aka update) */ g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); no_error (); - accept_signal (address_changed); + accept_signals (address_changed, 0, 1); /* Test address listing */ addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 102fe795f7..f2b82ae8c1 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -120,10 +120,15 @@ software_add (NMLinkType link_type, const char *name) { int parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + gboolean was_up = nm_platform_link_is_up (NM_PLATFORM_GET, parent_ifindex); parent_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex); g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, parent_ifindex)); - accept_signal (parent_changed); + if (was_up) { + /* when NM is running in the background, it will mess with addrgenmode which might cause additional signals. */ + accept_signals (parent_changed, 0, 1); + } else + accept_signal (parent_changed); free_signal (parent_changed); return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL); @@ -142,6 +147,9 @@ test_slave (int master, int type, SignalData *master_changed) SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, SLAVE_NAME); SignalData *link_changed, *link_removed; char *value; + NMLinkType link_type = nm_platform_link_get_type (NM_PLATFORM_GET, master); + + g_assert (NM_IN_SET (link_type, NM_LINK_TYPE_TEAM, NM_LINK_TYPE_BOND, NM_LINK_TYPE_BRIDGE)); g_assert (software_add (type, SLAVE_NAME)); ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME); @@ -154,19 +162,28 @@ test_slave (int master, int type, SignalData *master_changed) * * See https://bugzilla.redhat.com/show_bug.cgi?id=910348 */ + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); - accept_signal (link_changed); + ensure_no_signal (link_changed); /* Enslave */ link_changed->ifindex = ifindex; g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, master); no_error (); + accept_signal (link_changed); - accept_signal (master_changed); + accept_signals (master_changed, 0, 1); + + /* enslaveing brings put the slave */ + if (NM_IN_SET (link_type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + else + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); /* Set master up */ g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, master)); + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, master)); accept_signal (master_changed); /* Master with a disconnected slave is disconnected @@ -179,7 +196,7 @@ test_slave (int master, int type, SignalData *master_changed) case NM_LINK_TYPE_TEAM: g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); - accept_signal (master_changed); + accept_signals (master_changed, 0, 2); break; default: break; @@ -206,15 +223,16 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signal (link_changed); - accept_signal (master_changed); + /* NM running, can cause additional change of addrgenmode */ + accept_signals (master_changed, 1, 2); /* Enslave again * * Gracefully succeed if already enslaved. */ g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); - accept_signal (link_changed); - accept_signal (master_changed); + ensure_no_signal (link_changed); + ensure_no_signal (master_changed); /* Set slave option */ switch (type) { @@ -236,7 +254,10 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0); no_error (); accept_signal (link_changed); - accept_signal (master_changed); + if (link_type != NM_LINK_TYPE_TEAM) + accept_signals (master_changed, 1, 2); + else + accept_signals (master_changed, 1, 1); /* Release again */ g_assert (!nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); @@ -539,6 +560,7 @@ test_external (void) run_command ("ip link del %s", DEVICE_NAME); wait_signal (link_removed); + accept_signals (link_changed, 0, 1); g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); free_signal (link_added); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index fd86719958..90406f8ed7 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -153,7 +153,7 @@ test_ip4_route (void) /* Add route again */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); no_error (); - accept_signal (route_changed); + accept_signals (route_changed, 0, 1); /* Add default route */ assert_ip4_route_exists (FALSE, DEVICE_NAME, 0, 0, metric); @@ -167,7 +167,7 @@ test_ip4_route (void) /* Add default route again */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); no_error (); - accept_signal (route_changed); + accept_signals (route_changed, 0, 1); /* Test route listing */ routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); @@ -251,7 +251,7 @@ test_ip6_route (void) /* Add route again */ g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); no_error (); - accept_signal (route_changed); + accept_signals (route_changed, 0, 1); /* Add default route */ g_assert (!nm_platform_ip6_route_exists (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); @@ -265,7 +265,7 @@ test_ip6_route (void) /* Add default route again */ g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); no_error (); - accept_signal (route_changed); + accept_signals (route_changed, 0, 1); /* Test route listing */ routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index 5b1c5f9d5a..9b5f187232 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -250,7 +250,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) }; setup_dev0_ip4 (fixture->ifindex0, 1000, 21021); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 8.0.0.0/8 via 6.6.6.2 dev *"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure adding ip4-route '*: 8.0.0.0/8 22': *"); setup_dev1_ip4 (fixture->ifindex1); g_test_assert_expected_messages (); @@ -262,7 +262,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 8.0.0.0/8 via 6.6.6.2 dev *"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure adding ip4-route '*: 8.0.0.0/8 22': *"); setup_dev1_ip4 (fixture->ifindex1); g_test_assert_expected_messages (); @@ -584,7 +584,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) }; setup_dev0_ip6 (fixture->ifindex0); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 2001:db8:d34d::/64 via 2001:db8:8086::2 dev *"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure adding ip6-route '*: 2001:db8:d34d::/64 20': *"); setup_dev1_ip6 (fixture->ifindex1); g_test_assert_expected_messages (); @@ -598,7 +598,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) g_array_free (routes, TRUE); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 2001:db8:d34d::/64 via 2001:db8:8086::2 dev *"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure adding ip6-route '*: 2001:db8:d34d::/64 20': *"); setup_dev1_ip6 (fixture->ifindex1); g_test_assert_expected_messages (); setup_dev0_ip6 (fixture->ifindex0); From 2f0d0b96db71c68d4c23880c89323ea8868bb63f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 25 Apr 2015 14:53:39 +0200 Subject: [PATCH 22/50] platform: remove obsolete functions after refactoring platform cache --- src/platform/nm-linux-platform.c | 1426 ------------------------------ 1 file changed, 1426 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index c077d9cc03..081dfaa85b 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -109,9 +109,6 @@ #define warning(...) _LOG (LOGL_WARN , _LOG_DOMAIN, NULL, __VA_ARGS__) #define error(...) _LOG (LOGL_ERR , _LOG_DOMAIN, NULL, __VA_ARGS__) -NM_PRAGMA_WARNING_DISABLE ("-Wunused") -NM_PRAGMA_WARNING_DISABLE ("-Wunused-function") - /****************************************************************** * Forward declarations and enums ******************************************************************/ @@ -176,18 +173,6 @@ _nl_has_capability (int capability) } /* Automatic deallocation of local variables */ -#define auto_nl_cache __attribute__((cleanup(put_nl_cache))) -static void -put_nl_cache (void *ptr) -{ - struct nl_cache **cache = ptr; - - if (cache && *cache) { - nl_cache_free (*cache); - *cache = NULL; - } -} - #define auto_nl_object __attribute__((cleanup(put_nl_object))) static void put_nl_object (void *ptr) @@ -292,53 +277,6 @@ nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) /******************************************************************/ -static guint32 -_get_expiry (guint32 now_s, guint32 lifetime_s) -{ - gint64 t = ((gint64) now_s) + ((gint64) lifetime_s); - - return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1); -} - -/* The rtnl_addr object contains relative lifetimes @valid and @preferred - * that count in seconds, starting from the moment when the kernel constructed - * the netlink message. - * - * There is also a field rtnl_addr_last_update_time(), which is the absolute - * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address - * was modified (wrapping every 497 days). - * Immediately at the time when the address was last modified, #NOW and @last_update_time - * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. - * However, this is not true in general. As time goes by, whenever kernel sends a new address - * via netlink, the lifetimes keep counting down. - * - * As we cache the rtnl_addr object we must know the absolute expiries. - * As a hack, modify the relative timestamps valid and preferred into absolute - * timestamps of scale nm_utils_get_monotonic_timestamp_s(). - **/ -static void -_rtnl_addr_hack_lifetimes_rel_to_abs (struct rtnl_addr *rtnladdr) -{ - guint32 a_valid = rtnl_addr_get_valid_lifetime (rtnladdr); - guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr); - guint32 now; - - if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && - a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) - return; - - now = (guint32) nm_utils_get_monotonic_timestamp_s (); - - if (a_preferred > a_valid) - a_preferred = a_valid; - - if (a_valid != NM_PLATFORM_LIFETIME_PERMANENT) - rtnl_addr_set_valid_lifetime (rtnladdr, _get_expiry (now, a_valid)); - rtnl_addr_set_preferred_lifetime (rtnladdr, _get_expiry (now, a_preferred)); -} - -/******************************************************************/ - #if HAVE_LIBNL_INET6_ADDR_GEN_MODE static int _support_user_ipv6ll = 0; #endif @@ -431,15 +369,11 @@ typedef struct { typedef struct { struct nl_sock *nlh; struct nl_sock *nlh_event; - struct nl_cache *link_cache; - struct nl_cache *address_cache; - struct nl_cache *route_cache; NMPCache *cache; GIOChannel *event_channel; guint event_id; GUdevClient *udev_client; - GHashTable *udev_devices; struct { GArray *list; @@ -454,10 +388,6 @@ typedef struct { G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) -static const char *to_string_object (NMPlatform *platform, struct nl_object *obj); -static gboolean _address_match (struct rtnl_addr *addr, int family, int ifindex); -static gboolean _route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel); - void nm_linux_platform_setup (void) { @@ -498,125 +428,6 @@ _nlo_get_object_type (const struct nl_object *object) return OBJECT_TYPE_UNKNOWN; } -static void -_nl_link_family_unset (struct nl_object *obj, int *family) -{ - if (!obj || _nlo_get_object_type (obj) != OBJECT_TYPE_LINK) - *family = AF_UNSPEC; - else { - *family = rtnl_link_get_family ((struct rtnl_link *) obj); - - /* Always explicitly set the family to AF_UNSPEC, even if rtnl_link_get_family() might - * already return %AF_UNSPEC. The reason is, that %AF_UNSPEC is the default family - * and libnl nl_object_identical() function will only succeed, if the family is - * explicitly set (which we cannot be sure, unless setting it). */ - rtnl_link_set_family ((struct rtnl_link *) obj, AF_UNSPEC); - } -} - -/* In our link cache, we coerce the family of all link objects to AF_UNSPEC. - * Thus, before searching for an object, we fixup @needle to have the right - * id (by resetting the family). */ -static struct nl_object * -nm_nl_cache_search (struct nl_cache *cache, struct nl_object *needle) -{ - int family; - struct nl_object *obj; - - _nl_link_family_unset (needle, &family); - obj = nl_cache_search (cache, needle); - if (family != AF_UNSPEC) { - /* restore the family of the @needle instance. If the family was - * unset before, we cannot make it unset again. Thus, in that case - * we cannot undo _nl_link_family_unset() entirely. */ - rtnl_link_set_family ((struct rtnl_link *) needle, family); - } - - return obj; -} - -/* Ask the kernel for an object identical (as in nl_cache_identical) to the - * needle argument. This is a kernel counterpart for nl_cache_search. - * - * The returned object must be freed by the caller with nl_object_put(). - */ -static struct nl_object * -get_kernel_object (struct nl_sock *sock, struct nl_object *needle) -{ - struct nl_object *object = NULL; - ObjectType type = _nlo_get_object_type (needle); - - switch (type) { - case OBJECT_TYPE_LINK: - { - int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) needle); - const char *name = rtnl_link_get_name ((struct rtnl_link *) needle); - int nle; - - nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &object); - switch (nle) { - case -NLE_SUCCESS: - _support_user_ipv6ll_detect ((struct rtnl_link *) object); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - name = rtnl_link_get_name ((struct rtnl_link *) object); - debug ("get_kernel_object for link: %s (%d, family %d)", - name ? name : "(unknown)", - rtnl_link_get_ifindex ((struct rtnl_link *) object), - rtnl_link_get_family ((struct rtnl_link *) object)); - } - - _nl_link_family_unset (object, &nle); - return object; - case -NLE_NODEV: - debug ("get_kernel_object for link %s (%d) had no result", - name ? name : "(unknown)", ifindex); - return NULL; - default: - error ("get_kernel_object for link %s (%d) failed: %s (%d)", - name ? name : "(unknown)", ifindex, nl_geterror (nle), nle); - return NULL; - } - } - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - /* Fallback to a one-time cache allocation. */ - { - struct nl_cache *cache; - int nle; - - /* FIXME: every time we refresh *one* object, we request an - * entire dump. E.g. check_cache_items() gets O(n2) complexitly. */ - - nle = nl_cache_alloc_and_fill ( - nl_cache_ops_lookup (nl_object_get_type (needle)), - sock, &cache); - if (nle) { - error ("get_kernel_object for type %d failed: %s (%d)", - type, nl_geterror (nle), nle); - return NULL; - } - - object = nl_cache_search (cache, needle); - - nl_cache_free (cache); - - if (object && (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS)) - _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); - - if (object) - debug ("get_kernel_object for type %d returned %p", type, object); - else - debug ("get_kernel_object for type %d had no result", type); - return object; - } - default: - g_return_val_if_reached (NULL); - return NULL; - } -} - static NMPObject * kernel_get_object (NMPlatform *platform, struct nl_sock *sock, const NMPObject *needle) { @@ -686,25 +497,6 @@ kernel_get_object (NMPlatform *platform, struct nl_sock *sock, const NMPObject * } } -/* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */ -static int -add_kernel_object (struct nl_sock *sock, struct nl_object *object) -{ - switch (_nlo_get_object_type (object)) { - case OBJECT_TYPE_LINK: - return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE); - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE | NLM_F_REPLACE); - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE | NLM_F_REPLACE); - default: - g_return_val_if_reached (-NLE_INVAL); - return -NLE_INVAL; - } -} - /* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel * and parses its IFLA_INFO_DATA using a caller-provided parser. * @@ -1027,108 +819,6 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean co return NM_LINK_TYPE_UNKNOWN; } -static gboolean -init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllink) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GUdevDevice *udev_device; - const char *name; - char *tmp; - struct nl_addr *nladdr; - - g_return_val_if_fail (rtnllink, FALSE); - - name = rtnl_link_get_name (rtnllink); - memset (info, 0, sizeof (*info)); - - info->ifindex = rtnl_link_get_ifindex (rtnllink); - if (name) - g_strlcpy (info->name, name, sizeof (info->name)); - else - info->name[0] = '\0'; - info->type = link_extract_type (platform, rtnllink, FALSE, NULL); - info->kind = g_intern_string (rtnl_link_get_type (rtnllink)); - info->flags = rtnl_link_get_flags (rtnllink); - info->up = NM_FLAGS_HAS (info->flags, IFF_UP); - info->connected = NM_FLAGS_HAS (info->flags, IFF_LOWER_UP); - info->arp = !NM_FLAGS_HAS (info->flags, IFF_NOARP); - info->master = rtnl_link_get_master (rtnllink); - info->parent = rtnl_link_get_link (rtnllink); - info->mtu = rtnl_link_get_mtu (rtnllink); - info->arptype = rtnl_link_get_arptype (rtnllink); - - if (info->type == NM_LINK_TYPE_VLAN) - info->vlan_id = rtnl_link_vlan_get_id (rtnllink); - - if ((nladdr = rtnl_link_get_addr (rtnllink))) { - unsigned int l = 0; - - l = nl_addr_get_len (nladdr); - if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { - G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (info->addr.data)); - memcpy (info->addr.data, nl_addr_get_binary_addr (nladdr), l); - info->addr.len = l; - } - } - -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (_support_user_ipv6ll_get ()) { - guint8 mode = 0; - - if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) == 0) - info->inet6_addr_gen_mode_inv = ~mode; - } -#endif - -#if HAVE_LIBNL_INET6_TOKEN - if ((rtnl_link_inet6_get_token (rtnllink, &nladdr)) == 0) { - if ( nl_addr_get_family (nladdr) == AF_INET6 - && nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) { - struct in6_addr *addr; - NMUtilsIPv6IfaceId *iid = &info->inet6_token.iid; - - addr = nl_addr_get_binary_addr (nladdr); - iid->id_u8[7] = addr->s6_addr[15]; - iid->id_u8[6] = addr->s6_addr[14]; - iid->id_u8[5] = addr->s6_addr[13]; - iid->id_u8[4] = addr->s6_addr[12]; - iid->id_u8[3] = addr->s6_addr[11]; - iid->id_u8[2] = addr->s6_addr[10]; - iid->id_u8[1] = addr->s6_addr[9]; - iid->id_u8[0] = addr->s6_addr[8]; - info->inet6_token.is_valid = TRUE; - } - nl_addr_put (nladdr); - } -#endif - - udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); - if (udev_device) { - info->driver = nmp_utils_udev_get_driver (udev_device); - info->udi = g_udev_device_get_sysfs_path (udev_device); - info->initialized = TRUE; - } - - if (!info->driver) - info->driver = info->kind; - if (!info->driver) { - if (nmp_utils_ethtool_get_driver_info (name, &tmp, NULL, NULL)) { - info->driver = g_intern_string (tmp); - g_free (tmp); - } - } - if (!info->driver) - info->driver = "unknown"; - - /* Only demand further initialization (udev rules ran, device has - * a stable name now) in case udev is running (not in a container). */ - if ( !info->initialized - && access ("/sys", W_OK) != 0) - info->initialized = TRUE; - - return TRUE; -} - gboolean _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { @@ -1210,67 +900,6 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob return TRUE; } -/* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break - * the carrier detection. This hack makes nm-platform think they don't have the - * IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all - * slaves down. - * - * Note: This is still a bit racy but when NetworkManager asks for enslaving a slave, - * nm-platform will do that synchronously and will immediately ask for both master - * and slave information after the enslaving request. After the synchronous call, the - * master carrier is already updated with the slave carrier in mind. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=910348 - */ -static void -hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct rtnl_link *rtnllink; - int ifindex; - struct nl_object *slave; - const char *type; - - if (!object) - return; - if (strcmp (nl_object_get_type (object), "route/link")) - return; - - rtnllink = (struct rtnl_link *) object; - - ifindex = rtnl_link_get_ifindex (rtnllink); - - type = rtnl_link_get_type (rtnllink); - if (!type || (strcmp (type, "bridge") != 0 && strcmp (type, "bond") != 0)) - return; - - for (slave = nl_cache_get_first (priv->link_cache); slave; slave = nl_cache_get_next (slave)) { - struct rtnl_link *rtnlslave = (struct rtnl_link *) slave; - if (rtnl_link_get_master (rtnlslave) == ifindex - && rtnl_link_get_flags (rtnlslave) & IFF_LOWER_UP) - return; - } - - rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP); -} - -static guint32 -_get_remaining_time (guint32 start_timestamp, guint32 end_timestamp) -{ - /* Return the remaining time between @start_timestamp until @end_timestamp. - * - * If @end_timestamp is NM_PLATFORM_LIFETIME_PERMANENT, it returns - * NM_PLATFORM_LIFETIME_PERMANENT. If @start_timestamp already passed - * @end_timestamp it returns 0. Beware, NMPlatformIPAddress treats a @lifetime - * of 0 as permanent. - */ - if (end_timestamp == NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT; - if (start_timestamp >= end_timestamp) - return 0; - return end_timestamp - start_timestamp; -} - /* _timestamp_nl_to_ms: * @timestamp_nl: a timestamp from ifa_cacheinfo. * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current @@ -1346,53 +975,6 @@ _rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr, gint32 *out return result / 1000; } -static void -_init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr) -{ - guint32 a_valid = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr); - guint32 a_preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr); - - /* the meaning of the valid and preferred lifetimes is different from the - * original meaning. See _rtnl_addr_hack_lifetimes_rel_to_abs(). - * Beware: this function expects hacked rtnl_addr objects. - */ - - if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && - a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) { - address->timestamp = 0; - address->lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - address->preferred = NM_PLATFORM_LIFETIME_PERMANENT; - return; - } - - /* The valies are hacked and absolute expiry times. They must - * be positive and preferred<=valid. */ - g_assert (a_preferred <= a_valid && - a_valid > 0 && - a_preferred > 0); - - if (a_valid <= 1) { - /* Since we want to have positive @timestamp and @valid != 0, - * we must handle this case special. */ - address->timestamp = 1; - address->lifetime = 1; /* Extend the lifetime by one second */ - address->preferred = 0; /* no longer preferred. */ - return; - } - - /* _rtnl_addr_last_update_time_to_nm() might be wrong, so don't rely on - * timestamp to have any meaning beyond anchoring the relative durations - * @lifetime and @preferred. - */ - address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr, NULL); - - /* We would expect @timestamp to be less then @a_valid. Just to be sure, - * fix it up. */ - address->timestamp = MIN (address->timestamp, a_valid - 1); - address->lifetime = _get_remaining_time (address->timestamp, a_valid); - address->preferred = _get_remaining_time (address->timestamp, a_preferred); -} - static guint32 _extend_lifetime (guint32 lifetime, guint32 seconds) { @@ -1453,41 +1035,6 @@ _nlo_rtnl_addr_get_lifetimes (const struct rtnl_addr *rtnladdr, *out_preferred = preferred; } -static gboolean -init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) -{ - struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); - struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr); - const char *label; - - g_return_val_if_fail (nladdr, FALSE); - - memset (address, 0, sizeof (*address)); - - address->source = NM_IP_CONFIG_SOURCE_KERNEL; - address->ifindex = rtnl_addr_get_ifindex (rtnladdr); - address->plen = rtnl_addr_get_prefixlen (rtnladdr); - _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address)); - } - label = rtnl_addr_get_label (rtnladdr); - /* Check for ':'; we're only interested in labels used as interface aliases */ - if (label && strchr (label, ':')) - g_strlcpy (address->label, label, sizeof (address->label)); - - return TRUE; -} - gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { @@ -1526,35 +1073,6 @@ _nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObje return TRUE; } -static gboolean -init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) -{ - struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); - struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr); - - memset (address, 0, sizeof (*address)); - - address->source = NM_IP_CONFIG_SOURCE_KERNEL; - address->ifindex = rtnl_addr_get_ifindex (rtnladdr); - address->plen = rtnl_addr_get_prefixlen (rtnladdr); - _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); - address->flags = rtnl_addr_get_flags (rtnladdr); - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address)); - } - - return TRUE; -} - gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { @@ -1631,57 +1149,6 @@ rtprot_to_source (guint rtprot, gboolean preserve_rtprot) } } -static gboolean -_rtnl_route_is_default (const struct rtnl_route *rtnlroute) -{ - struct nl_addr *dst; - - return rtnlroute - && (dst = rtnl_route_get_dst ((struct rtnl_route *) rtnlroute)) - && nl_addr_get_prefixlen (dst) == 0; -} - -static gboolean -init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute) -{ - struct nl_addr *dst, *gw; - struct rtnl_nexthop *nexthop; - - memset (route, 0, sizeof (*route)); - - /* Multi-hop routes not supported. */ - if (rtnl_route_get_nnexthops (rtnlroute) != 1) - return FALSE; - - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - dst = rtnl_route_get_dst (rtnlroute); - gw = rtnl_route_nh_get_gateway (nexthop); - - route->ifindex = rtnl_route_nh_get_ifindex (nexthop); - route->plen = nl_addr_get_prefixlen (dst); - /* Workaround on previous workaround for libnl default route prefixlen bug. */ - if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (route->network)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network)); - } - if (gw) { - if (nl_addr_get_len (gw) != sizeof (route->network)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway)); - } - route->metric = rtnl_route_get_priority (rtnlroute); - rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); - route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute), FALSE); - route->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (rtnlroute)); - - return TRUE; -} - gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { @@ -1737,46 +1204,6 @@ _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject return TRUE; } -static gboolean -init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) -{ - struct nl_addr *dst, *gw; - struct rtnl_nexthop *nexthop; - - memset (route, 0, sizeof (*route)); - - /* Multi-hop routes not supported. */ - if (rtnl_route_get_nnexthops (rtnlroute) != 1) - return FALSE; - - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - dst = rtnl_route_get_dst (rtnlroute); - gw = rtnl_route_nh_get_gateway (nexthop); - - route->ifindex = rtnl_route_nh_get_ifindex (nexthop); - route->plen = nl_addr_get_prefixlen (dst); - /* Workaround on previous workaround for libnl default route prefixlen bug. */ - if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (route->network)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network)); - } - if (gw) { - if (nl_addr_get_len (gw) != sizeof (route->network)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway)); - } - route->metric = rtnl_route_get_priority (rtnlroute); - rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); - route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute), FALSE); - - return TRUE; -} - gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { @@ -1827,289 +1254,8 @@ _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject return TRUE; } -static char to_string_buffer[255]; - -#define SET_AND_RETURN_STRING_BUFFER(...) \ - G_STMT_START { \ - g_snprintf (to_string_buffer, sizeof (to_string_buffer), ## __VA_ARGS__); \ - return to_string_buffer; \ - } G_STMT_END - -static const char * -to_string_link (NMPlatform *platform, struct rtnl_link *obj) -{ - NMPlatformLink pl_obj; - - if (init_link (platform, &pl_obj, obj)) - return nm_platform_link_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid link %p)", obj); -} - -static const char * -to_string_ip4_address (struct rtnl_addr *obj) -{ - NMPlatformIP4Address pl_obj; - - if (init_ip4_address (&pl_obj, obj)) - return nm_platform_ip4_address_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 address %p)", obj); -} - -static const char * -to_string_ip6_address (struct rtnl_addr *obj) -{ - NMPlatformIP6Address pl_obj; - - if (init_ip6_address (&pl_obj, obj)) - return nm_platform_ip6_address_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 address %p)", obj); -} - -static const char * -to_string_ip4_route (struct rtnl_route *obj) -{ - NMPlatformIP4Route pl_obj; - - if (init_ip4_route (&pl_obj, obj)) - return nm_platform_ip4_route_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 route %p)", obj); -} - -static const char * -to_string_ip6_route (struct rtnl_route *obj) -{ - NMPlatformIP6Route pl_obj; - - if (init_ip6_route (&pl_obj, obj)) - return nm_platform_ip6_route_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 route %p)", obj); -} - -static const char * -to_string_object_with_type (NMPlatform *platform, struct nl_object *obj, ObjectType type) -{ - switch (type) { - case OBJECT_TYPE_LINK: - return to_string_link (platform, (struct rtnl_link *) obj); - case OBJECT_TYPE_IP4_ADDRESS: - return to_string_ip4_address ((struct rtnl_addr *) obj); - case OBJECT_TYPE_IP6_ADDRESS: - return to_string_ip6_address ((struct rtnl_addr *) obj); - case OBJECT_TYPE_IP4_ROUTE: - return to_string_ip4_route ((struct rtnl_route *) obj); - case OBJECT_TYPE_IP6_ROUTE: - return to_string_ip6_route ((struct rtnl_route *) obj); - default: - SET_AND_RETURN_STRING_BUFFER ("(unknown netlink object %p)", obj); - } -} - -static const char * -to_string_object (NMPlatform *platform, struct nl_object *obj) -{ - return to_string_object_with_type (platform, obj, _nlo_get_object_type (obj)); -} - -#undef SET_AND_RETURN_STRING_BUFFER - /******************************************************************/ -/* Object and cache manipulation */ - -static const char *signal_by_type_and_status[OBJECT_TYPE_MAX + 1] = { - [OBJECT_TYPE_LINK] = NM_PLATFORM_SIGNAL_LINK_CHANGED, - [OBJECT_TYPE_IP4_ADDRESS] = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - [OBJECT_TYPE_IP6_ADDRESS] = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - [OBJECT_TYPE_IP4_ROUTE] = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - [OBJECT_TYPE_IP6_ROUTE] = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, -}; - -static struct nl_cache * -choose_cache_by_type (NMPlatform *platform, ObjectType object_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - switch (object_type) { - case OBJECT_TYPE_LINK: - return priv->link_cache; - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return priv->address_cache; - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - return priv->route_cache; - default: - g_return_val_if_reached (NULL); - return NULL; - } -} - -static struct nl_cache * -choose_cache (NMPlatform *platform, struct nl_object *object) -{ - return choose_cache_by_type (platform, _nlo_get_object_type (object)); -} - -static gboolean -object_has_ifindex (struct nl_object *object, int ifindex) -{ - switch (_nlo_get_object_type (object)) { - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return ifindex == rtnl_addr_get_ifindex ((struct rtnl_addr *) object); - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - { - struct rtnl_route *rtnlroute = (struct rtnl_route *) object; - struct rtnl_nexthop *nexthop; - - if (rtnl_route_get_nnexthops (rtnlroute) != 1) - return FALSE; - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - - return ifindex == rtnl_route_nh_get_ifindex (nexthop); - } - default: - g_assert_not_reached (); - } -} - -static gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason); - -static void -check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex) -{ - auto_nl_cache struct nl_cache *cloned_cache = nl_cache_clone (cache); - struct nl_object *object; - GPtrArray *objects_to_refresh = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put); - guint i; - - for (object = nl_cache_get_first (cloned_cache); object; object = nl_cache_get_next (object)) { - if (object_has_ifindex (object, ifindex)) { - nl_object_get (object); - g_ptr_array_add (objects_to_refresh, object); - } - } - - for (i = 0; i < objects_to_refresh->len; i++) - refresh_object (platform, objects_to_refresh->pdata[i], TRUE, NM_PLATFORM_REASON_CACHE_CHECK); - - g_ptr_array_free (objects_to_refresh, TRUE); -} - -static void -announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatformSignalChangeType change_type, NMPlatformReason reason) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - ObjectType object_type = _nlo_get_object_type (object); - const char *sig = signal_by_type_and_status[object_type]; - - switch (object_type) { - case OBJECT_TYPE_LINK: - { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; - NMPlatformLink device; - - if (!init_link (platform, &device, rtnl_link)) - return; - - /* Link deletion or setting down is sometimes accompanied by address - * and/or route deletion. - * - * More precisely, kernel removes routes when interface goes !IFF_UP and - * removes both addresses and routes when interface is removed. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_CHANGED: - if (!device.connected) - check_cache_items (platform, priv->route_cache, device.ifindex); - break; - case NM_PLATFORM_SIGNAL_REMOVED: - check_cache_items (platform, priv->address_cache, device.ifindex); - check_cache_items (platform, priv->route_cache, device.ifindex); - g_hash_table_remove (priv->wifi_data, GINT_TO_POINTER (device.ifindex)); - break; - default: - break; - } - - g_signal_emit_by_name (platform, sig, device.ifindex, &device, change_type, reason); - } - return; - case OBJECT_TYPE_IP4_ADDRESS: - { - NMPlatformIP4Address address; - - /* Address deletion is sometimes accompanied by route deletion. We need to - * check all routes belonging to the same interface. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_REMOVED: - check_cache_items (platform, - priv->route_cache, - rtnl_addr_get_ifindex ((struct rtnl_addr *) object)); - break; - default: - break; - } - - if (!_address_match ((struct rtnl_addr *) object, AF_INET, 0)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 address %s", to_string_ip4_address ((struct rtnl_addr *) object)); - return; - } - if (!init_ip4_address (&address, (struct rtnl_addr *) object)) - return; - g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason); - } - return; - case OBJECT_TYPE_IP6_ADDRESS: - { - NMPlatformIP6Address address; - - if (!_address_match ((struct rtnl_addr *) object, AF_INET6, 0)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 address %s", to_string_ip6_address ((struct rtnl_addr *) object)); - return; - } - if (!init_ip6_address (&address, (struct rtnl_addr *) object)) - return; - g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason); - } - return; - case OBJECT_TYPE_IP4_ROUTE: - { - NMPlatformIP4Route route; - - if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL) - return; - - if (!_route_match ((struct rtnl_route *) object, AF_INET, 0, FALSE)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 route %s", to_string_ip4_route ((struct rtnl_route *) object)); - return; - } - if (init_ip4_route (&route, (struct rtnl_route *) object)) - g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason); - } - return; - case OBJECT_TYPE_IP6_ROUTE: - { - NMPlatformIP6Route route; - - if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL) - return; - - if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0, FALSE)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 route %s", to_string_ip6_route ((struct rtnl_route *) object)); - return; - } - if (init_ip6_route (&route, (struct rtnl_route *) object)) - g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason); - } - return; - default: - g_return_if_reached (); - } -} - static void do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason) { @@ -2571,83 +1717,6 @@ cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_ /******************************************************************/ -static struct nl_object * build_rtnl_link (int ifindex, const char *name, NMLinkType type); - -static gboolean -refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct nl_object *cached_object = NULL; - auto_nl_object struct nl_object *kernel_object = NULL; - struct nl_cache *cache; - int nle; - - cache = choose_cache (platform, object); - cached_object = nm_nl_cache_search (cache, object); - kernel_object = get_kernel_object (priv->nlh, object); - - if (removed) { - if (kernel_object) - return TRUE; - - /* Only announce object if it was still in the cache. */ - if (cached_object) { - nl_cache_remove (cached_object); - - announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, reason); - } - } else { - ObjectType type; - - if (!kernel_object) - return FALSE; - - /* Unsupported object types should never have reached the caches */ - type = _nlo_get_object_type (kernel_object); - g_assert (type != OBJECT_TYPE_UNKNOWN); - - hack_empty_master_iff_lower_up (platform, kernel_object); - - if (cached_object) - nl_cache_remove (cached_object); - nle = nl_cache_add (cache, kernel_object); - if (nle) { - nm_log_dbg (LOGD_PLATFORM, "refresh_object(reason %d) failed during nl_cache_add with %d", reason, nle); - return FALSE; - } - - announce_object (platform, kernel_object, cached_object ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, reason); - - if (type == OBJECT_TYPE_LINK) { - int kernel_master = rtnl_link_get_master ((struct rtnl_link *) kernel_object); - int cached_master = cached_object ? rtnl_link_get_master ((struct rtnl_link *) cached_object) : 0; - const char *orig_link_type = rtnl_link_get_type ((struct rtnl_link *) object); - const char *kernel_link_type = rtnl_link_get_type ((struct rtnl_link *) kernel_object); - struct nl_object *master_object; - - /* Refresh the master device (even on enslave/release) */ - if (kernel_master) { - master_object = build_rtnl_link (kernel_master, NULL, NM_LINK_TYPE_NONE); - refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL); - nl_object_put (master_object); - } - if (cached_master && cached_master != kernel_master) { - master_object = build_rtnl_link (cached_master, NULL, NM_LINK_TYPE_NONE); - refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL); - nl_object_put (master_object); - } - - /* Ensure the existing link type matches the refreshed link type */ - if (orig_link_type && kernel_link_type && strcmp (orig_link_type, kernel_link_type)) { - platform->error = NM_PLATFORM_ERROR_WRONG_TYPE; - return FALSE; - } - } - } - - return TRUE; -} - static void do_refresh_object (NMPlatform *platform, const NMPObject *obj_needle, NMPlatformReason reason, gboolean handle_all_delayed_actions, const NMPObject **out_obj) { @@ -2833,46 +1902,6 @@ do_refresh_all (NMPlatform *platform, ObjectType obj_type, int ifindex, NMPlatfo cache_remove_netlink (platform, prune_list->pdata[i], NULL, NULL, reason); } -/* Decreases the reference count if @obj for convenience */ -static gboolean -add_object (NMPlatform *platform, struct nl_object *obj) -{ - auto_nl_object struct nl_object *object = obj; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; - - g_return_val_if_fail (object, FALSE); - - nle = add_kernel_object (priv->nlh, object); - - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. - */ - switch (nle) { - case -NLE_SUCCESS: - case -NLE_EXIST: - break; - default: - error ("Netlink error adding %s: %s", to_string_object (platform, object), nl_geterror (nle)); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - char buf[256]; - struct nl_dump_params dp = { - .dp_type = NL_DUMP_DETAILS, - .dp_buf = buf, - .dp_buflen = sizeof (buf), - }; - - nl_object_dump (object, &dp); - buf[sizeof (buf) - 1] = '\0'; - debug ("netlink object:\n%s", buf); - } - return FALSE; - } - - return refresh_object (platform, object, FALSE, NM_PLATFORM_REASON_INTERNAL); -} - static gboolean kernel_add_object (NMPlatform *platform, ObjectType obj_type, const struct nl_object *nlo) { @@ -2915,72 +1944,6 @@ kernel_add_object (NMPlatform *platform, ObjectType obj_type, const struct nl_ob } } -/* Decreases the reference count if @obj for convenience */ -static gboolean -delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refresh_object) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int object_type; - int nle; - gboolean result = FALSE; - - object_type = _nlo_get_object_type (object); - g_return_val_if_fail (object_type != OBJECT_TYPE_UNKNOWN, FALSE); - - switch (object_type) { - case OBJECT_TYPE_LINK: - nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); - break; - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0); - break; - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0); - break; - default: - g_assert_not_reached (); - } - - switch (nle) { - case -NLE_SUCCESS: - break; - case -NLE_OBJ_NOTFOUND: - debug("delete_object failed with \"%s\" (%d), meaning the object was already removed", - nl_geterror (nle), nle); - break; - case -NLE_FAILURE: - if (object_type == OBJECT_TYPE_IP6_ADDRESS) { - /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */ - debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed", - nl_geterror (nle), nle); - break; - } - goto DEFAULT; - case -NLE_NOADDR: - if (object_type == OBJECT_TYPE_IP4_ADDRESS || object_type == OBJECT_TYPE_IP6_ADDRESS) { - debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed", - nl_geterror (nle), nle); - break; - } - goto DEFAULT; - DEFAULT: - default: - error ("Netlink error deleting %s: %s (%d)", to_string_object (platform, object), nl_geterror (nle), nle); - goto out; - } - - if (do_refresh_object) - refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL); - - result = TRUE; - -out: - nl_object_put (object); - return result; -} - static int kernel_delete_object (NMPlatform *platform, ObjectType object_type, const struct nl_object *object) { @@ -3144,80 +2107,6 @@ ref_object (struct nl_object *obj, void *data) *out = obj; } -static gboolean -_rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2) -{ - guint32 diff; - - if (ts1 == ts2) - return TRUE; - if (ts1 == NM_PLATFORM_LIFETIME_PERMANENT || - ts2 == NM_PLATFORM_LIFETIME_PERMANENT) - return FALSE; - - /** accept the timestamps as equal if they are within two seconds. */ - diff = ts1 > ts2 ? ts1 - ts2 : ts2 - ts1; - return diff <= 2; -} - -static gboolean -nm_nl_object_diff (ObjectType type, struct nl_object *_a, struct nl_object *_b) -{ - if (nl_object_diff (_a, _b)) { - /* libnl thinks objects are different*/ - return TRUE; - } - -#if HAVE_LIBNL_INET6_TOKEN - /* libnl ignores PROTINFO changes in object without AF assigned */ - if (type == OBJECT_TYPE_LINK) { - struct rtnl_addr *a = (struct rtnl_addr *) _a; - struct rtnl_addr *b = (struct rtnl_addr *) _b; - auto_nl_addr struct nl_addr *token_a = NULL; - auto_nl_addr struct nl_addr *token_b = NULL; - - if (rtnl_link_inet6_get_token ((struct rtnl_link *) a, &token_a) != 0) - token_a = NULL; - if (rtnl_link_inet6_get_token ((struct rtnl_link *) b, &token_b) != 0) - token_b = NULL; - - if (token_a && token_b) { - if (nl_addr_get_family (token_a) == AF_INET6 && - nl_addr_get_family (token_b) == AF_INET6 && - nl_addr_get_len (token_a) == sizeof (struct in6_addr) && - nl_addr_get_len (token_b) == sizeof (struct in6_addr) && - memcmp (nl_addr_get_binary_addr (token_a), - nl_addr_get_binary_addr (token_b), - sizeof (struct in6_addr))) { - /* Token changed */ - return TRUE; - } - } else if (token_a != token_b) { - /* Token added or removed (?). */ - return TRUE; - } - } -#endif - - if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) { - struct rtnl_addr *a = (struct rtnl_addr *) _a; - struct rtnl_addr *b = (struct rtnl_addr *) _b; - - /* libnl nl_object_diff() ignores differences in timestamp. Let's care about - * them (if they are large enough). - * - * Note that these valid and preferred timestamps are absolute, after - * _rtnl_addr_hack_lifetimes_rel_to_abs(). */ - if ( !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (a), - rtnl_addr_get_preferred_lifetime (b)) - || !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (a), - rtnl_addr_get_valid_lifetime (b))) - return TRUE; - } - - return FALSE; -} - /* This function does all the magic to avoid race conditions caused * by concurrent usage of synchronous commands and an asynchronous cache. This * might be a nice future addition to libnl but it requires to do all operations @@ -3644,21 +2533,6 @@ do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_f return TRUE; } -static gboolean -link_get_by_name (NMPlatform *platform, const char *name, NMPlatformLink *out_link) -{ - int ifindex; - - g_return_val_if_fail (name != NULL, FALSE); - - if (out_link) { - ifindex = nm_platform_link_get_ifindex (platform, name); - g_return_val_if_fail (ifindex > 0, FALSE); - return _nm_platform_link_get (platform, ifindex, out_link); - } - return TRUE; -} - static gboolean link_add (NMPlatform *platform, const char *name, @@ -3696,58 +2570,6 @@ link_add (NMPlatform *platform, return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link); } -static struct rtnl_link * -link_get (NMPlatform *platform, int ifindex) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex); - - if (!rtnllink) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return NULL; - } - - return rtnllink; -} - -static gboolean -link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - int nle; - - if (!rtnllink) - return FALSE; - g_return_val_if_fail (rtnl_link_get_ifindex (change) > 0, FALSE); - - nle = rtnl_link_change (priv->nlh, rtnllink, change, 0); - - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. - * - * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find - * firmware for the device, especially on nm_platform_link_set_up (). - * This is basically the same check as in the original code and could - * potentially be improved. - */ - switch (nle) { - case -NLE_SUCCESS: - case -NLE_EXIST: - break; - case -NLE_OBJ_NOTFOUND: - error ("Firmware not found for changing link %s; Netlink error: %s)", to_string_link (platform, change), nl_geterror (nle)); - platform->error = NM_PLATFORM_ERROR_NO_FIRMWARE; - return FALSE; - default: - error ("Netlink error changing link %s: %s", to_string_link (platform, change), nl_geterror (nle)); - return FALSE; - } - - return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL); -} - static gboolean link_delete (NMPlatform *platform, int ifindex) { @@ -4971,15 +3793,6 @@ link_get_driver_info (NMPlatform *platform, /******************************************************************/ -static gboolean -_address_match (struct rtnl_addr *addr, int family, int ifindex) -{ - g_return_val_if_fail (addr, FALSE); - - return rtnl_addr_get_family (addr) == family && - (ifindex == 0 || rtnl_addr_get_ifindex (addr) == ifindex); -} - static GArray * ipx_address_get_all (NMPlatform *platform, int ifindex, gboolean is_v4) { @@ -5193,15 +4006,6 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int return do_delete_object (platform, &obj_needle, NULL); } -static gboolean -ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen) -{ - auto_nl_object struct nl_object *object = build_rtnl_addr (platform, family, ifindex, addr, NULL, plen, 0, 0, 0, NULL); - auto_nl_object struct nl_object *cached_object = nl_cache_search (choose_cache (platform, object), object); - - return !!cached_object; -} - static gboolean ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { @@ -5253,29 +4057,6 @@ check_for_route: /******************************************************************/ -static gboolean -_route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel) -{ - struct rtnl_nexthop *nexthop; - - g_return_val_if_fail (rtnlroute, FALSE); - - if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST || - rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN || - rtnl_route_get_tos (rtnlroute) != 0 || - (!include_proto_kernel && rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL) || - rtnl_route_get_family (rtnlroute) != family || - rtnl_route_get_nnexthops (rtnlroute) != 1 || - rtnl_route_get_flags (rtnlroute) & RTM_F_CLONED) - return FALSE; - - if (ifindex == 0) - return TRUE; - - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - return rtnl_route_nh_get_ifindex (nexthop) == ifindex; -} - static GArray * ipx_route_get_all (NMPlatform *platform, int ifindex, gboolean is_v4, NMPlatformGetRouteMode mode) { @@ -5433,62 +4214,6 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, nlo); } -static struct rtnl_route * -route_search_cache (struct nl_cache *cache, int family, int ifindex, const void *network, int plen, guint32 metric) -{ - guint32 network_clean[4], dst_clean[4]; - struct nl_object *object; - - clear_host_address (family, network, plen, network_clean); - - for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { - struct nl_addr *dst; - struct rtnl_route *rtnlroute = (struct rtnl_route *) object; - - if (!_route_match (rtnlroute, family, ifindex, FALSE)) - continue; - - if (metric != rtnl_route_get_priority (rtnlroute)) - continue; - - dst = rtnl_route_get_dst (rtnlroute); - if ( !dst - || nl_addr_get_family (dst) != family - || nl_addr_get_prefixlen (dst) != plen) - continue; - - /* plen = 0 means all host bits, so all bits should be cleared. - * Likewise if the binary address is not present or all zeros. - */ - if (plen == 0 || nl_addr_iszero (dst)) - memset (dst_clean, 0, sizeof (dst_clean)); - else - clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean); - - if (memcmp (dst_clean, network_clean, - family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0) - continue; - - rtnl_route_get (rtnlroute); - return rtnlroute; - } - return NULL; -} - -static gboolean -refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, guint32 metric) -{ - struct nl_cache *cache; - auto_nl_object struct rtnl_route *cached_object = NULL; - - cache = choose_cache_by_type (platform, family == AF_INET ? OBJECT_TYPE_IP4_ROUTE : OBJECT_TYPE_IP6_ROUTE); - cached_object = route_search_cache (cache, family, ifindex, network, plen, metric); - - if (cached_object) - return refresh_object (platform, (struct nl_object *) cached_object, TRUE, NM_PLATFORM_REASON_INTERNAL); - return TRUE; -} - static gboolean ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { @@ -5573,20 +4298,6 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in return do_delete_object (platform, &obj_needle, nlo); } -static gboolean -ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, guint32 metric) -{ - auto_nl_object struct nl_object *object = build_rtnl_route (family, ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - network, plen, NULL, NULL, metric, 0); - struct nl_cache *cache = choose_cache (platform, object); - auto_nl_object struct nl_object *cached_object = nl_cache_search (cache, object); - - if (!cached_object) - cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric); - return !!cached_object; -} - static gboolean ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { @@ -5609,138 +4320,6 @@ ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, in /******************************************************************/ -/* Initialize the link cache while ensuring all links are of AF_UNSPEC, - * family (even though the kernel might set AF_BRIDGE for bridges). - * See also: _nl_link_family_unset() */ -static void -init_link_cache (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_object *object = NULL; - - rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache); - - do { - for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - if (rtnl_link_get_family ((struct rtnl_link *)object) != AF_UNSPEC) - break; - } - - if (object) { - /* A non-AF_UNSPEC object encoutnered */ - struct nl_object *existing; - - nl_object_get (object); - nl_cache_remove (object); - rtnl_link_set_family ((struct rtnl_link *)object, AF_UNSPEC); - existing = nl_cache_search (priv->link_cache, object); - if (existing) - nl_object_put (existing); - else - nl_cache_add (priv->link_cache, object); - nl_object_put (object); - } - } while (object); -} - -/* Calls announce_object with appropriate arguments for all objects - * which are not coherent between old and new caches and deallocates - * the old cache. */ -static void -cache_announce_changes (NMPlatform *platform, struct nl_cache *new, struct nl_cache *old) -{ - struct nl_object *object; - - if (!old) - return; - - for (object = nl_cache_get_first (new); object; object = nl_cache_get_next (object)) { - struct nl_object *cached_object = nm_nl_cache_search (old, object); - - if (cached_object) { - ObjectType type = _nlo_get_object_type (object); - if (nm_nl_object_diff (type, object, cached_object)) - announce_object (platform, object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL); - nl_object_put (cached_object); - } else - announce_object (platform, object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL); - } - for (object = nl_cache_get_first (old); object; object = nl_cache_get_next (object)) { - struct nl_object *cached_object = nm_nl_cache_search (new, object); - if (cached_object) - nl_object_put (cached_object); - else - announce_object (platform, object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); - } - - nl_cache_free (old); -} - -/* The cache should always avoid containing objects not handled by NM, like - * e.g. addresses of the AF_PHONET family. */ -static void -cache_remove_unknown (struct nl_cache *cache) -{ - GPtrArray *objects_to_remove = NULL; - struct nl_object *object; - - for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { - if (_nlo_get_object_type (object) == OBJECT_TYPE_UNKNOWN) { - if (!objects_to_remove) - objects_to_remove = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put); - nl_object_get (object); - g_ptr_array_add (objects_to_remove, object); - } - } - - if (objects_to_remove) { - guint i; - - for (i = 0; i < objects_to_remove->len; i++) - nl_cache_remove (g_ptr_array_index (objects_to_remove, i)); - - g_ptr_array_free (objects_to_remove, TRUE); - } -} - -/* Creates and populates the netlink object caches. Called upon platform init and - * when we run out of sync (out of buffer space, netlink congestion control). In case - * the caches already exist, it finds changed, added and removed objects, announces - * them and destroys the old caches. */ -static void -cache_repopulate_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_cache *old_link_cache = priv->link_cache; - struct nl_cache *old_address_cache = priv->address_cache; - struct nl_cache *old_route_cache = priv->route_cache; - struct nl_object *object; - - debug ("platform: %spopulate platform cache", old_link_cache ? "re" : ""); - - /* Allocate new netlink caches */ - init_link_cache (platform); - rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache); - rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache); - g_assert (priv->link_cache && priv->address_cache && priv->route_cache); - - /* Remove all unknown objects from the caches */ - cache_remove_unknown (priv->link_cache); - cache_remove_unknown (priv->address_cache); - cache_remove_unknown (priv->route_cache); - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); - } - - /* Make sure all changes we've missed are announced. */ - cache_announce_changes (platform, priv->link_cache, old_link_cache); - cache_announce_changes (platform, priv->address_cache, old_address_cache); - cache_announce_changes (platform, priv->route_cache, old_route_cache); -} - -/******************************************************************/ - #define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) #define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) @@ -6006,7 +4585,6 @@ constructed (GObject *_object) /* Set up udev monitoring */ priv->udev_client = g_udev_client_new (udev_subsys); g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform); - priv->udev_devices = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); /* request all IPv6 addresses (hopeing that there is at least one), to check for * the IFA_FLAGS attribute. */ @@ -6060,12 +4638,8 @@ nm_linux_platform_finalize (GObject *object) g_io_channel_unref (priv->event_channel); nl_socket_free (priv->nlh); nl_socket_free (priv->nlh_event); - nl_cache_free (priv->link_cache); - nl_cache_free (priv->address_cache); - nl_cache_free (priv->route_cache); g_object_unref (priv->udev_client); - g_hash_table_unref (priv->udev_devices); g_hash_table_unref (priv->wifi_data); G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); From 4fee05c35b9fee0786ff77b9b7631eb10002352d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 5 May 2015 15:33:02 +0200 Subject: [PATCH 23/50] platform: remove unused argument preserve_rtprot from rtprot_to_source() --- src/platform/nm-linux-platform.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 081dfaa85b..480cbd8011 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1128,15 +1128,13 @@ source_to_rtprot (NMIPConfigSource source) } static NMIPConfigSource -rtprot_to_source (guint rtprot, gboolean preserve_rtprot) +rtprot_to_source (guint rtprot) { switch (rtprot) { case RTPROT_UNSPEC: return NM_IP_CONFIG_SOURCE_UNKNOWN; case RTPROT_KERNEL: - if (preserve_rtprot) - return _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; - /* fall through */ + return _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; case RTPROT_REDIRECT: return NM_IP_CONFIG_SOURCE_KERNEL; case RTPROT_RA: @@ -1199,7 +1197,7 @@ _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject * */ obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; } else - obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo), TRUE); + obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo)); return TRUE; } @@ -1249,7 +1247,7 @@ _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; else - obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo), TRUE); + obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo)); return TRUE; } From 977626d9429baa2d30c575d045aab12b12a044be Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 3 May 2015 21:27:39 +0200 Subject: [PATCH 24/50] platform/test: add simple test for NMLinuxPlatform Just create a NMLinuxPlatform instance and unref it again. This already connects to netlink and fetches all objects. --- src/platform/tests/test-general.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index 2f29d7de70..9a6a79d44a 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -20,11 +20,24 @@ #include "nm-platform-utils.h" +#include + +#include "nm-linux-platform.h" #include "nm-logging.h" #include "nm-test-utils.h" +/******************************************************************/ + +static void +test_init_linux_platform () +{ + gs_unref_object NMPlatform *platform = NULL; + + platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL); +} + /******************************************************************/ NMTST_DEFINE (); @@ -34,5 +47,7 @@ main (int argc, char **argv) { nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + g_test_add_func ("/general/init_linux_platform", test_init_linux_platform); + return g_test_run (); } From 9a16ce08765faf76e29300090661a5c8e979f6bb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 7 May 2015 10:16:15 +0200 Subject: [PATCH 25/50] platform: refactor flushing of event socket to _nl_sock_flush_data() --- src/platform/nm-linux-platform.c | 38 +++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 480cbd8011..150ad20ac3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -277,6 +277,33 @@ nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) /******************************************************************/ +static int +_nl_sock_flush_data (struct nl_sock *sk) +{ + int nle; + struct nl_cb *cb; + + cb = nl_cb_clone (nl_socket_get_cb (sk)); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set (cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); + do { + errno = 0; + + nle = nl_recvmsgs (sk, cb); + + /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ + if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + nle = -NLE_AGAIN; + } while (nle != -NLE_AGAIN); + + nl_cb_put (cb); + return nle; +} + +/******************************************************************/ + #if HAVE_LIBNL_INET6_ADDR_GEN_MODE static int _support_user_ipv6ll = 0; #endif @@ -4360,17 +4387,8 @@ event_handler (GIOChannel *channel, warning ("Too many netlink events. Need to resynchronize platform cache"); /* Drain the event queue, we've lost events and are out of sync anyway and we'd * like to free up some space. We'll read in the status synchronously. */ - nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); - do { - errno = 0; + _nl_sock_flush_data (priv->nlh_event); - nle = nl_recvmsgs_default (priv->nlh_event); - - /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ - if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) - nle = -NLE_AGAIN; - } while (nle != -NLE_AGAIN); - nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data); delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); delayed_action_handle_all (platform); break; From bd9dab2a0991d598f2fe34bb0bd587ed78310f67 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 10 May 2015 09:16:31 +0200 Subject: [PATCH 26/50] platform: add priv pointer to NMLinuxPlatform --- src/platform/nm-linux-platform.c | 20 +++++++++++++++----- src/platform/nm-linux-platform.h | 4 ++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 150ad20ac3..eca9bcd882 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -393,7 +393,9 @@ typedef struct { gpointer user_data; } DelayedActionData; -typedef struct { +typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; + +struct _NMLinuxPlatformPrivate { struct nl_sock *nlh; struct nl_sock *nlh_event; NMPCache *cache; @@ -409,9 +411,15 @@ typedef struct { } delayed_action; GHashTable *wifi_data; -} NMLinuxPlatformPrivate; +}; -#define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate)) +static inline NMLinuxPlatformPrivate * +NM_LINUX_PLATFORM_GET_PRIVATE (const void *self) +{ + nm_assert (NM_IS_LINUX_PLATFORM (self)); + + return ((NMLinuxPlatform *) self)->priv; +} G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) @@ -4540,9 +4548,11 @@ handle_udev_event (GUdevClient *client, /******************************************************************/ static void -nm_linux_platform_init (NMLinuxPlatform *platform) +nm_linux_platform_init (NMLinuxPlatform *self) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMLinuxPlatformPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate); + + self->priv = priv; priv->cache = nmp_cache_new (); priv->delayed_action.list = g_array_new (FALSE, FALSE, sizeof (DelayedActionData)); diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index 6f2c199391..a9e2cd82f9 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -32,8 +32,12 @@ /******************************************************************/ +struct _NMLinuxPlatformPrivate; + typedef struct { NMPlatform parent; + + struct _NMLinuxPlatformPrivate *priv; } NMLinuxPlatform; typedef struct { From a2d793f0e12d7de92f81f3a76431653e0254dd2b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 May 2015 12:52:36 +0200 Subject: [PATCH 27/50] platform: add _support_user_ipv6ll_still_undecided() macro --- src/platform/nm-linux-platform.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index eca9bcd882..a1a7533aa8 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -306,13 +306,16 @@ _nl_sock_flush_data (struct nl_sock *sk) #if HAVE_LIBNL_INET6_ADDR_GEN_MODE static int _support_user_ipv6ll = 0; +#define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) +#else +#define _support_user_ipv6ll_still_undecided() (FALSE) #endif static gboolean _support_user_ipv6ll_get (void) { #if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (G_UNLIKELY (_support_user_ipv6ll == 0)) { + if (_support_user_ipv6ll_still_undecided ()) { _support_user_ipv6ll = -1; nm_log_warn (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "failed to detect; assume no support"); } else @@ -329,7 +332,7 @@ _support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) /* If we ever see a link with valid IPv6 link-local address * generation modes, the kernel supports it. */ - if (G_UNLIKELY (_support_user_ipv6ll == 0)) { + if (_support_user_ipv6ll_still_undecided ()) { uint8_t mode; if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) rtnl_link, &mode) == 0) { From f1f1c3cb732fdff37670b83de356a5dc8fba69fb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 May 2015 07:14:55 +0200 Subject: [PATCH 28/50] platform: reorder initialization First fully construct the GOjbect instance before starting to populate the cache. --- src/platform/nm-linux-platform.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index a1a7533aa8..10afa18ffc 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -4559,6 +4559,7 @@ nm_linux_platform_init (NMLinuxPlatform *self) priv->cache = nmp_cache_new (); priv->delayed_action.list = g_array_new (FALSE, FALSE, sizeof (DelayedActionData)); + priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); } static void @@ -4573,10 +4574,12 @@ constructed (GObject *_object) GUdevEnumerator *enumerator; GList *devices, *iter; + _LOGD ("create"); + /* Initialize netlink socket for requests */ priv->nlh = setup_socket (FALSE, platform); g_assert (priv->nlh); - debug ("Netlink socket for requests established: %d", nl_socket_get_local_port (priv->nlh)); + debug ("Netlink socket for requests established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), nl_socket_get_fd (priv->nlh)); /* Initialize netlink socket for events */ priv->nlh_event = setup_socket (TRUE, platform); @@ -4593,7 +4596,7 @@ constructed (GObject *_object) RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); g_assert (!nle); - debug ("Netlink socket for events established: %d", nl_socket_get_local_port (priv->nlh_event)); + debug ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh_event), nl_socket_get_fd (priv->nlh_event)); priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh_event)); g_io_channel_set_encoding (priv->event_channel, NULL, NULL); @@ -4604,24 +4607,26 @@ constructed (GObject *_object) channel_flags | G_IO_FLAG_NONBLOCK, NULL); g_assert (status); priv->event_id = g_io_add_watch (priv->event_channel, - (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), - event_handler, platform); - - _LOGD ("populate platform cache"); - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); - delayed_action_handle_all (platform); + (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), + event_handler, platform); /* Set up udev monitoring */ priv->udev_client = g_udev_client_new (udev_subsys); g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform); + /* complete construction of the GObject instance before populating the cache. */ + G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); + + _LOGD ("populate platform cache"); + /* request all IPv6 addresses (hopeing that there is at least one), to check for * the IFA_FLAGS attribute. */ nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP); if (nle < 0) nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle)); - priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); + delayed_action_handle_all (platform); /* And read initial device list */ enumerator = g_udev_enumerator_new (priv->udev_client); @@ -4636,8 +4641,6 @@ constructed (GObject *_object) } g_list_free (devices); g_object_unref (enumerator); - - G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); } static void @@ -4646,6 +4649,8 @@ dispose (GObject *object) NMPlatform *platform = NM_PLATFORM (object); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + _LOGD ("dispose"); + g_array_set_size (priv->delayed_action.list, 0); nm_clear_g_source (&priv->delayed_action.idle_id); From 56b07b1a3f26da472ae93ffb94a5928390057ef8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 May 2015 07:34:56 +0200 Subject: [PATCH 29/50] platform: register singleton instance early with NM_PLATFORM_REGISTER_SINGLETON Add a construct-only property NM_PLATFORM_REGISTER_SINGLETON to NMPlatform. When set to TRUE, the constructor will self-register to nm_platform_setup(). The reason for this is that the _LOG() macro in NMLinuxPlatform logs the self pointer if the instance is not the singleton instance. During construction, we already have many log lines due to initialization of the instance. These lines all end up qualified with the self pointer. By earlier self-registering, printing the pointer value is omitted. Yes, this patch is really just to prettify logging. --- src/platform/nm-linux-platform.c | 4 ++- src/platform/nm-platform.c | 52 ++++++++++++++++++++++++++++++++ src/platform/nm-platform.h | 4 +++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 10afa18ffc..6fe685e25b 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -429,7 +429,9 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) void nm_linux_platform_setup (void) { - nm_platform_setup (g_object_new (NM_TYPE_LINUX_PLATFORM, NULL)); + g_object_new (NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_REGISTER_SINGLETON, TRUE, + NULL); } /******************************************************************/ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index caad16e2c8..da1cea829c 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -58,6 +58,16 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; +enum { + PROP_0, + PROP_REGISTER_SINGLETON, + LAST_PROP, +}; + +typedef struct { + gboolean register_singleton; +} NMPlatformPrivate; + /******************************************************************/ /* Singleton NMPlatform subclass instance and cached class object */ @@ -3119,6 +3129,35 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { /******************************************************************/ +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_REGISTER_SINGLETON: + /* construct-only */ + priv->register_singleton = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +constructed (GObject *object) +{ + NMPlatform *self = NM_PLATFORM (object); + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + G_OBJECT_CLASS (nm_platform_parent_class)->constructed (object); + + if (priv->register_singleton) + nm_platform_setup (self); +} + static void nm_platform_init (NMPlatform *object) { @@ -3137,8 +3176,21 @@ nm_platform_class_init (NMPlatformClass *platform_class) { GObjectClass *object_class = G_OBJECT_CLASS (platform_class); + g_type_class_add_private (object_class, sizeof (NMPlatformPrivate)); + + object_class->set_property = set_property; + object_class->constructed = constructed; + platform_class->wifi_set_powersave = wifi_set_powersave; + g_object_class_install_property + (object_class, PROP_REGISTER_SINGLETON, + g_param_spec_boolean (NM_PLATFORM_REGISTER_SINGLETON, "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /* Signals */ SIGNAL (SIGNAL_LINK_CHANGED, log_link) SIGNAL (SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 62247bc31c..81771b87f2 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -40,6 +40,10 @@ /******************************************************************/ +#define NM_PLATFORM_REGISTER_SINGLETON "register-singleton" + +/******************************************************************/ + typedef struct _NMPlatform NMPlatform; /* workaround for older libnl version, that does not define these flags. */ From 0a3c1f57741677e21ec9efb9c67bfac81c31d208 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 May 2015 09:24:05 +0200 Subject: [PATCH 30/50] utils: add nm_utils_is_power_of_two() macro --- include/nm-macros-internal.h | 17 ++++++ libnm-core/tests/test-general.c | 98 +++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h index 9be6b0f321..0cb85cea74 100644 --- a/include/nm-macros-internal.h +++ b/include/nm-macros-internal.h @@ -181,6 +181,23 @@ nm_clear_g_source (guint *id) /*****************************************************************************/ +/* Determine whether @x is a power of two (@x being an integer type). + * For the special cases @x equals zero or one, it also returns true. + * For negative @x, always returns FALSE. That only applies, is the data + * type of @x is signed. */ +#define nm_utils_is_power_of_two(x) ({ \ + const typeof(x) __x = (x); \ + \ + ((__x & (__x - 1)) == 0) && \ + /* Check if the value is negative. In that case, return FALSE. + * The first expression is a compile time constant, depending on whether + * the type is signed. The second expression is a clumsy way for (__x >= 0), + * which causes a compiler warning for unsigned types. */ \ + ( ( ((typeof(__x)) -1) > ((typeof(__x)) 0) ) || (__x > 0) || (__x == 0) ); \ + }) + +/*****************************************************************************/ + /* check if @flags has exactly one flag (@check) set. You should call this * only with @check being a compile time constant and a power of two. */ #define NM_FLAGS_HAS(flags, check) \ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 1aca515a88..106b67606e 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -4358,6 +4358,103 @@ test_nm_utils_dns_option_find_idx (void) /******************************************************************************/ +enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED { + _DUMMY_1 = -1, +}; + +enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED { + _DUMMY_2, +}; + +enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED_64 { + _DUMMY_3 = (1LL << 40), +}; + +enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED_64 { + _DUMMY_4a = -1, + _DUMMY_4b = (1LL << 40), +}; + +#define test_nm_utils_is_power_of_two_do(type, x, expect) \ + G_STMT_START { \ + typeof (x) x1 = (x); \ + type x2 = (type) x1; \ + \ + if (((typeof (x1)) x2) == x1 && (x2 > 0 || x2 == 0)) { \ + /* x2 equals @x, and is positive. Compare to @expect */ \ + g_assert_cmpint (expect, ==, nm_utils_is_power_of_two (x2)); \ + } else if (!(x2 > 0) && !(x2 == 0)) { \ + /* a (signed) negative value is always FALSE. */ \ + g_assert_cmpint (FALSE, ==, nm_utils_is_power_of_two (x2));\ + } \ + g_assert_cmpint (expect, ==, nm_utils_is_power_of_two (x1)); \ + } G_STMT_END + +static void +test_nm_utils_is_power_of_two () +{ + guint64 xyes, xno; + gint i, j; + GRand *rand = nmtst_get_rand (); + int numbits; + + for (i = -1; i < 64; i++) { + + /* find a (positive) x which is a power of two. */ + if (i == -1) + xyes = 0; + else { + xyes = (1LL << i); + g_assert (xyes != 0); + } + + xno = xyes; + if (xyes != 0) { +again: + /* Find another @xno, that is not a power of two. Do that, + * by randomly setting bits. */ + numbits = g_rand_int_range (rand, 1, 65); + while (xno != ~((guint64) 0) && numbits > 0) { + guint64 v = (1LL << g_rand_int_range (rand, 0, 65)); + + if ((xno | v) != xno) { + xno |= v; + --numbits; + } + } + if (xno == xyes) + goto again; + } + + for (j = 0; j < 2; j++) { + gboolean expect = j == 0; + guint64 x = expect ? xyes : xno; + + if (!expect && xno == 0) + continue; + + /* check if @x is as @expect, when casted to a certain data type. */ + test_nm_utils_is_power_of_two_do (gint8, x, expect); + test_nm_utils_is_power_of_two_do (guint8, x, expect); + test_nm_utils_is_power_of_two_do (gint16, x, expect); + test_nm_utils_is_power_of_two_do (guint16, x, expect); + test_nm_utils_is_power_of_two_do (gint32, x, expect); + test_nm_utils_is_power_of_two_do (guint32, x, expect); + test_nm_utils_is_power_of_two_do (gint64, x, expect); + test_nm_utils_is_power_of_two_do (guint64, x, expect); + test_nm_utils_is_power_of_two_do (char, x, expect); + test_nm_utils_is_power_of_two_do (unsigned char, x, expect); + test_nm_utils_is_power_of_two_do (signed char, x, expect); + test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED, x, expect); + test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED, x, expect); + test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED_64, x, expect); + test_nm_utils_is_power_of_two_do (enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED_64, x, expect); + } + } +} + +/******************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -4459,6 +4556,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/_nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings); g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64); + g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two); g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate); g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx); From 3377cd7e180959f3265d0b91f630bbb73436bb09 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 May 2015 11:29:29 +0200 Subject: [PATCH 31/50] libnm: add _nm_utils_ptrarray_find_first() utility function --- libnm-core/nm-core-internal.h | 2 ++ libnm-core/nm-utils.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 76dc9355ca..27288df022 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -112,6 +112,8 @@ GPtrArray *_nm_utils_copy_array (const GPtrArray *array, GDestroyNotify free_func); GPtrArray *_nm_utils_copy_object_array (const GPtrArray *array); +gssize _nm_utils_ptrarray_find_first (gpointer *list, gssize len, gconstpointer needle); + gboolean _nm_utils_string_in_list (const char *str, const char **valid_strings); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 4a02c98c6e..8ce59b0eeb 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -631,6 +631,32 @@ _nm_utils_copy_object_array (const GPtrArray *array) return _nm_utils_copy_array (array, g_object_ref, g_object_unref); } +/* have @list of type 'gpointer *' instead of 'gconstpointer *' to + * reduce the necessity for annoying const-casts. */ +gssize +_nm_utils_ptrarray_find_first (gpointer *list, gssize len, gconstpointer needle) +{ + gssize i; + + if (len == 0) + return -1; + + if (len > 0) { + g_return_val_if_fail (list, -1); + for (i = 0; i < len; i++) { + if (list[i] == needle) + return i; + } + } else { + g_return_val_if_fail (needle, -1); + for (i = 0; list && list[i]; i++) { + if (list[i] == needle) + return i; + } + } + return -1; +} + GVariant * _nm_utils_bytes_to_dbus (const GValue *prop_value) { From 051cf8bbde9b73cdb3718be94e78237758b451ff Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 10 May 2015 10:02:31 +0200 Subject: [PATCH 32/50] platform: fetch objects via the event socket Use the event socket to request object via NLM_F_DUMP. No longer use 'priv->nlh' socket to fetch objects. Instead fetch them via the priv->nlh_event socket that also provides asynchronous events when objects change. That way, the events are in sync with our explicit requests and we can directly use the events. Previously, the events were only used to indicate that a refetch must happen, so that every event triggered a complete dump of all addresses/routes. We still use 'priv->nlh' to make synchronous requests such as adding/changing/deleting objects. That means, after we send a request, we must make sure that the result manifested itself at 'nlh_event' socket and the platform cache. That's why we sometimes still must force a dump to sync changes. That could be improved by using only one netlink socket so that we would wait for the ACK of our request. While not yet perfect, this already significantly reduces the number of fetches. Additionally, before, whenever requesting a dump of addresses or routes (which we did much more often, search for "get_kernel_object for type" log lines), we always dumped IPv4 and IPv6 together. Now only request the addr-family in question. https://bugzilla.gnome.org/show_bug.cgi?id=747985 https://bugzilla.redhat.com/show_bug.cgi?id=1211133 --- src/platform/nm-fake-platform.c | 60 +- src/platform/nm-linux-platform.c | 1157 +++++++++++++++++------------ src/platform/tests/test-cleanup.c | 2 +- src/platform/tests/test-link.c | 37 +- valgrind.suppressions | 18 - 5 files changed, 760 insertions(+), 514 deletions(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 4069650dbe..8361e147c0 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -32,6 +32,8 @@ #include "nm-fake-platform.h" #include "nm-logging.h" +#include "nm-test-utils.h" + #define debug(format, ...) nm_log_dbg (LOGD_PLATFORM, format, __VA_ARGS__) typedef struct { @@ -50,6 +52,7 @@ typedef struct { GBytes *address; int vlan_id; int ib_p_key; + struct in6_addr ip6_lladdr; } NMFakePlatformLink; #define NM_FAKE_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_PLATFORM, NMFakePlatformPrivate)) @@ -58,6 +61,15 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) /******************************************************************/ +static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal); + +static gboolean ip6_address_add (NMPlatform *platform, int ifindex, + struct in6_addr addr, struct in6_addr peer_addr, + int plen, guint32 lifetime, guint32 preferred, guint flags); +static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen); + +/******************************************************************/ + static gboolean sysctl_set (NMPlatform *platform, const char *path, const char *value) { @@ -105,16 +117,21 @@ type_to_type_name (NMLinkType type) 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->link.udi = 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) { @@ -207,9 +224,12 @@ link_add (NMPlatform *platform, g_array_append_val (priv->links, device); - if (device.link.ifindex) + if (device.link.ifindex) { g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE); + } + if (out_link) *out_link = device.link; return TRUE; @@ -305,12 +325,20 @@ link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *managed) } static void -link_changed (NMPlatform *platform, NMFakePlatformLink *device) +link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + if (raise_signal) + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + + if (device->link.ifindex && !IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { + if (device->link.connected) + ip6_address_add (platform, device->link.ifindex, device->ip6_lladdr, in6addr_any, 64, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); + else + ip6_address_delete (platform, device->link.ifindex, device->ip6_lladdr, 64); + } if (device->link.master) { gboolean connected = FALSE; @@ -328,7 +356,7 @@ link_changed (NMPlatform *platform, NMFakePlatformLink *device) if (master->link.connected != connected) { master->link.connected = connected; - link_changed (platform, master); + link_changed (platform, master, TRUE); } } } @@ -362,7 +390,7 @@ link_set_up (NMPlatform *platform, int ifindex) || device->link.connected != connected) { device->link.up = up; device->link.connected = connected; - link_changed (platform, device); + link_changed (platform, device, TRUE); } return TRUE; @@ -380,7 +408,7 @@ link_set_down (NMPlatform *platform, int ifindex) device->link.up = FALSE; device->link.connected = FALSE; - link_changed (platform, device); + link_changed (platform, device, TRUE); } return TRUE; @@ -396,7 +424,7 @@ link_set_arp (NMPlatform *platform, int ifindex) device->link.arp = TRUE; - link_changed (platform, device); + link_changed (platform, device, TRUE); return TRUE; } @@ -411,7 +439,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) device->link.arp = FALSE; - link_changed (platform, device); + link_changed (platform, device, TRUE); return TRUE; } @@ -450,7 +478,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t device->address = g_bytes_new (addr, len); - link_changed (platform, link_get (platform, ifindex)); + link_changed (platform, link_get (platform, ifindex), TRUE); return TRUE; } @@ -482,7 +510,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) if (device) { device->link.mtu = mtu; - link_changed (platform, device); + link_changed (platform, device, TRUE); } return !!device; @@ -592,7 +620,7 @@ link_enslave (NMPlatform *platform, int master, int slave) device->link.connected = TRUE; } - link_changed (platform, device); + link_changed (platform, device, TRUE); } return TRUE; @@ -614,8 +642,8 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) slave->link.master = 0; - link_changed (platform, slave); - link_changed (platform, master); + link_changed (platform, slave, TRUE); + link_changed (platform, master, TRUE); return TRUE; } @@ -1005,8 +1033,10 @@ ip6_address_add (NMPlatform *platform, int ifindex, if (item->plen != address.plen) continue; - memcpy (item, &address, sizeof (address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + if (nm_platform_ip6_address_cmp (item, &address) != 0) { + memcpy (item, &address, sizeof (address)); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + } return TRUE; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6fe685e25b..de82cc53fe 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -114,17 +114,33 @@ ******************************************************************/ typedef enum { - DELAYED_ACTION_TYPE_REFRESH_ALL, - DELAYED_ACTION_TYPE_REFRESH_LINK, - DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, - DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, - DELAYED_ACTION_TYPE_MASTER_CONNECTED, + DELAYED_ACTION_TYPE_NONE = 0, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = (1LL << 0), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = (1LL << 1), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = (1LL << 2), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << 3), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << 4), + DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 5), + DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 6), + DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 7), + __DELAYED_ACTION_TYPE_MAX, + + DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + + DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, } DelayedActionType; static gboolean tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props); -static void do_refresh_object (NMPlatform *platform, const NMPObject *obj_needle, NMPlatformReason reason, gboolean handle_all_delayed_actions, const NMPObject **out_obj); -static void do_refresh_all (NMPlatform *platform, ObjectType obj_type, int ifindex, NMPlatformReason reason); +static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data); +static void do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action); +static void do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action); static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); +static gboolean event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks); +static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason); /****************************************************************** * libnl unility functions and wrappers @@ -288,6 +304,8 @@ _nl_sock_flush_data (struct nl_sock *sk) return -NLE_NOMEM; nl_cb_set (cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); + nl_cb_set (cb, NL_CB_SEQ_CHECK, NL_CB_DEFAULT, NULL, NULL); + nl_cb_err (cb, NL_CB_DEFAULT, NULL, NULL); do { errno = 0; @@ -302,6 +320,83 @@ _nl_sock_flush_data (struct nl_sock *sk) return nle; } +static void +_nl_msg_set_seq (struct nl_sock *sk, struct nl_msg *msg, guint32 *out_seq) +{ + guint32 seq; + + /* choose our own sequence number, because libnl does not ensure that + * it isn't zero -- which would confuse our checking for outstanding + * messages. */ + seq = nl_socket_use_seq (sk); + if (seq == 0) + seq = nl_socket_use_seq (sk); + + nlmsg_hdr (msg)->nlmsg_seq = seq; + if (out_seq) + *out_seq = seq; +} + +static int +_nl_sock_request_link (NMPlatform *platform, struct nl_sock *sk, int ifindex, const char *name, guint32 *out_seq) +{ + struct nl_msg *msg = NULL; + int err; + + if (name && !name[0]) + name = NULL; + + g_return_val_if_fail (ifindex > 0 || name, -NLE_INVAL); + + _LOGT ("sock: request-link %d%s%s%s", ifindex, name ? ", \"" : "", name ? name : "", name ? "\"" : ""); + + if ((err = rtnl_link_build_get_request (ifindex, name, &msg)) < 0) + return err; + + _nl_msg_set_seq (sk, msg, out_seq); + + err = nl_send_auto (sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return 0; +} + +static int +_nl_sock_request_all (NMPlatform *platform, struct nl_sock *sk, ObjectType obj_type, guint32 *out_seq) +{ + const NMPClass *klass; + struct rtgenmsg gmsg = { 0 }; + struct nl_msg *msg; + int err; + + klass = nmp_class_from_type (obj_type); + + _LOGT ("sock: request-all-%s", klass->obj_type_name); + + /* reimplement + * nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP); + * because we need the sequence number. + */ + msg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + gmsg.rtgen_family = klass->addr_family; + err = nlmsg_append (msg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO); + if (err < 0) + goto errout; + + _nl_msg_set_seq (sk, msg, out_seq); + + err = nl_send_auto (sk, msg); +errout: + nlmsg_free(msg); + + return err >= 0 ? 0 : err; +} + /******************************************************************/ #if HAVE_LIBNL_INET6_ADDR_GEN_MODE @@ -391,16 +486,13 @@ _support_kernel_extended_ifa_flags_get (void) * NMPlatform types and functions ******************************************************************/ -typedef struct { - DelayedActionType action_type; - gpointer user_data; -} DelayedActionData; - typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; struct _NMLinuxPlatformPrivate { struct nl_sock *nlh; struct nl_sock *nlh_event; + guint32 nlh_seq_expect; + guint32 nlh_seq_last; NMPCache *cache; GIOChannel *event_channel; guint event_id; @@ -408,11 +500,16 @@ struct _NMLinuxPlatformPrivate { GUdevClient *udev_client; struct { - GArray *list; + DelayedActionType flags; + GPtrArray *list_master_connected; + GPtrArray *list_refresh_link; gint is_handling; guint idle_id; } delayed_action; + GHashTable *prune_candidates; + GHashTable *delayed_deletion; + GHashTable *wifi_data; }; @@ -468,75 +565,6 @@ _nlo_get_object_type (const struct nl_object *object) return OBJECT_TYPE_UNKNOWN; } -static NMPObject * -kernel_get_object (NMPlatform *platform, struct nl_sock *sock, const NMPObject *needle) -{ - auto_nl_object struct nl_object *nlo = NULL; - struct nl_object *nlo_needle; - NMPObject *obj; - int nle; - struct nl_cache *cache; - - switch (NMP_OBJECT_GET_TYPE (needle)) { - case OBJECT_TYPE_LINK: - nle = rtnl_link_get_kernel (sock, needle->link.ifindex, - needle->link.name[0] ? needle->link.name : NULL, - (struct rtnl_link **) &nlo); - switch (nle) { - case -NLE_SUCCESS: - _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); - obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - _LOGD ("kernel-get-link: succeeds for ifindex=%d, name=%s: %s", - needle->link.ifindex, needle->link.name, - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - return obj; - case -NLE_NODEV: - _LOGD ("kernel-get-link: succeeds for ifindex=%d, name=%s: NO DEVICE", - needle->link.ifindex, needle->link.name); - return NULL; - default: - _LOGD ("kernel-get-link: fails for ifindex=%d, name=%s: %s (%d)", - needle->link.ifindex, needle->link.name, - nl_geterror (nle), nle); - return NULL; - } - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - /* FIXME: every time we refresh *one* object, we request an - * entire dump. */ - nle = nl_cache_alloc_and_fill (nl_cache_ops_lookup (NMP_OBJECT_GET_CLASS (needle)->nl_type), - sock, &cache); - if (nle) { - _LOGE ("kernel-get-%s: fails for %s: %s (%d)", - NMP_OBJECT_GET_CLASS (needle)->obj_type_name, - nmp_object_to_string (needle, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nl_geterror (nle), nle); - return NULL; - } - - nlo_needle = nmp_object_to_nl (platform, needle, TRUE); - nlo = nl_cache_search (cache, nlo_needle); - nl_object_put (nlo_needle); - nl_cache_free (cache); - - if (!nlo) { - _LOGD ("kernel-get-%s: had no results for %s", - NMP_OBJECT_GET_CLASS (needle)->obj_type_name, - nmp_object_to_string (needle, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - return NULL; - } - obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - _LOGD ("kernel-get-%s: succeeds: %s", - NMP_OBJECT_GET_CLASS (needle)->obj_type_name, - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - return obj; - default: - g_return_val_if_reached (NULL); - } -} - /* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel * and parses its IFLA_INFO_DATA using a caller-provided parser. * @@ -871,6 +899,9 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); + if (_LOGT_ENABLED () && !NM_IN_SET (rtnl_link_get_family (nlo), AF_UNSPEC, AF_BRIDGE)) + _LOGT ("netlink object for ifindex %d has unusual family %d", rtnl_link_get_ifindex (nlo), rtnl_link_get_family (nlo)); + obj->ifindex = rtnl_link_get_ifindex (nlo); if (id_only) @@ -1349,25 +1380,51 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach /******************************************************************/ +static DelayedActionType +delayed_action_refresh_from_object_type (ObjectType obj_type) +{ + switch (obj_type) { + case OBJECT_TYPE_LINK: return DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS; + case OBJECT_TYPE_IP4_ADDRESS: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES; + case OBJECT_TYPE_IP6_ADDRESS: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES; + case OBJECT_TYPE_IP4_ROUTE: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES; + case OBJECT_TYPE_IP6_ROUTE: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; + default: g_return_val_if_reached (DELAYED_ACTION_TYPE_NONE); + } +} + +static ObjectType +delayed_action_refresh_to_object_type (DelayedActionType action_type) +{ + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS: return OBJECT_TYPE_LINK; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES: return OBJECT_TYPE_IP4_ADDRESS; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES: return OBJECT_TYPE_IP6_ADDRESS; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES: return OBJECT_TYPE_IP4_ROUTE; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES: return OBJECT_TYPE_IP6_ROUTE; + default: g_return_val_if_reached (OBJECT_TYPE_UNKNOWN); + } +} + static const char * delayed_action_to_string (DelayedActionType action_type) { - static const char *lookup[] = { - [DELAYED_ACTION_TYPE_REFRESH_ALL] = "refresh-all", - [DELAYED_ACTION_TYPE_REFRESH_LINK] = "refresh-link", - [DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX] = "refres-addresses-for-ifindex", - [DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX] = "refesh-routes-for-ifindex", - [DELAYED_ACTION_TYPE_MASTER_CONNECTED] = "master-connected", - }; - if ( ((gssize) action_type) >= 0 - && ((gssize) action_type) < G_N_ELEMENTS (lookup) - && lookup[action_type]) - return lookup[action_type]; - g_return_val_if_reached ("unknown"); + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS : return "refresh-all-links"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES : return "refresh-all-ip4-addresses"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES : return "refresh-all-ip6-addresses"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES : return "refresh-all-ip4-routes"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES : return "refresh-all-ip6-routes"; + case DELAYED_ACTION_TYPE_REFRESH_LINK : return "refresh-link"; + case DELAYED_ACTION_TYPE_MASTER_CONNECTED : return "master-connected"; + case DELAYED_ACTION_TYPE_READ_NETLINK : return "read-netlink"; + default: + return "unknown"; + } } -#define _LOGT_delayed_action(data, operation) \ - _LOGT ("delayed-action: %s %s (%d) [%p / %d]", ""operation, delayed_action_to_string ((data)->action_type), (int) (data)->action_type, (data)->user_data, GPOINTER_TO_INT ((data)->user_data)) +#define _LOGT_delayed_action(action_type, arg, operation) \ + _LOGT ("delayed-action: %s %s (%d) [%p / %d]", ""operation, delayed_action_to_string (action_type), (int) action_type, arg, GPOINTER_TO_INT (arg)) static void delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) @@ -1384,76 +1441,28 @@ delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex static void delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex) { - NMPObject needle; - - if (ifindex <= 0) - return; - do_refresh_object (platform, nmp_object_stackinit_id_link (&needle, ifindex), NM_PLATFORM_REASON_INTERNAL, FALSE, NULL); + do_request_link (platform, ifindex, NULL, FALSE); } static void -delayed_action_handle_REFRESH_ADDRESSES_FOR_IFINDEX (NMPlatform *platform, int ifindex) +delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags) { - if (ifindex > 0) - do_refresh_all (platform, OBJECT_TYPE_IP4_ADDRESS, ifindex, NM_PLATFORM_REASON_INTERNAL); + do_request_all (platform, flags, FALSE); } static void -delayed_action_handle_REFRESH_ROUTES_FOR_IFINDEX (NMPlatform *platform, int ifindex) +delayed_action_handle_READ_NETLINK (NMPlatform *platform) { - if (ifindex > 0) - do_refresh_all (platform, OBJECT_TYPE_IP4_ROUTE, ifindex, NM_PLATFORM_REASON_INTERNAL); -} - -static void -delayed_action_handle_REFRESH_ALL (NMPlatform *platform) -{ - do_refresh_all (platform, OBJECT_TYPE_LINK, 0, NM_PLATFORM_REASON_INTERNAL); - do_refresh_all (platform, OBJECT_TYPE_IP4_ADDRESS, 0, NM_PLATFORM_REASON_INTERNAL); - do_refresh_all (platform, OBJECT_TYPE_IP4_ROUTE, 0, NM_PLATFORM_REASON_INTERNAL); -} - -static const DelayedActionData * -delayed_action_find (GArray *array, DelayedActionType action_type, gconstpointer *user_data, gboolean consider_user_data) -{ - guint i; - - for (i = 0; i < array->len; i++) { - const DelayedActionData *d = &g_array_index (array, DelayedActionData, i); - - if ( d->action_type == action_type - && (!consider_user_data || d->user_data == user_data)) - return d; - } - return NULL; -} - -static guint -delayed_action_prune_all (GArray *array, DelayedActionType action_type, gconstpointer *user_data, gboolean consider_user_data) -{ - guint i, pruned = 0; - - for (i = 0; i < array->len; ) { - const DelayedActionData *d = &g_array_index (array, DelayedActionData, i); - - if ( action_type == d->action_type - && (!consider_user_data || user_data == d->user_data)) { - g_array_remove_index_fast (array, i); - pruned++; - } else - i++; - } - return pruned; + event_handler_read_netlink_all (platform, TRUE); } static gboolean delayed_action_handle_one (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const DelayedActionData *data, *data2; - int ifindex; + gpointer user_data; - if (priv->delayed_action.list->len == 0) { + if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) { nm_clear_g_source (&priv->delayed_action.idle_id); return FALSE; } @@ -1461,102 +1470,74 @@ delayed_action_handle_one (NMPlatform *platform) /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. * This type of action is entirely cache-internal and is here to resolve a * cache inconsistency. It should be fixed right away. */ - if ((data = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_MASTER_CONNECTED, NULL, FALSE))) { - /* It is possible that we also have a REFREH_LINK action scheduled that would refresh - * this link anyway. Still, handle the potential cache-inconsistency first and possibly - * reload the link later. */ - _LOGT_delayed_action (data, "handle"); - ifindex = GPOINTER_TO_INT (data->user_data); - delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); - delayed_action_handle_MASTER_CONNECTED (platform, ifindex); + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { + nm_assert (priv->delayed_action.list_master_connected->len > 0); + + user_data = priv->delayed_action.list_master_connected->pdata[0]; + g_ptr_array_remove_index_fast (priv->delayed_action.list_master_connected, 0); + if (priv->delayed_action.list_master_connected->len == 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_MASTER_CONNECTED; + nm_assert (_nm_utils_ptrarray_find_first (priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0); + + _LOGT_delayed_action (DELAYED_ACTION_TYPE_MASTER_CONNECTED, user_data, "handle"); + delayed_action_handle_MASTER_CONNECTED (platform, GPOINTER_TO_INT (user_data)); + return TRUE; + } + nm_assert (priv->delayed_action.list_master_connected->len == 0); + + /* Next we prefer read-netlink, because the buffer size is limited and we want to process events + * from netlink early. */ + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) { + _LOGT_delayed_action (DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle"); + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK; + delayed_action_handle_READ_NETLINK (platform); return TRUE; } - if ((data = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL, FALSE))) { - _LOGT_delayed_action (data, "handle"); + if (NM_FLAGS_ANY (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) { + DelayedActionType flags, iflags; - /* all our delayed actions are plain data. We can just flush the array. */ - g_array_set_size (priv->delayed_action.list, 0); - delayed_action_prune_all (priv->delayed_action.list, data->action_type, NULL, FALSE); - delayed_action_handle_REFRESH_ALL (platform); + flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; + + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; + + if (_LOGT_ENABLED ()) { + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (flags, iflags)) + _LOGT_delayed_action (iflags, NULL, "handle"); + } + } + + delayed_action_handle_REFRESH_ALL (platform, flags); return TRUE; } - data = &g_array_index (priv->delayed_action.list, DelayedActionData, 0); + nm_assert (priv->delayed_action.flags == DELAYED_ACTION_TYPE_REFRESH_LINK); + nm_assert (priv->delayed_action.list_refresh_link->len > 0); - switch (data->action_type) { + user_data = priv->delayed_action.list_refresh_link->pdata[0]; + g_ptr_array_remove_index_fast (priv->delayed_action.list_refresh_link, 0); + if (priv->delayed_action.list_master_connected->len == 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + nm_assert (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0); - case DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX: - ifindex = GPOINTER_TO_INT (data->user_data); + _LOGT_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle"); - /* We should reload routes for @ifindex. Check if we also should reload - * the link too, that has preference. Reloading link can trigger a reloading of routes. */ - data2 = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex), TRUE); - if (data2) { - _LOGT_delayed_action (data2, "handle"); - delayed_action_prune_all (priv->delayed_action.list, data2->action_type, data2->user_data, TRUE); - delayed_action_handle_REFRESH_LINK (platform, ifindex); - break; - } - - /* If we should reload routes but also have to reload addresses for the same @ifindex, - * reload the addresses first. Reloading addreses can trigger a reload or routes. */ - data2 = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, GINT_TO_POINTER (ifindex), TRUE); - if (data2) { - _LOGT_delayed_action (data2, "handle"); - delayed_action_prune_all (priv->delayed_action.list, data2->action_type, data2->user_data, TRUE); - delayed_action_handle_REFRESH_ADDRESSES_FOR_IFINDEX (platform, ifindex); - break; - } - - _LOGT_delayed_action (data, "handle"); - delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); - delayed_action_handle_REFRESH_ROUTES_FOR_IFINDEX (platform, ifindex); - break; - - - case DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX: - ifindex = GPOINTER_TO_INT (data->user_data); - - /* We should reload addresses for @ifindex. Check if we also should reload - * the link too, that has preference. Reloading link can trigger a reloading of addresses. */ - data2 = delayed_action_find (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex), TRUE); - if (data2) { - _LOGT_delayed_action (data2, "handle"); - delayed_action_prune_all (priv->delayed_action.list, data2->action_type, data2->user_data, TRUE); - delayed_action_handle_REFRESH_LINK (platform, ifindex); - break; - } - - _LOGT_delayed_action (data, "handle"); - delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); - delayed_action_handle_REFRESH_ADDRESSES_FOR_IFINDEX (platform, ifindex); - break; - - - case DELAYED_ACTION_TYPE_REFRESH_LINK: - _LOGT_delayed_action (data, "handle"); - ifindex = GPOINTER_TO_INT (data->user_data); - delayed_action_prune_all (priv->delayed_action.list, data->action_type, data->user_data, TRUE); - delayed_action_handle_REFRESH_LINK (platform, ifindex); - break; - - - default: - g_assert_not_reached (); - } + delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data)); return TRUE; } static gboolean -delayed_action_handle_all (NMPlatform *platform) +delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); gboolean any = FALSE; nm_clear_g_source (&priv->delayed_action.idle_id); priv->delayed_action.is_handling++; + if (read_netlink) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); while (delayed_action_handle_one (platform)) any = TRUE; priv->delayed_action.is_handling--; @@ -1567,7 +1548,7 @@ static gboolean delayed_action_handle_idle (gpointer user_data) { NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0; - delayed_action_handle_all (user_data); + delayed_action_handle_all (user_data, FALSE); return G_SOURCE_REMOVE; } @@ -1575,21 +1556,143 @@ static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionData data = { - .action_type = action_type, - .user_data = user_data, - }; + DelayedActionType iflags; - _LOGT_delayed_action (&data, "schedule"); + nm_assert (action_type != DELAYED_ACTION_TYPE_NONE); + + if (NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)) { + nm_assert (nm_utils_is_power_of_two (action_type)); + if (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0) + g_ptr_array_add (priv->delayed_action.list_refresh_link, user_data); + } else if (NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { + nm_assert (nm_utils_is_power_of_two (action_type)); + if (_nm_utils_ptrarray_find_first (priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0) + g_ptr_array_add (priv->delayed_action.list_master_connected, user_data); + } else + nm_assert (!user_data); + + priv->delayed_action.flags |= action_type; + + if (_LOGT_ENABLED ()) { + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (action_type, iflags)) + _LOGT_delayed_action (iflags, user_data, "schedule"); + } + } if (priv->delayed_action.is_handling == 0 && priv->delayed_action.idle_id == 0) priv->delayed_action.idle_id = g_idle_add (delayed_action_handle_idle, platform); - - g_array_append_val (priv->delayed_action.list, data); } /******************************************************************/ +static void +cache_prune_candidates_record_all (NMPlatform *platform, ObjectType obj_type) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, obj_type), + 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) +{ + 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); + } +} + +static void +cache_prune_candidates_prune (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; + + g_hash_table_iter_init (&iter, prune_candidates); + while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) { + auto_nmp_obj NMPObject *obj_cache = NULL; + + _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, NM_PLATFORM_REASON_INTERNAL); + } + + g_hash_table_unref (prune_candidates); +} + +static void +cache_delayed_deletion_prune (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GPtrArray *prune_list = NULL; + GHashTableIter iter; + guint i; + NMPObject *obj; + + if (g_hash_table_size (priv->delayed_deletion) == 0) + return; + + g_hash_table_iter_init (&iter, priv->delayed_deletion); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &obj)) { + if (obj) { + if (!prune_list) + prune_list = g_ptr_array_new_full (g_hash_table_size (priv->delayed_deletion), (GDestroyNotify) nmp_object_unref); + g_ptr_array_add (prune_list, nmp_object_ref (obj)); + } + } + + g_hash_table_remove_all (priv->delayed_deletion); + + if (prune_list) { + for (i = 0; i < prune_list->len; i++) { + obj = prune_list->pdata[i]; + _LOGT ("delayed-deletion: delete %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + cache_remove_netlink (platform, obj, NULL, NULL, NM_PLATFORM_REASON_EXTERNAL); + } + g_ptr_array_unref (prune_list); + } +} + static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) { @@ -1654,8 +1757,12 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP ifindex = new->link.ifindex; if (ifindex > 0) { - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, GINT_TO_POINTER (ifindex)); - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, GINT_TO_POINTER (ifindex)); + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); } } { @@ -1664,8 +1771,12 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP && old->_link.netlink.is_in_netlink && NM_FLAGS_HAS (old->link.flags, IFF_LOWER_UP) && new->_link.netlink.is_in_netlink - && !NM_FLAGS_HAS (new->link.flags, IFF_LOWER_UP)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, GINT_TO_POINTER (new->link.ifindex)); + && !NM_FLAGS_HAS (new->link.flags, IFF_LOWER_UP)) { + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); + } } { /* on enslave/release, we also refresh the master. */ @@ -1695,12 +1806,11 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP /* 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) { - /* Theoretically, it might suffice to reload only IPv4 or IPv6 separately. Don't - * do that optimizatoin, as our current implementation with nl_cache_alloc_and_fill() - * anyway dumps all addresses. */ delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, - GINT_TO_POINTER (old->ip_address.ifindex)); + (klass->obj_type == OBJECT_TYPE_IP4_ADDRESS) + ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); } } default: @@ -1756,188 +1866,91 @@ cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_ /******************************************************************/ static void -do_refresh_object (NMPlatform *platform, const NMPObject *obj_needle, NMPlatformReason reason, gboolean handle_all_delayed_actions, const NMPObject **out_obj) +_new_sequence_number (NMPlatform *platform, guint32 seq) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nmp_obj NMPObject *obj_kernel = NULL; - auto_nmp_obj NMPObject *obj_cache = NULL; - NMPCacheOpsType cache_op; - const NMPClass *klass; - char str_buf[sizeof (_nm_platform_to_string_buffer)]; - char str_buf2[sizeof (_nm_platform_to_string_buffer)]; - obj_kernel = kernel_get_object (platform, priv->nlh, obj_needle); + _LOGT ("_new_sequence_number(): new sequence number %u", seq); - klass = NMP_OBJECT_GET_CLASS (obj_needle); - if (klass->obj_type == OBJECT_TYPE_LINK) { - int ifindex = obj_kernel ? obj_kernel->link.ifindex : obj_needle->link.ifindex; - - if (ifindex >= 0) - delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex), TRUE); - } - - if (obj_kernel) - cache_op = cache_update_netlink (platform, obj_kernel, &obj_cache, NULL, reason); - else if ( klass->obj_type != OBJECT_TYPE_LINK - || obj_needle->link.ifindex > 0) - cache_op = cache_remove_netlink (platform, obj_needle, &obj_cache, NULL, reason); - else { - /* The needle has no ifindex and kernel_get_object() could not resolve the instance. - * For example infiniband_partition_add() does this. - * - * We cannot lookup this instance by name. */ - cache_op = NMP_CACHE_OPS_UNCHANGED; - } - - _LOGD ("refresh-%s: refreshed %s: %s", - klass->obj_type_name, - nmp_object_to_string (obj_needle, NMP_OBJECT_TO_STRING_ID, str_buf, sizeof (str_buf)), - nmp_object_to_string (obj_cache, NMP_OBJECT_TO_STRING_PUBLIC, str_buf2, sizeof (str_buf2))); - - if (handle_all_delayed_actions) { - if (delayed_action_handle_all (platform)) { - /* The object might have changed. Lookup again. */ - if (out_obj) { - /* We only lookup links by ifindex, not ifname. */ - if ( klass->obj_type != OBJECT_TYPE_LINK - || obj_needle->link.ifindex > 0) { - *out_obj = nmp_cache_lookup_obj (priv->cache, obj_needle); - } else - *out_obj = NULL; - } - return; - } - } - - if (out_obj) { - /* @out_obj has a somewhat unexpected meaning. It is not the same instance as returned by - * cache_update_netlink(). On the contrary, it is the object inside the cache (if it exists). - * - * As the cache owns the object already, we don't pass ownership (a reference). - * - * The effect is identical to lookup nmp_cache_lookup_obj() after do_refresh_object(). - * - * Here we don't do lookup by name. If @obj_needle only has an ifname, but no ifindex, - * the lookup cannot succeed. */ - if (cache_op == NMP_CACHE_OPS_REMOVED) - *out_obj = NULL; - else - *out_obj = obj_cache; - } + priv->nlh_seq_expect = seq; } static void -do_refresh_all (NMPlatform *platform, ObjectType obj_type, int ifindex, NMPlatformReason reason) +do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - guint i, j, len; - gs_unref_hashtable GHashTable *alive_list = NULL; - gs_unref_ptrarray GPtrArray *prune_list = NULL; - NMPCacheOpsType cache_op; - struct nl_object *nlo; - struct nl_cache *cache; - int nle; - const NMPClass *klass; + guint32 seq; - /* we can only reload v4 and v6 together. Coerce the obj_type to v4. */ - if (obj_type == OBJECT_TYPE_IP6_ADDRESS) - obj_type = OBJECT_TYPE_IP4_ADDRESS; - else if (obj_type == OBJECT_TYPE_IP6_ROUTE) - obj_type = OBJECT_TYPE_IP4_ROUTE; + _LOGT ("do_request_link (%d,%s)", ifindex, name ? name : ""); - if (obj_type == OBJECT_TYPE_LINK) - ifindex = 0; + if (ifindex > 0) { + NMPObject *obj; - if (obj_type == OBJECT_TYPE_IP4_ADDRESS) { - if (ifindex > 0) - delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, GINT_TO_POINTER (ifindex), TRUE); - else - delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ADDRESSES_FOR_IFINDEX, NULL, FALSE); - } else if (obj_type == OBJECT_TYPE_IP4_ROUTE) { - if (ifindex > 0) - delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, GINT_TO_POINTER (ifindex), TRUE); - else - delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_ROUTES_FOR_IFINDEX, NULL, FALSE); - } else if (obj_type == OBJECT_TYPE_LINK) - delayed_action_prune_all (priv->delayed_action.list, DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, FALSE); - - klass = nmp_class_from_type (obj_type); - - if (obj_type == OBJECT_TYPE_LINK) - nle = rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &cache); - else { - nle = nl_cache_alloc_and_fill (nl_cache_ops_lookup (klass->nl_type), - priv->nlh, &cache); - } - if (nle || !cache) { - error ("refresh-all for %s on interface %d failed: %s (%d)", - klass->obj_type_name, - ifindex, - nl_geterror (nle), nle); - return; + cache_prune_candidates_record_one (platform, + (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); + obj = nmp_object_new_link (ifindex); + _LOGT ("delayed-deletion: protect object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, obj, NULL); } - alive_list = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); + event_handler_read_netlink_all (platform, FALSE); - for (nlo = nl_cache_get_first (cache); nlo; nlo = nl_cache_get_next (nlo)) { - auto_nmp_obj NMPObject *obj_kernel = NULL; - NMPObject *obj_cache; + if (_nl_sock_request_link (platform, priv->nlh_event, ifindex, name, &seq) == 0) + _new_sequence_number (platform, seq); - if (obj_type == OBJECT_TYPE_LINK) - _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); + event_handler_read_netlink_all (platform, TRUE); - obj_kernel = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - if (!obj_kernel) - continue; + cache_delayed_deletion_prune (platform); + cache_prune_candidates_prune (platform); - if (ifindex > 0 && obj_kernel->object.ifindex != ifindex) - continue; + if (handle_delayed_action) + delayed_action_handle_all (platform, FALSE); +} - cache_op = cache_update_netlink (platform, obj_kernel, &obj_cache, NULL, reason); - if (!obj_cache) - continue; +static void +do_request_one_type (NMPlatform *platform, ObjectType obj_type, gboolean handle_delayed_action) +{ + do_request_all (platform, delayed_action_refresh_from_object_type (obj_type), handle_delayed_action); +} - if (cache_op == NMP_CACHE_OPS_REMOVED) - nmp_object_unref (obj_cache); - else - g_hash_table_add (alive_list, obj_cache); +static void +do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint32 seq; + DelayedActionType iflags; + + nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); + action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; + + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (action_type, iflags)) + cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags)); } - nl_cache_free (cache); - prune_list = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (action_type, iflags)) { + ObjectType obj_type = delayed_action_refresh_to_object_type (iflags); - /* Find all the objects that are not in the alive list and must be pruned. */ - for (j = 0; j < 2; j++) { - NMPCacheId cache_id; - const NMPlatformObject *const *list; - ObjectType obj_type_j; + /* clear any delayed action that request a refresh of this object type. */ + priv->delayed_action.flags &= ~iflags; + if (obj_type == OBJECT_TYPE_LINK) { + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); + } - obj_type_j = obj_type; - if (j == 1) { - if (obj_type == OBJECT_TYPE_LINK) - break; - if (obj_type == OBJECT_TYPE_IP4_ADDRESS) - obj_type_j = OBJECT_TYPE_IP6_ADDRESS; - else - obj_type_j = OBJECT_TYPE_IP6_ROUTE; - } + event_handler_read_netlink_all (platform, FALSE); - if (ifindex > 0) - nmp_cache_id_init_addrroute_by_ifindex (&cache_id, obj_type_j, ifindex); - else - nmp_cache_id_init_object_type (&cache_id, obj_type_j); - - list = nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - for (i = 0; i < len; i++) { - NMPObject *o = NMP_OBJECT_UP_CAST (list[i]); - - if (!g_hash_table_lookup (alive_list, o)) - g_ptr_array_add (prune_list, nmp_object_ref (o)); + if (_nl_sock_request_all (platform, priv->nlh_event, obj_type, &seq) == 0) + _new_sequence_number (platform, seq); } } + event_handler_read_netlink_all (platform, TRUE); - for (i = 0; i < prune_list->len; i++) - cache_remove_netlink (platform, prune_list->pdata[i], NULL, NULL, reason); + cache_prune_candidates_prune (platform); + + if (handle_delayed_action) + delayed_action_handle_all (platform, FALSE); } static gboolean @@ -2145,6 +2158,44 @@ ref_object (struct nl_object *obj, void *data) *out = obj; } +static int +event_seq_check (struct nl_msg *msg, gpointer user_data) +{ + NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data); + struct nlmsghdr *hdr; + + hdr = nlmsg_hdr (msg); + + if (hdr->nlmsg_seq == 0) + return NL_OK; + + priv->nlh_seq_last = hdr->nlmsg_seq; + + if (priv->nlh_seq_expect == 0) + _LOGT ("event_seq_check(): seq %u received (not waited)", hdr->nlmsg_seq); + else if (hdr->nlmsg_seq == priv->nlh_seq_expect) { + _LOGT ("event_seq_check(): seq %u received", hdr->nlmsg_seq); + + priv->nlh_seq_expect = 0; + } else + _LOGT ("event_seq_check(): seq %u received (wait for %u)", hdr->nlmsg_seq, priv->nlh_seq_last); + + return NL_OK; +} + +static int +event_err (struct sockaddr_nl *nla, struct nlmsgerr *nlerr, gpointer user_data) +{ + NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data); + int errsv = nlerr ? -nlerr->error : 0; + + _LOGT ("event_err(): error from kernel: %s (%d) for request %d", strerror (errsv), errsv, priv->nlh_seq_last); + + return NL_OK; +} + /* This function does all the magic to avoid race conditions caused * by concurrent usage of synchronous commands and an asynchronous cache. This * might be a nice future addition to libnl but it requires to do all operations @@ -2155,28 +2206,66 @@ static int event_notification (struct nl_msg *msg, gpointer user_data) { NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data); auto_nl_object struct nl_object *nlo = NULL; auto_nmp_obj NMPObject *obj = NULL; - int event; + struct nlmsghdr *msghdr; - event = nlmsg_hdr (msg)->nlmsg_type; + msghdr = nlmsg_hdr (msg); - if (_support_kernel_extended_ifa_flags_still_undecided () && event == RTM_NEWADDR) + if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); nl_msg_parse (msg, ref_object, &nlo); if (!nlo) return NL_OK; - obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - - if ( (obj && NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK) - || (!obj && _nlo_get_object_type (nlo) == OBJECT_TYPE_LINK)) + if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK) _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); - _LOGD ("event-notification: type %d: %s", event, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - if (obj) - do_refresh_object (platform, obj, NM_PLATFORM_REASON_EXTERNAL, TRUE, NULL); + obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); + + _LOGD ("event-notification: type %d, seq %u: %s", msghdr->nlmsg_type, msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + if (obj) { + auto_nmp_obj NMPObject *obj_cache = NULL; + + switch (msghdr->nlmsg_type) { + + case RTM_NEWLINK: + if ( NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK + && g_hash_table_lookup (priv->delayed_deletion, obj) != NULL) { + /* the object is scheduled for delayed deletion. Replace that object + * by clearing the value from priv->delayed_deletion. */ + _LOGT ("delayed-deletion: clear delayed deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), NULL); + } + /* fall-through */ + case RTM_NEWADDR: + case RTM_NEWROUTE: + cache_update_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); + break; + + case RTM_DELLINK: + if ( NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK + && g_hash_table_contains (priv->delayed_deletion, obj)) { + /* We sometimes receive spurious RTM_DELLINK events. In this case, we want to delay + * the deletion of the object until later. */ + _LOGT ("delayed-deletion: delay deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), nmp_object_ref (obj)); + break; + } + /* fall-through */ + case RTM_DELADDR: + case RTM_DELROUTE: + cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); + break; + + default: + break; + } + + cache_prune_candidates_drop (platform, obj_cache); + } return NL_OK; } @@ -2443,11 +2532,13 @@ _nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj } static gboolean -do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, const NMPObject **out_obj) +do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo) { NMPObject obj_needle; int nle; + event_handler_read_netlink_all (platform, FALSE); + nle = kernel_add_object (platform, OBJECT_TYPE_LINK, (const struct nl_object *) nlo); if (nle < 0) { _LOGE ("do-add-link: failure adding link '%s': %s", name, nl_geterror (nle)); @@ -2458,7 +2549,16 @@ do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo nmp_object_stackinit_id_link (&obj_needle, 0); g_strlcpy (obj_needle.link.name, name, sizeof (obj_needle.link.name)); - do_refresh_object (platform, &obj_needle, NM_PLATFORM_REASON_INTERNAL, TRUE, out_obj); + delayed_action_handle_all (platform, TRUE); + + /* FIXME: we add the link object via the second netlink socket. Sometimes, + * the notification is not yet ready via nlh_event, so we have to re-request the + * link so that it is in the cache. A better solution would be to do everything + * via one netlink socket. */ + if (!nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, 0, obj_needle.link.name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { + _LOGT ("do-add-link: reload: the added link is not yet ready. Request %s", obj_needle.link.name); + do_request_link (platform, 0, obj_needle.link.name, TRUE); + } /* Return true, because kernel_add_object() succeeded. This doesn't indicate that the * object is now actuall in the cache, because there could be a race. @@ -2472,12 +2572,13 @@ do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rt { const NMPObject *obj; - do_add_link (platform, name, nlo, &obj); - if (!obj || (expected_link_type != NM_LINK_TYPE_NONE && expected_link_type != obj->link.type)) - return FALSE; - if (out_link) + do_add_link (platform, name, nlo); + + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, name, FALSE, expected_link_type, NULL, NULL); + if (out_link && obj) *out_link = obj->link; - return TRUE; + return !!obj; } static gboolean @@ -2489,6 +2590,8 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl OBJECT_TYPE_IP4_ADDRESS, OBJECT_TYPE_IP6_ADDRESS, OBJECT_TYPE_IP4_ROUTE, OBJECT_TYPE_IP6_ROUTE)); + event_handler_read_netlink_all (platform, FALSE); + nle = kernel_add_object (platform, NMP_OBJECT_GET_CLASS (obj_id)->obj_type, (const struct nl_object *) nlo); if (nle < 0) { _LOGW ("do-add-%s: failure adding %s '%s': %s (%d)", @@ -2500,11 +2603,17 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl } _LOGD ("do-add-%s: success adding object %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - do_refresh_object (platform, obj_id, NM_PLATFORM_REASON_INTERNAL, TRUE, NULL); + delayed_action_handle_all (platform, TRUE); + + /* FIXME: instead of re-requesting the added object, add it via nlh_event + * so that the events are in sync. */ + if (!nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_id)) { + _LOGT ("do-add-%s: reload: the added object is not yet ready. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); + } /* The return value doesn't say, whether the object is in the platform cache after adding - * it. That would be wrong, because do_refresh_object() is not in sync with kernel_add_object() - * and there could be a race. + * it. * Instead the return value says, whether kernel_add_object() succeeded. */ return TRUE; } @@ -2513,9 +2622,12 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl static gboolean do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); auto_nl_object struct nl_object *nlo_free = NULL; int nle; + event_handler_read_netlink_all (platform, FALSE); + if (!nlo) nlo = nlo_free = nmp_object_to_nl (platform, obj_id, FALSE); @@ -2525,12 +2637,28 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl else _LOGD ("do-delete-%s: success deleting '%s'", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - /* also on failure refresh the cache. */ - do_refresh_object (platform, obj_id, NM_PLATFORM_REASON_INTERNAL, TRUE, NULL); + delayed_action_handle_all (platform, TRUE); - /* This doesn't say, whether the object is still in the platform cache. Since - * our delete operation is not in sync with do_refresh_object(), that would - * be racy. Instead, just return whether kernel_delete_object() succeeded. */ + /* FIXME: instead of re-requesting the deleted object, add it via nlh_event + * so that the events are in sync. */ + if (NMP_OBJECT_GET_TYPE (obj_id) == OBJECT_TYPE_LINK) { + const NMPObject *obj; + + obj = nmp_cache_lookup_link_full (priv->cache, obj_id->link.ifindex, obj_id->link.ifindex <= 0 && obj_id->link.name[0] ? obj_id->link.name : NULL, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); + if (obj && obj->_link.netlink.is_in_netlink) { + _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_link (platform, obj_id->link.ifindex, obj_id->link.name, TRUE); + } + } else { + if (nmp_cache_lookup_obj (priv->cache, obj_id)) { + _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); + } + } + + /* The return value doesn't say, whether the object is in the platform cache after adding + * it. + * Instead the return value says, whether kernel_add_object() succeeded. */ return nle >= 0; } @@ -2539,7 +2667,6 @@ do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_f { int nle; int ifindex; - NMPObject obj_needle; gboolean complete_from_cache2 = complete_from_cache; ifindex = rtnl_link_get_ifindex (nlo); @@ -2567,7 +2694,9 @@ do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_f return FALSE; } - do_refresh_object (platform, nmp_object_stackinit_id_link (&obj_needle, ifindex), NM_PLATFORM_REASON_INTERNAL, TRUE, NULL); + /* FIXME: as we modify the link via a separate socket, the cache is not in + * sync and we have to refetch the link. */ + do_request_link (platform, ifindex, NULL, TRUE); return TRUE; } @@ -2706,11 +2835,8 @@ link_get_flags (NMPlatform *platform, int ifindex) static gboolean link_refresh (NMPlatform *platform, int ifindex) { - NMPObject obj_needle; - const NMPObject *obj_cache; - - do_refresh_object (platform, nmp_object_stackinit_id_link (&obj_needle, ifindex), NM_PLATFORM_REASON_EXTERNAL, TRUE, &obj_cache); - return !!obj_cache; + do_request_link (platform, ifindex, NULL, TRUE); + return !!cache_lookup_link (platform, ifindex); } static gboolean @@ -3153,7 +3279,6 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatfor NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj_parent; const NMPObject *obj; - NMPObject obj_needle; gs_free char *path = NULL; gs_free char *id = NULL; gs_free char *ifname = NULL; @@ -3169,17 +3294,13 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatfor if (!nm_platform_sysctl_set (platform, path, id)) return FALSE; - nmp_object_stackinit_id_link (&obj_needle, 0); - g_strlcpy (obj_needle.link.name, ifname, sizeof (obj_needle.link.name)); + do_request_link (platform, 0, ifname, TRUE); - do_refresh_object (platform, &obj_needle, NM_PLATFORM_REASON_INTERNAL, TRUE, &obj); - - if (!obj || obj->link.type != NM_LINK_TYPE_INFINIBAND) - return FALSE; - - if (out_link) + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, ifname, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL); + if (out_link && obj) *out_link = obj->link; - return TRUE; + return !!obj; } typedef struct { @@ -4271,14 +4392,32 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * 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, re-fetch the route from kernel, and if that fails, there is nothing to do. - * On success, there is still a race that we might end up deleting the wrong route. */ + * 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); + } - do_refresh_object (platform, &obj_needle, NM_PLATFORM_REASON_INTERNAL, TRUE, &obj); + obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); + + if (metric == 0 && !obj) { + /* 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, OBJECT_TYPE_IP4_ROUTE, TRUE); + + obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); if (!obj) return TRUE; - } else - obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); + } if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) { /* When searching for a matching IPv4 route to delete, the kernel @@ -4383,17 +4522,27 @@ event_handler (GIOChannel *channel, GIOCondition io_condition, gpointer user_data) { - NMPlatform *platform = NM_PLATFORM (user_data); + delayed_action_handle_all (NM_PLATFORM (user_data), TRUE); + return TRUE; +} + +static gboolean +event_handler_read_netlink_one (NMPlatform *platform) +{ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; nle = nl_recvmsgs_default (priv->nlh_event); + + /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ + if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + nle = -NLE_AGAIN; + if (nle < 0) switch (nle) { + case -NLE_AGAIN: + return FALSE; case -NLE_DUMP_INTR: - /* this most likely happens due to our request (RTM_GETADDR, AF_INET6, NLM_F_DUMP) - * to detect support for support_kernel_extended_ifa_flags. This is not critical - * and can happen easily. */ debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; case -NLE_NOMEM: @@ -4401,9 +4550,14 @@ event_handler (GIOChannel *channel, /* Drain the event queue, we've lost events and are out of sync anyway and we'd * like to free up some space. We'll read in the status synchronously. */ _nl_sock_flush_data (priv->nlh_event); - - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); - delayed_action_handle_all (platform); + priv->nlh_seq_expect = 0; + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); break; default: error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); @@ -4412,6 +4566,71 @@ event_handler (GIOChannel *channel, return TRUE; } +static gboolean +event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int r; + struct pollfd pfd; + gboolean any = FALSE; + gint64 timestamp = 0, now; + const int TIMEOUT = 250; + int timeout = 0; + guint32 wait_for_seq = 0; + + while (TRUE) { + while (event_handler_read_netlink_one (platform)) + any = TRUE; + + if (!wait_for_acks || priv->nlh_seq_expect == 0) { + if (wait_for_seq) + _LOGT ("read-netlink-all: ACK for sequence number %u received", priv->nlh_seq_expect); + return any; + } + + now = nm_utils_get_monotonic_timestamp_ms (); + if (wait_for_seq != priv->nlh_seq_expect) { + /* We are waiting for a new sequence number (or we will wait for the first time). + * Reset/start counting the overall wait time. */ + _LOGT ("read-netlink-all: wait for ACK for sequence number %u...", priv->nlh_seq_expect); + wait_for_seq = priv->nlh_seq_expect; + timestamp = now; + timeout = TIMEOUT; + } else { + if ((now - timestamp) >= TIMEOUT) { + /* timeout. Don't wait for this sequence number anymore. */ + break; + } + + /* readjust the wait-time. */ + timeout = TIMEOUT - (now - timestamp); + } + + memset (&pfd, 0, sizeof (pfd)); + pfd.fd = nl_socket_get_fd (priv->nlh_event); + pfd.events = POLLIN; + r = poll (&pfd, 1, timeout); + + if (r == 0) { + /* timeout. */ + break; + } + if (r < 0) { + int errsv = errno; + + if (errsv != EINTR) { + _LOGE ("read-netlink-all: poll failed with %s", strerror (errsv)); + return any; + } + /* Continue to read again, even if there might be nothing to read after EINTR. */ + } + } + + _LOGW ("read-netlink-all: timeout waiting for ACK to sequence number %u...", wait_for_seq); + priv->nlh_seq_expect = 0; + return any; +} + static struct nl_sock * setup_socket (gboolean event, gpointer user_data) { @@ -4428,7 +4647,8 @@ setup_socket (gboolean event, gpointer user_data) /* Dispatch event messages (event socket only) */ if (event) { nl_socket_modify_cb (sock, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data); - nl_socket_disable_seq_check (sock); + nl_socket_modify_cb (sock, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, event_seq_check, user_data); + nl_socket_modify_err_cb (sock, NL_CB_CUSTOM, event_err, user_data); } nle = nl_connect (sock, NETLINK_ROUTE); @@ -4559,8 +4779,13 @@ nm_linux_platform_init (NMLinuxPlatform *self) self->priv = priv; + priv->delayed_deletion = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + (GDestroyNotify) nmp_object_unref); priv->cache = nmp_cache_new (); - priv->delayed_action.list = g_array_new (FALSE, FALSE, sizeof (DelayedActionData)); + priv->delayed_action.list_master_connected = g_ptr_array_new (); + priv->delayed_action.list_refresh_link = g_ptr_array_new (); priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); } @@ -4620,15 +4845,15 @@ constructed (GObject *_object) G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); _LOGD ("populate platform cache"); + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); - /* request all IPv6 addresses (hopeing that there is at least one), to check for - * the IFA_FLAGS attribute. */ - nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP); - if (nle < 0) - nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle)); - - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL, NULL); - delayed_action_handle_all (platform); + delayed_action_handle_all (platform, FALSE); /* And read initial device list */ enumerator = g_udev_enumerator_new (priv->udev_client); @@ -4653,10 +4878,15 @@ dispose (GObject *object) _LOGD ("dispose"); - g_array_set_size (priv->delayed_action.list, 0); + priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; + g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); + g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); nm_clear_g_source (&priv->delayed_action.idle_id); + g_clear_pointer (&priv->prune_candidates, g_hash_table_unref); + g_clear_pointer (&priv->delayed_deletion, g_hash_table_unref); + G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } @@ -4667,7 +4897,8 @@ nm_linux_platform_finalize (GObject *object) nmp_cache_free (priv->cache); - g_array_unref (priv->delayed_action.list); + g_ptr_array_unref (priv->delayed_action.list_master_connected); + g_ptr_array_unref (priv->delayed_action.list_refresh_link); /* Free netlink resources */ g_source_remove (priv->event_id); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 962730463f..75c057c89b 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -58,7 +58,7 @@ test_cleanup_internal (void) routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); g_assert_cmpint (addresses4->len, ==, 1); - g_assert_cmpint (addresses6->len, ==, 1); + g_assert_cmpint (addresses6->len, ==, 2); /* also has a IPv6 LL address. */ g_assert_cmpint (routes4->len, ==, 3); g_assert_cmpint (routes6->len, ==, 3); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index f2b82ae8c1..6d88bd4240 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -172,7 +172,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, master); no_error (); - accept_signal (link_changed); + accept_signals (link_changed, 1, 3); accept_signals (master_changed, 0, 1); /* enslaveing brings put the slave */ @@ -184,7 +184,7 @@ test_slave (int master, int type, SignalData *master_changed) /* Set master up */ g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, master)); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, master)); - accept_signal (master_changed); + accept_signals (master_changed, 1, 2); /* Master with a disconnected slave is disconnected * @@ -222,7 +222,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex)); no_error (); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); - accept_signal (link_changed); + accept_signals (link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ accept_signals (master_changed, 1, 2); @@ -230,8 +230,9 @@ test_slave (int master, int type, SignalData *master_changed) * * Gracefully succeed if already enslaved. */ - g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); ensure_no_signal (link_changed); + g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); + accept_signals (link_changed, 0, 2); ensure_no_signal (master_changed); /* Set slave option */ @@ -251,21 +252,30 @@ test_slave (int master, int type, SignalData *master_changed) } /* Release */ + ensure_no_signal (link_changed); g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0); no_error (); - accept_signal (link_changed); + accept_signals (link_changed, 1, 3); if (link_type != NM_LINK_TYPE_TEAM) accept_signals (master_changed, 1, 2); else accept_signals (master_changed, 1, 1); + ensure_no_signal (master_changed); + /* Release again */ + ensure_no_signal (link_changed); g_assert (!nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); error (NM_PLATFORM_ERROR_NOT_SLAVE); + ensure_no_signal (master_changed); + /* Remove */ + ensure_no_signal (link_changed); g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); no_error (); + accept_signals (master_changed, 0, 1); + accept_signals (link_changed, 0, 1); accept_signal (link_removed); free_signal (link_added); @@ -309,7 +319,7 @@ test_software (NMLinkType link_type, const char *link_typename) g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_set_noarp (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); - accept_signal (link_changed); + accept_signals (link_changed, 1, 2); g_assert (nm_platform_link_set_arp (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); @@ -353,6 +363,7 @@ test_software (NMLinkType link_type, const char *link_typename) default: break; } + free_signal (link_changed); /* Delete */ g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); @@ -379,7 +390,6 @@ test_software (NMLinkType link_type, const char *link_typename) /* No pending signal */ free_signal (link_added); - free_signal (link_changed); free_signal (link_removed); } @@ -543,20 +553,13 @@ test_external (void) wait_signal (link_changed); g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - /* This test doesn't trigger a netlink event at least on - * 3.8.2-206.fc18.x86_64. Disabling the waiting and checking code - * because of that. - */ + run_command ("ip link set %s arp on", DEVICE_NAME); -#if 0 wait_signal (link_changed); - g_assert (nm_platform_link_uses_arp (ifindex)); -#endif + g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); run_command ("ip link set %s arp off", DEVICE_NAME); -#if 0 wait_signal (link_changed); - g_assert (!nm_platform_link_uses_arp (ifindex)); -#endif + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); run_command ("ip link del %s", DEVICE_NAME); wait_signal (link_removed); diff --git a/valgrind.suppressions b/valgrind.suppressions index 6bcd4c9d09..5a345fd93c 100644 --- a/valgrind.suppressions +++ b/valgrind.suppressions @@ -417,24 +417,6 @@ # libnl3 ############################################################### -{ - # fixed by https://github.com/thom311/libnl/commit/d65c32a7205e679c7fc13f0e4565b13e698ba906 - libnl_rtnl_link_set_type_01 - Memcheck:Leak - match-leak-kinds: definite - fun:calloc - fun:vlan_alloc - fun:rtnl_link_set_type - fun:link_msg_parser - fun:__pickup_answer - fun:nl_cb_call - fun:recvmsgs - fun:nl_recvmsgs_report - fun:nl_recvmsgs - fun:nl_pickup - fun:rtnl_link_get_kernel - ... -} { # fixed by https://github.com/thom311/libnl/commit/d65c32a7205e679c7fc13f0e4565b13e698ba906 # Same issue as libnl_rtnl_link_set_type_01, but different backtrace by calling nl_msg_parse(). From 076fe578e3989a2834bc8620883c355ada84b498 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 25 May 2015 11:16:21 +0200 Subject: [PATCH 33/50] platform: remove redundant NMPlatformLink fields "arp" and "up" --- src/devices/nm-device.c | 10 +++++----- src/platform/nm-fake-platform.c | 23 ++++++++++++----------- src/platform/nm-linux-platform.c | 2 -- src/platform/nm-platform.c | 6 ++---- src/platform/nm-platform.h | 6 ------ src/platform/tests/dump.c | 7 ++++--- 6 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 63baa63270..2a19619d8f 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1406,15 +1406,15 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) if (ip_ifname_changed) update_for_ip_ifname_change (self); - if (priv->up != info->up) { - priv->up = info->up; + if (priv->up != NM_FLAGS_HAS (info->flags, IFF_UP)) { + priv->up = NM_FLAGS_HAS (info->flags, IFF_UP); /* Manage externally-created software interfaces only when they are IFF_UP */ g_assert (priv->ifindex > 0); if (NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) { gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN); - if (external_down && info->up) { + if (external_down && NM_FLAGS_HAS (info->flags, IFF_UP)) { if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) { /* Ensure the assume check is queued before any queued state changes * from the transition to UNAVAILABLE. @@ -1437,7 +1437,7 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) */ priv->unmanaged_flags &= ~NM_UNMANAGED_EXTERNAL_DOWN; } - } else if (!external_down && !info->up && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) { + } else if (!external_down && !NM_FLAGS_HAS (info->flags, IFF_UP) && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) { /* If the device is already disconnected and is set !IFF_UP, * unmanage it. */ @@ -8942,7 +8942,7 @@ set_property (GObject *object, guint prop_id, g_free (priv->iface); priv->iface = g_strdup (platform_device->name); priv->ifindex = platform_device->ifindex; - priv->up = platform_device->up; + priv->up = NM_FLAGS_HAS (platform_device->flags, IFF_UP); g_free (priv->driver); priv->driver = g_strdup (platform_device->driver); priv->platform_link_initialized = platform_device->initialized; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 8361e147c0..042d98a282 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -136,10 +136,11 @@ link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) strcpy (device->link.name, name); switch (device->link.type) { case NM_LINK_TYPE_DUMMY: - device->link.arp = FALSE; + device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_NOARP); break; default: - device->link.arp = TRUE; + device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_NOARP); + break; } device->address = NULL; } @@ -386,9 +387,9 @@ link_set_up (NMPlatform *platform, int ifindex) g_error ("Unexpected device type: %d", device->link.type); } - if ( device->link.up != up + if ( NM_FLAGS_HAS (device->link.flags, IFF_UP) != !!up || device->link.connected != connected) { - device->link.up = up; + device->link.flags = NM_FLAGS_ASSIGN (device->link.flags, IFF_UP, up); device->link.connected = connected; link_changed (platform, device, TRUE); } @@ -404,8 +405,8 @@ link_set_down (NMPlatform *platform, int ifindex) if (!device) return FALSE; - if (device->link.up || device->link.connected) { - device->link.up = FALSE; + if (NM_FLAGS_HAS (device->link.flags, IFF_UP) || device->link.connected) { + device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_UP); device->link.connected = FALSE; link_changed (platform, device, TRUE); @@ -422,7 +423,7 @@ link_set_arp (NMPlatform *platform, int ifindex) if (!device) return FALSE; - device->link.arp = TRUE; + device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_NOARP); link_changed (platform, device, TRUE); @@ -437,7 +438,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) if (!device) return FALSE; - device->link.arp = FALSE; + device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_NOARP); link_changed (platform, device, TRUE); @@ -449,7 +450,7 @@ link_is_up (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - return device ? device->link.up : FALSE; + return device ? NM_FLAGS_HAS (device->link.flags, IFF_UP) : FALSE; } static gboolean @@ -465,7 +466,7 @@ link_uses_arp (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - return device ? device->link.arp : FALSE; + return device ? !NM_FLAGS_HAS (device->link.flags, IFF_NOARP) : FALSE; } static gboolean @@ -616,7 +617,7 @@ link_enslave (NMPlatform *platform, int master, int slave) device->link.master = master; if (NM_IN_SET (master_device->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) { - device->link.up = TRUE; + device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_UP); device->link.connected = TRUE; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index de82cc53fe..fe40cbc535 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -913,9 +913,7 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob obj->type = link_extract_type (platform, nlo, complete_from_cache, &kind); obj->kind = g_intern_string (kind); obj->flags = rtnl_link_get_flags (nlo); - obj->up = NM_FLAGS_HAS (obj->flags, IFF_UP); obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP); - obj->arp = !NM_FLAGS_HAS (obj->flags, IFF_NOARP); obj->master = rtnl_link_get_master (nlo); obj->parent = rtnl_link_get_link (nlo); obj->mtu = rtnl_link_get_mtu (nlo); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index da1cea829c..f77ee93498 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2466,9 +2466,9 @@ nm_platform_link_to_string (const NMPlatformLink *link) return "(unknown link)"; str_flags = g_string_new (NULL); - if (!link->arp) + if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) g_string_append (str_flags, "NOARP,"); - if (link->up) + if (NM_FLAGS_HAS (link->flags, IFF_UP)) g_string_append (str_flags, "UP"); else g_string_append (str_flags, "DOWN"); @@ -2844,11 +2844,9 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD_STR (a, b, name); _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, up); _CMP_FIELD (a, b, vlan_id); _CMP_FIELD (a, b, flags); _CMP_FIELD (a, b, connected); - _CMP_FIELD (a, b, arp); _CMP_FIELD (a, b, mtu); _CMP_FIELD_BOOL (a, b, initialized); _CMP_FIELD (a, b, arptype); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 81771b87f2..a0addbfdd5 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -135,16 +135,10 @@ struct _NMPlatformLink { * but libnl stores the flag internally as u32. */ guint32 flags; - /* FIXME: @up is redundant to (@flags & IFF_UP) */ - gboolean up; - /* @connected is mostly identical to (@flags & IFF_UP). Except for bridge/bond masters, * where we coerce the link as disconnect if it has no slaves. */ gboolean connected; - /* FIXME: @arp is redundant to !(@flags & IFF_NOARP) */ - gboolean arp; - guint mtu; }; diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index d28d517936..5187d266f6 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -7,6 +7,7 @@ #include "nm-platform.h" #include "nm-linux-platform.h" #include "nm-fake-platform.h" +#include "nm-macros-internal.h" static void dump_interface (NMPlatformLink *link) @@ -26,14 +27,14 @@ dump_interface (NMPlatformLink *link) size_t addrlen; int i; - g_assert (link->up || !link->connected); + g_assert (NM_FLAGS_HAS (link->flags, IFF_UP) || !link->connected); printf ("%d: %s: %s", link->ifindex, link->name, nm_link_type_to_string (link->type)); - if (link->up) + if (NM_FLAGS_HAS (link->flags, IFF_UP)) printf (" %s", link->connected ? "CONNECTED" : "DISCONNECTED"); else printf (" DOWN"); - if (!link->arp) + if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) printf (" noarp"); if (link->master) printf (" master %d", link->master); From af70a7fc7c70d89420e909799c8a0b3583934ffd Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 29 May 2015 09:38:26 +0200 Subject: [PATCH 34/50] platform/trivial: rename by using _nl_*() prefix for libnl related functions --- src/platform/nm-linux-platform.c | 57 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index fe40cbc535..510cbbc310 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -189,9 +189,9 @@ _nl_has_capability (int capability) } /* Automatic deallocation of local variables */ -#define auto_nl_object __attribute__((cleanup(put_nl_object))) +#define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object))) static void -put_nl_object (void *ptr) +_nl_auto_nl_object (void *ptr) { struct nl_object **object = ptr; @@ -201,9 +201,9 @@ put_nl_object (void *ptr) } } -#define auto_nl_addr __attribute__((cleanup(put_nl_addr))) +#define auto_nl_addr __attribute__((cleanup(_nl_auto_nl_addr))) static void -put_nl_addr (void *ptr) +_nl_auto_nl_addr (void *ptr) { struct nl_addr **object = ptr; @@ -216,7 +216,7 @@ put_nl_addr (void *ptr) /* wrap the libnl alloc functions and abort on out-of-memory*/ static struct nl_addr * -_nm_nl_addr_build (int family, const void *buf, size_t size) +_nl_addr_build (int family, const void *buf, size_t size) { struct nl_addr *addr; @@ -228,7 +228,7 @@ _nm_nl_addr_build (int family, const void *buf, size_t size) } static struct rtnl_link * -_nm_rtnl_link_alloc (int ifindex, const char*name) +_nl_rtnl_link_alloc (int ifindex, const char*name) { struct rtnl_link *rtnllink; @@ -244,7 +244,7 @@ _nm_rtnl_link_alloc (int ifindex, const char*name) } static struct rtnl_addr * -_nm_rtnl_addr_alloc (int ifindex) +_nl_rtnl_addr_alloc (int ifindex) { struct rtnl_addr *rtnladdr; @@ -257,7 +257,7 @@ _nm_rtnl_addr_alloc (int ifindex) } static struct rtnl_route * -_nm_rtnl_route_alloc (void) +_nl_rtnl_route_alloc (void) { struct rtnl_route *rtnlroute = rtnl_route_alloc (); @@ -267,7 +267,7 @@ _nm_rtnl_route_alloc (void) } static struct rtnl_nexthop * -_nm_rtnl_route_nh_alloc (void) +_nl_rtnl_route_nh_alloc (void) { struct rtnl_nexthop *nexthop; @@ -279,7 +279,7 @@ _nm_rtnl_route_nh_alloc (void) /* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */ static void -nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) +_nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) { struct nl_addr *nladdr; @@ -289,7 +289,6 @@ nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) if (nladdr) nl_addr_set_prefixlen (nladdr, plen); } -#define rtnl_addr_set_prefixlen nm_rtnl_addr_set_prefixlen /******************************************************************/ @@ -2511,7 +2510,7 @@ build_rtnl_link (int ifindex, const char *name, NMLinkType type) struct rtnl_link *rtnllink; int nle; - rtnllink = _nm_rtnl_link_alloc (ifindex, name); + rtnllink = _nl_rtnl_link_alloc (ifindex, name); if (type) { nle = rtnl_link_set_type (rtnllink, nm_link_type_to_rtnl_type_string (type)); g_assert (!nle); @@ -2727,7 +2726,7 @@ link_add (NMPlatform *platform, g_assert ( (address != NULL) ^ (address_len == 0) ); if (address) { - auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, address_len); + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, address_len); rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); } @@ -2860,7 +2859,7 @@ link_uses_arp (NMPlatform *platform, int ifindex) static gboolean link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); const NMPObject *obj_cache; char buf[256]; @@ -2937,7 +2936,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable { #if HAVE_LIBNL_INET6_ADDR_GEN_MODE if (_support_user_ipv6ll_get ()) { - auto_nl_object struct rtnl_link *nlo = _nm_rtnl_link_alloc (ifindex, NULL); + auto_nl_object struct rtnl_link *nlo = _nl_rtnl_link_alloc (ifindex, NULL); guint8 mode = enabled ? IN6_ADDR_GEN_MODE_NONE : IN6_ADDR_GEN_MODE_EUI64; char buf[32]; @@ -2982,8 +2981,8 @@ link_supports_vlans (NMPlatform *platform, int ifindex) static gboolean link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); - auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, length); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, length); gs_free char *mac = NULL; rtnl_link_set_addr (change, nladdr); @@ -3028,7 +3027,7 @@ link_get_permanent_address (NMPlatform *platform, static gboolean link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); rtnl_link_set_mtu (change, mtu); debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu); @@ -3162,7 +3161,7 @@ vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) static gboolean link_enslave (NMPlatform *platform, int master, int slave) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (slave, NULL); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (slave, NULL); rtnl_link_set_master (change, master); debug ("link: change %d: enslave to master %d", slave, master); @@ -3994,10 +3993,10 @@ build_rtnl_addr (NMPlatform *platform, guint flags, const char *label) { - auto_nl_object struct rtnl_addr *rtnladdr = _nm_rtnl_addr_alloc (ifindex); + auto_nl_object struct rtnl_addr *rtnladdr = _nl_rtnl_addr_alloc (ifindex); struct rtnl_addr *rtnladdr_copy; int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); - auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (family, addr, addrlen); + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (family, addr, addrlen); int nle; /* IP address */ @@ -4017,14 +4016,14 @@ build_rtnl_addr (NMPlatform *platform, auto_nl_addr struct nl_addr *bcaddr = NULL; bcast = *((in_addr_t *) addr) | ~nm_utils_ip4_prefix_to_netmask (plen); - bcaddr = _nm_nl_addr_build (family, &bcast, addrlen); + bcaddr = _nl_addr_build (family, &bcast, addrlen); g_assert (bcaddr); rtnl_addr_set_broadcast (rtnladdr, bcaddr); } /* Peer/point-to-point address */ if (peer_addr) { - auto_nl_addr struct nl_addr *nlpeer = _nm_nl_addr_build (family, peer_addr, addrlen); + auto_nl_addr struct nl_addr *nlpeer = _nl_addr_build (family, peer_addr, addrlen); nle = rtnl_addr_set_peer (rtnladdr, nlpeer); if (nle && nle != -NLE_AF_NOSUPPORT) { @@ -4034,7 +4033,7 @@ build_rtnl_addr (NMPlatform *platform, } } - rtnl_addr_set_prefixlen (rtnladdr, plen); + _nl_rtnl_addr_set_prefixlen (rtnladdr, plen); if (lifetime) { /* note that here we set the relative timestamps (ticking from *now*). * Contrary to the rtnl_addr objects from our cache, which have absolute @@ -4276,18 +4275,18 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source, int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr); /* Workaround a libnl bug by using zero destination address length for default routes */ auto_nl_addr struct nl_addr *dst = NULL; - auto_nl_addr struct nl_addr *gw = gateway ? _nm_nl_addr_build (family, gateway, addrlen) : NULL; - auto_nl_addr struct nl_addr *pref_src_nl = pref_src ? _nm_nl_addr_build (family, pref_src, addrlen) : NULL; + auto_nl_addr struct nl_addr *gw = gateway ? _nl_addr_build (family, gateway, addrlen) : NULL; + auto_nl_addr struct nl_addr *pref_src_nl = pref_src ? _nl_addr_build (family, pref_src, addrlen) : NULL; /* There seem to be problems adding a route with non-zero host identifier. * Adding IPv6 routes is simply ignored, without error message. * In the IPv4 case, we got an error. Thus, we have to make sure, that * the address is sane. */ clear_host_address (family, network, plen, network_clean); - dst = _nm_nl_addr_build (family, network_clean, plen ? addrlen : 0); + dst = _nl_addr_build (family, network_clean, plen ? addrlen : 0); nl_addr_set_prefixlen (dst, plen); - rtnlroute = _nm_rtnl_route_alloc (); + rtnlroute = _nl_rtnl_route_alloc (); rtnl_route_set_table (rtnlroute, RT_TABLE_MAIN); rtnl_route_set_tos (rtnlroute, 0); rtnl_route_set_dst (rtnlroute, dst); @@ -4295,7 +4294,7 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source, rtnl_route_set_family (rtnlroute, family); rtnl_route_set_protocol (rtnlroute, source_to_rtprot (source)); - nexthop = _nm_rtnl_route_nh_alloc (); + nexthop = _nl_rtnl_route_nh_alloc (); rtnl_route_nh_set_ifindex (nexthop, ifindex); if (gw && !nl_addr_iszero (gw)) rtnl_route_nh_set_gateway (nexthop, gw); From 2f0a9bbe9a75c1c1ac3c426f70a562f2636bafd7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 29 May 2015 09:40:24 +0200 Subject: [PATCH 35/50] platform/trivial: move code (nm_rtnl_link_parse_info_data) --- src/platform/nm-linux-platform.c | 166 ++++++++++++++++--------------- 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 510cbbc310..8973f6bec8 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -292,6 +292,90 @@ _nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) /******************************************************************/ +/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel + * and parses its IFLA_INFO_DATA using a caller-provided parser. + * + * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser(). + */ + +typedef int (*NMNLInfoDataParser) (struct nlattr *info_data, gpointer parser_data); + +typedef struct { + NMNLInfoDataParser parser; + gpointer parser_data; +} NMNLInfoDataClosure; + +static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = { + [IFLA_LINKINFO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, +}; + +static int +info_data_parser (struct nl_msg *msg, void *arg) +{ + NMNLInfoDataClosure *closure = arg; + struct nlmsghdr *n = nlmsg_hdr (msg); + struct nlattr *tb[IFLA_MAX + 1]; + struct nlattr *li[IFLA_INFO_MAX + 1]; + int err; + + if (!nlmsg_valid_hdr (n, sizeof (struct ifinfomsg))) + return -NLE_MSG_TOOSHORT; + + err = nlmsg_parse (n, sizeof (struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy); + if (err < 0) + return err; + + if (!tb[IFLA_LINKINFO]) + return -NLE_MISSING_ATTR; + + err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy); + if (err < 0) + return err; + + if (!li[IFLA_INFO_DATA]) + return -NLE_MISSING_ATTR; + + return closure->parser (li[IFLA_INFO_DATA], closure->parser_data); +} + +static int +nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, + NMNLInfoDataParser parser, gpointer parser_data) +{ + NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; + struct nl_msg *msg = NULL; + struct nl_cb *cb; + int err; + + err = rtnl_link_build_get_request (ifindex, NULL, &msg); + if (err < 0) + return err; + + err = nl_send_auto (sk, msg); + nlmsg_free (msg); + if (err < 0) + return err; + + cb = nl_cb_clone (nl_socket_get_cb (sk)); + if (cb == NULL) + return -NLE_NOMEM; + nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data); + + err = nl_recvmsgs (sk, cb); + nl_cb_put (cb); + if (err < 0) + return err; + + nl_wait_for_ack (sk); + return 0; +} + +/******************************************************************/ + static int _nl_sock_flush_data (struct nl_sock *sk) { @@ -564,88 +648,6 @@ _nlo_get_object_type (const struct nl_object *object) return OBJECT_TYPE_UNKNOWN; } -/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel - * and parses its IFLA_INFO_DATA using a caller-provided parser. - * - * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser(). - */ - -typedef int (*NMNLInfoDataParser) (struct nlattr *info_data, gpointer parser_data); - -typedef struct { - NMNLInfoDataParser parser; - gpointer parser_data; -} NMNLInfoDataClosure; - -static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = { - [IFLA_LINKINFO] = { .type = NLA_NESTED }, -}; - -static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { - [IFLA_INFO_DATA] = { .type = NLA_NESTED }, -}; - -static int -info_data_parser (struct nl_msg *msg, void *arg) -{ - NMNLInfoDataClosure *closure = arg; - struct nlmsghdr *n = nlmsg_hdr (msg); - struct nlattr *tb[IFLA_MAX + 1]; - struct nlattr *li[IFLA_INFO_MAX + 1]; - int err; - - if (!nlmsg_valid_hdr (n, sizeof (struct ifinfomsg))) - return -NLE_MSG_TOOSHORT; - - err = nlmsg_parse (n, sizeof (struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy); - if (err < 0) - return err; - - if (!tb[IFLA_LINKINFO]) - return -NLE_MISSING_ATTR; - - err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy); - if (err < 0) - return err; - - if (!li[IFLA_INFO_DATA]) - return -NLE_MISSING_ATTR; - - return closure->parser (li[IFLA_INFO_DATA], closure->parser_data); -} - -static int -nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, - NMNLInfoDataParser parser, gpointer parser_data) -{ - NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; - struct nl_msg *msg = NULL; - struct nl_cb *cb; - int err; - - err = rtnl_link_build_get_request (ifindex, NULL, &msg); - if (err < 0) - return err; - - err = nl_send_auto (sk, msg); - nlmsg_free (msg); - if (err < 0) - return err; - - cb = nl_cb_clone (nl_socket_get_cb (sk)); - if (cb == NULL) - return -NLE_NOMEM; - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data); - - err = nl_recvmsgs (sk, cb); - nl_cb_put (cb); - if (err < 0) - return err; - - nl_wait_for_ack (sk); - return 0; -} - /******************************************************************/ static gboolean From 0e0d3b07759d024656b0eaca11760293a9626f2b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 29 May 2015 09:38:26 +0200 Subject: [PATCH 36/50] platform/trivial: rename functions (nm_rtnl_link_parse_info_data) --- src/platform/nm-linux-platform.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 8973f6bec8..aee2c653c1 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -292,7 +292,7 @@ _nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) /******************************************************************/ -/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel +/* _nl_link_parse_info_data(): Re-fetches a link from the kernel * and parses its IFLA_INFO_DATA using a caller-provided parser. * * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser(). @@ -314,7 +314,7 @@ static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { }; static int -info_data_parser (struct nl_msg *msg, void *arg) +_nl_link_parse_info_data_cb (struct nl_msg *msg, void *arg) { NMNLInfoDataClosure *closure = arg; struct nlmsghdr *n = nlmsg_hdr (msg); @@ -343,8 +343,8 @@ info_data_parser (struct nl_msg *msg, void *arg) } static int -nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, - NMNLInfoDataParser parser, gpointer parser_data) +_nl_link_parse_info_data (struct nl_sock *sk, int ifindex, + NMNLInfoDataParser parser, gpointer parser_data) { NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; struct nl_msg *msg = NULL; @@ -363,7 +363,7 @@ nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, cb = nl_cb_clone (nl_socket_get_cb (sk)); if (cb == NULL) return -NLE_NOMEM; - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data); + nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, _nl_link_parse_info_data_cb, &data); err = nl_recvmsgs (sk, cb); nl_cb_put (cb); @@ -3371,10 +3371,10 @@ infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, if (parent) *parent = obj->link.parent; - if (nm_rtnl_link_parse_info_data (priv->nlh, - ifindex, - infiniband_info_data_parser, - &info) != 0) { + if (_nl_link_parse_info_data (priv->nlh, + ifindex, + infiniband_info_data_parser, + &info) != 0) { const char *iface = obj->link.name; char *path, *contents = NULL; @@ -3555,8 +3555,8 @@ macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProp props->parent_ifindex = obj->link.parent; - err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, - macvlan_info_data_parser, props); + err = _nl_link_parse_info_data (priv->nlh, ifindex, + macvlan_info_data_parser, props); if (err != 0) { warning ("(%s) could not read properties: %s", obj->link.name, nl_geterror (err)); @@ -3686,8 +3686,8 @@ vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperti NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int err; - err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, - vxlan_info_data_parser, props); + err = _nl_link_parse_info_data (priv->nlh, ifindex, + vxlan_info_data_parser, props); if (err != 0) { warning ("(%s) could not read properties: %s", link_get_name (platform, ifindex), nl_geterror (err)); @@ -3740,8 +3740,8 @@ gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties * NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int err; - err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, - gre_info_data_parser, props); + err = _nl_link_parse_info_data (priv->nlh, ifindex, + gre_info_data_parser, props); if (err != 0) { warning ("(%s) could not read properties: %s", link_get_name (platform, ifindex), nl_geterror (err)); From 37df41a38e162cc2c5a820651f741e034fb91e4f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 29 May 2015 09:40:24 +0200 Subject: [PATCH 37/50] platform/trivial: move code (NMIPConfigSource conversion) --- src/platform/nm-linux-platform.c | 238 ++++++++++++++++--------------- 1 file changed, 122 insertions(+), 116 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index aee2c653c1..992174885a 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -565,6 +565,127 @@ _support_kernel_extended_ifa_flags_get (void) return _support_kernel_extended_ifa_flags > 0; } +/****************************************************************** + * Object type specific utilities + ******************************************************************/ + +static guint +source_to_rtprot (NMIPConfigSource source) +{ + switch (source) { + case NM_IP_CONFIG_SOURCE_UNKNOWN: + return RTPROT_UNSPEC; + case NM_IP_CONFIG_SOURCE_KERNEL: + case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + return RTPROT_KERNEL; + case NM_IP_CONFIG_SOURCE_DHCP: + return RTPROT_DHCP; + case NM_IP_CONFIG_SOURCE_RDISC: + return RTPROT_RA; + + default: + return RTPROT_STATIC; + } +} + +static NMIPConfigSource +rtprot_to_source (guint rtprot) +{ + switch (rtprot) { + case RTPROT_UNSPEC: + return NM_IP_CONFIG_SOURCE_UNKNOWN; + case RTPROT_KERNEL: + return _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; + case RTPROT_REDIRECT: + return NM_IP_CONFIG_SOURCE_KERNEL; + case RTPROT_RA: + return NM_IP_CONFIG_SOURCE_RDISC; + case RTPROT_DHCP: + return NM_IP_CONFIG_SOURCE_DHCP; + + default: + return NM_IP_CONFIG_SOURCE_USER; + } +} + +/******************************************************************/ + +typedef struct { + const NMLinkType nm_type; + const char *type_string; + + /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type + * should only be specified if the device type can be created without + * additional parameters, and if the device type can be determined from + * the rtnl_type. eg, tun/tap should not be specified since both + * tun and tap devices use "tun", and InfiniBand should not be + * specified because a PKey is required at creation. Drivers set this + * value from their 'struct rtnl_link_ops' structure. + */ + const char *rtnl_type; + + /* uevent DEVTYPE where applicable, from /sys/class/net//uevent; + * drivers set this value from their SET_NETDEV_DEV() call and the + * 'struct device_type' name member. + */ + const char *devtype; +} LinkDesc; + +static const LinkDesc linktypes[] = { + { NM_LINK_TYPE_NONE, "none", NULL, NULL }, + { NM_LINK_TYPE_UNKNOWN, "unknown", NULL, NULL }, + + { NM_LINK_TYPE_ETHERNET, "ethernet", NULL, NULL }, + { NM_LINK_TYPE_INFINIBAND, "infiniband", NULL, NULL }, + { NM_LINK_TYPE_OLPC_MESH, "olpc-mesh", NULL, NULL }, + { NM_LINK_TYPE_WIFI, "wifi", NULL, "wlan" }, + { NM_LINK_TYPE_WWAN_ETHERNET, "wwan", NULL, "wwan" }, + { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, + + { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, + { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, + { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, + { NM_LINK_TYPE_IFB, "ifb", "ifb", NULL }, + { NM_LINK_TYPE_LOOPBACK, "loopback", NULL, NULL }, + { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, + { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, + { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", 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" }, + { NM_LINK_TYPE_TEAM, "team", "team", NULL }, +}; + +static const char * +nm_link_type_to_rtnl_type_string (NMLinkType type) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (type == linktypes[i].nm_type) + return linktypes[i].rtnl_type; + } + g_return_val_if_reached (NULL); +} + +const char * +nm_link_type_to_string (NMLinkType type) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (type == linktypes[i].nm_type) + return linktypes[i].type_string; + } + g_return_val_if_reached (NULL); +} + /****************************************************************** * NMPlatform types and functions ******************************************************************/ @@ -666,83 +787,7 @@ check_support_user_ipv6ll (NMPlatform *platform) return _support_user_ipv6ll_get (); } -/* Object type specific utilities */ - -typedef struct { - const NMLinkType nm_type; - const char *type_string; - - /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type - * should only be specified if the device type can be created without - * additional parameters, and if the device type can be determined from - * the rtnl_type. eg, tun/tap should not be specified since both - * tun and tap devices use "tun", and InfiniBand should not be - * specified because a PKey is required at creation. Drivers set this - * value from their 'struct rtnl_link_ops' structure. - */ - const char *rtnl_type; - - /* uevent DEVTYPE where applicable, from /sys/class/net//uevent; - * drivers set this value from their SET_NETDEV_DEV() call and the - * 'struct device_type' name member. - */ - const char *devtype; -} LinkDesc; - -static const LinkDesc linktypes[] = { - { NM_LINK_TYPE_NONE, "none", NULL, NULL }, - { NM_LINK_TYPE_UNKNOWN, "unknown", NULL, NULL }, - - { NM_LINK_TYPE_ETHERNET, "ethernet", NULL, NULL }, - { NM_LINK_TYPE_INFINIBAND, "infiniband", NULL, NULL }, - { NM_LINK_TYPE_OLPC_MESH, "olpc-mesh", NULL, NULL }, - { NM_LINK_TYPE_WIFI, "wifi", NULL, "wlan" }, - { NM_LINK_TYPE_WWAN_ETHERNET, "wwan", NULL, "wwan" }, - { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, - - { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, - { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, - { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, - { NM_LINK_TYPE_IFB, "ifb", "ifb", NULL }, - { NM_LINK_TYPE_LOOPBACK, "loopback", NULL, NULL }, - { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, - { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, - { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", 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" }, - { NM_LINK_TYPE_TEAM, "team", "team", NULL }, -}; - -static const char * -nm_link_type_to_rtnl_type_string (NMLinkType type) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { - if (type == linktypes[i].nm_type) - return linktypes[i].rtnl_type; - } - g_return_val_if_reached (NULL); -} - -const char * -nm_link_type_to_string (NMLinkType type) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { - if (type == linktypes[i].nm_type) - return linktypes[i].type_string; - } - g_return_val_if_reached (NULL); -} +/******************************************************************/ #define DEVTYPE_PREFIX "DEVTYPE=" @@ -1178,45 +1223,6 @@ _nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObje return TRUE; } -static guint -source_to_rtprot (NMIPConfigSource source) -{ - switch (source) { - case NM_IP_CONFIG_SOURCE_UNKNOWN: - return RTPROT_UNSPEC; - case NM_IP_CONFIG_SOURCE_KERNEL: - case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_DHCP: - return RTPROT_DHCP; - case NM_IP_CONFIG_SOURCE_RDISC: - return RTPROT_RA; - - default: - return RTPROT_STATIC; - } -} - -static NMIPConfigSource -rtprot_to_source (guint rtprot) -{ - switch (rtprot) { - case RTPROT_UNSPEC: - return NM_IP_CONFIG_SOURCE_UNKNOWN; - case RTPROT_KERNEL: - return _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; - case RTPROT_REDIRECT: - return NM_IP_CONFIG_SOURCE_KERNEL; - case RTPROT_RA: - return NM_IP_CONFIG_SOURCE_RDISC; - case RTPROT_DHCP: - return NM_IP_CONFIG_SOURCE_DHCP; - - default: - return NM_IP_CONFIG_SOURCE_USER; - } -} - gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { From 5b80be5a2c25674f6f5ba251a4a68cbc335b9c7e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 29 May 2015 09:55:51 +0200 Subject: [PATCH 38/50] platform/trivial: rename functions (NMIPConfigSource conversion) --- src/platform/nm-linux-platform.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 992174885a..ee10153b88 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -570,7 +570,7 @@ _support_kernel_extended_ifa_flags_get (void) ******************************************************************/ static guint -source_to_rtprot (NMIPConfigSource source) +_nm_ip_config_source_to_rtprot (NMIPConfigSource source) { switch (source) { case NM_IP_CONFIG_SOURCE_UNKNOWN: @@ -589,7 +589,7 @@ source_to_rtprot (NMIPConfigSource source) } static NMIPConfigSource -rtprot_to_source (guint rtprot) +_nm_ip_config_source_from_rtprot (guint rtprot) { switch (rtprot) { case RTPROT_UNSPEC: @@ -1273,7 +1273,7 @@ _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject * */ obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; } else - obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo)); + obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); return TRUE; } @@ -1323,7 +1323,7 @@ _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; else - obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo)); + obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); return TRUE; } @@ -4300,7 +4300,7 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source, rtnl_route_set_dst (rtnlroute, dst); rtnl_route_set_priority (rtnlroute, metric); rtnl_route_set_family (rtnlroute, family); - rtnl_route_set_protocol (rtnlroute, source_to_rtprot (source)); + rtnl_route_set_protocol (rtnlroute, _nm_ip_config_source_to_rtprot (source)); nexthop = _nl_rtnl_route_nh_alloc (); rtnl_route_nh_set_ifindex (nexthop, ifindex); From e16fe9ddd41b84143b2645bdd98958fcf77e2c48 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 29 May 2015 11:12:15 +0200 Subject: [PATCH 39/50] platform: stringify nlmsg-event-type in logging --- src/platform/nm-linux-platform.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index ee10153b88..edb1addaa5 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -290,6 +290,26 @@ _nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) nl_addr_set_prefixlen (nladdr, plen); } +static const char * +_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +{ + const char *str_type = NULL; + + 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; + } + if (str_type) + g_strlcpy (buf, str_type, len); + else + g_snprintf (buf, len, "(%d)", type); + return buf; +} + /******************************************************************/ /* _nl_link_parse_info_data(): Re-fetches a link from the kernel @@ -2215,6 +2235,7 @@ event_notification (struct nl_msg *msg, gpointer user_data) auto_nl_object struct nl_object *nlo = NULL; auto_nmp_obj NMPObject *obj = NULL; struct nlmsghdr *msghdr; + char buf_nlmsg_type[16]; msghdr = nlmsg_hdr (msg); @@ -2230,7 +2251,9 @@ event_notification (struct nl_msg *msg, gpointer user_data) obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - _LOGD ("event-notification: type %d, seq %u: %s", msghdr->nlmsg_type, msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + _LOGD ("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, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); if (obj) { auto_nmp_obj NMPObject *obj_cache = NULL; From 2f4301bd261af371acad0a9ab00a786308060aa7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 10 Apr 2015 09:16:11 +0200 Subject: [PATCH 40/50] glib-compat: add g_ptr_array_insert() --- include/nm-glib-compat.h | 32 ++++++++++++++++++++++++++++++++ libnm-core/tests/test-general.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/include/nm-glib-compat.h b/include/nm-glib-compat.h index c5674ea9e7..da4e2c2ce0 100644 --- a/include/nm-glib-compat.h +++ b/include/nm-glib-compat.h @@ -180,4 +180,36 @@ nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value) } +#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST) +static inline void +_nm_g_ptr_array_insert (GPtrArray *array, + gint index_, + gpointer data) +{ + g_return_if_fail (array); + g_return_if_fail (index_ >= -1); + g_return_if_fail (index_ <= (gint) array->len); + + g_ptr_array_add (array, data); + + if (index_ != -1 && index_ != (gint) (array->len - 1)) { + memmove (&(array->pdata[index_ + 1]), + &(array->pdata[index_]), + (array->len - index_ - 1) * sizeof (gpointer)); + array->pdata[index_] = data; + } +} +#endif +#if !GLIB_CHECK_VERSION(2, 40, 0) +#define g_ptr_array_insert(array, index, data) G_STMT_START { _nm_g_ptr_array_insert (array, index, data); } G_STMT_END +#else +#define g_ptr_array_insert(array, index, data) \ + G_STMT_START { \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_ptr_array_insert (array, index, data); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + } G_STMT_END +#endif + + #endif /* __NM_GLIB_COMPAT_H__ */ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 106b67606e..670a0f3e97 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -19,6 +19,8 @@ * */ +#define NM_GLIB_COMPAT_H_TEST + #include "config.h" #include @@ -59,6 +61,7 @@ #include "nm-setting-wireless-security.h" #include "nm-simple-connection.h" #include "nm-keyfile-internal.h" +#include "nm-glib-compat.h" #include "nm-test-utils.h" @@ -4455,6 +4458,31 @@ again: /******************************************************************************/ +static void +test_g_ptr_array_insert (void) +{ + /* this test only makes sense on a recent glib, where we compare our compat + * with the original implementation. */ +#if GLIB_CHECK_VERSION(2, 40, 0) + gs_unref_ptrarray GPtrArray *arr1 = g_ptr_array_new (); + gs_unref_ptrarray GPtrArray *arr2 = g_ptr_array_new (); + GRand *rand = nmtst_get_rand (); + guint i; + + for (i = 0; i < 560; i++) { + gint32 idx = g_rand_int_range (rand, -1, arr1->len + 1); + + g_ptr_array_insert (arr1, idx, GINT_TO_POINTER (i)); + _nm_g_ptr_array_insert (arr2, idx, GINT_TO_POINTER (i)); + + g_assert_cmpint (arr1->len, ==, arr2->len); + g_assert (memcmp (arr1->pdata, arr2->pdata, arr1->len * sizeof (gpointer)) == 0); + } +#endif +} + +/******************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -4557,6 +4585,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64); g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two); + g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert); g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate); g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx); From 1b2b988ea95c5d7cc7d4e8bcee3ab2a325c62c52 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 14:41:35 +0200 Subject: [PATCH 41/50] platform: no longer expose udi field in NMPlatformLink The @udi field is not a static string, so any user of a NMPlatformLink instance must make sure not to use the field beyond the lifetime of the NMPlatformLink instance. As we pass on the platform link instance during platform changed events, this is hard to ensure for the subscriber of the signal -- because a call back into platform could invalidate/modify the object. Just not expose this field as part of the link instance. The few callers who actually needed it should instead call nm_platform_get_uid(). With that, the lifetime of the returned 'const char *' pointer is clearly defined. --- src/devices/nm-device-ethernet.c | 2 +- src/devices/nm-device.c | 8 +++++--- src/platform/nm-fake-platform.c | 14 +++++++++++++- src/platform/nm-linux-platform.c | 13 +++++++++++++ src/platform/nm-platform.c | 23 ++++++++++++++--------- src/platform/nm-platform.h | 8 ++------ src/platform/nmp-object.c | 15 +++------------ src/platform/tests/dump.c | 2 +- 8 files changed, 52 insertions(+), 33 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index a0aa02d1ac..70e9e70c47 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1559,7 +1559,7 @@ link_changed (NMDevice *device, NMPlatformLink *info) NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info); - if (!priv->subchan1 && info->udi) + if (!priv->subchan1 && info->initialized) _update_s390_subchannels (self); } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2a19619d8f..04472847be 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1341,11 +1341,13 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) NMUtilsIPv6IfaceId token_iid; gboolean ip_ifname_changed = FALSE; gboolean platform_unmanaged = FALSE; + const char *udi; - if (info->udi && g_strcmp0 (info->udi, priv->udi)) { + udi = nm_platform_link_get_udi (NM_PLATFORM_GET, info->ifindex); + if (udi && g_strcmp0 (udi, priv->udi)) { /* Update UDI to what udev gives us */ g_free (priv->udi); - priv->udi = g_strdup (info->udi); + priv->udi = g_strdup (udi); g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); } @@ -8938,7 +8940,7 @@ set_property (GObject *object, guint prop_id, platform_device = g_value_get_pointer (value); if (platform_device) { g_free (priv->udi); - priv->udi = g_strdup (platform_device->udi); + priv->udi = g_strdup (nm_platform_link_get_udi (NM_PLATFORM_GET, platform_device->ifindex)); g_free (priv->iface); priv->iface = g_strdup (platform_device->name); priv->ifindex = platform_device->ifindex; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 042d98a282..36c6898b48 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -129,7 +129,7 @@ link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) device->link.type = type; device->link.kind = type_to_type_name (type); device->link.driver = type_to_type_name (type); - device->link.udi = device->udi = g_strdup_printf ("fake:%d", ifindex); + device->udi = g_strdup_printf ("fake:%d", ifindex); device->link.initialized = TRUE; device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); if (name) @@ -543,6 +543,16 @@ link_get_dev_id (NMPlatform *platform, int ifindex) return 0; } +static const char * +link_get_udi (NMPlatform *platform, int ifindex) +{ + NMFakePlatformLink *device = link_get (platform, ifindex); + + if (!device) + return NULL; + return device->udi; +} + static gboolean link_get_wake_on_lan (NMPlatform *platform, int ifindex) { @@ -1511,6 +1521,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_get_type_name = link_get_type_name; platform_class->link_get_unmanaged = link_get_unmanaged; + platform_class->link_get_udi = link_get_udi; + platform_class->link_set_up = link_set_up; platform_class->link_set_down = link_set_down; platform_class->link_set_arp = link_set_arp; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index edb1addaa5..b42b40feda 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2950,6 +2950,18 @@ link_get_ipv6_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId *iid) return TRUE; } +static const char * +link_get_udi (NMPlatform *platform, int ifindex) +{ + const NMPObject *obj = cache_lookup_link (platform, ifindex); + + if ( !obj + || !obj->_link.netlink.is_in_netlink + || !obj->_link.udev.device) + return NULL; + return g_udev_device_get_sysfs_path (obj->_link.udev.device); +} + static gboolean link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) { @@ -4979,6 +4991,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_is_connected = link_is_connected; platform_class->link_uses_arp = link_uses_arp; + platform_class->link_get_udi = link_get_udi; platform_class->link_get_ipv6_token = link_get_ipv6_token; platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f77ee93498..b5a2099012 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -915,6 +915,18 @@ nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6Iface return FALSE; } +const char * +nm_platform_link_get_udi (NMPlatform *self, int ifindex) +{ + _CHECK_SELF (self, klass, FALSE); + reset_error (self); + + g_return_val_if_fail (ifindex >= 0, NULL); + + if (klass->link_get_udi) + return klass->link_get_udi (self, ifindex); + return NULL; +} /** * nm_platform_link_get_user_ip6vll_enabled: @@ -2533,7 +2545,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) "%s%s" /* addr */ "%s%s" /* inet6_token */ "%s%s" /* driver */ - "%s%s", /* udi */ + , link->ifindex, link->name, parent, @@ -2552,9 +2564,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) str_inet6_token ? " inet6token " : "", str_inet6_token ? str_inet6_token : "", link->driver ? " driver " : "", - link->driver ? link->driver : "", - link->udi ? " udi " : "", - link->udi ? link->udi : ""); + link->driver ? link->driver : ""); g_string_free (str_flags, TRUE); return _nm_platform_to_string_buffer; } @@ -2854,11 +2864,6 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); _CMP_FIELD (a, b, inet6_token.is_valid); _CMP_FIELD_STR_INTERNED (a, b, kind); - - /* udi is not an interned string, but NMRefString. Hence, - * do a pointer comparison first. */ - _CMP_FIELD_STR_INTERNED (a, b, udi); - _CMP_FIELD_STR_INTERNED (a, b, driver); if (a->addr.len) _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index a0addbfdd5..fb9ed34fec 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -95,12 +95,6 @@ struct _NMPlatformLink { /* NMPlatform initializes this field with a static string. */ const char *kind; - /* Beware: NMPlatform initializes this string with an allocated string - * (NMRefString). Handle it properly (i.e. don't keep a reference to it - * without incrementing the ref-counter). - * This property depends on @initialized. */ - const char *udi; - /* NMPlatform initializes this field with a static string. */ const char *driver; @@ -438,6 +432,7 @@ typedef struct { gboolean (*link_is_connected) (NMPlatform *, int ifindex); gboolean (*link_uses_arp) (NMPlatform *, int ifindex); + const char *(*link_get_udi) (NMPlatform *self, int ifindex); gboolean (*link_get_ipv6_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId *iid); gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex); @@ -627,6 +622,7 @@ gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex); gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex); gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid); +const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex); gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 38eef91452..2765d113d1 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -132,7 +132,6 @@ void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) { const char *driver = NULL; - const char *udi = NULL; gboolean initialized = FALSE; nm_assert (NMP_OBJECT_GET_TYPE (obj) == OBJECT_TYPE_LINK); @@ -145,10 +144,9 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) driver = _link_get_driver (obj->_link.udev.device, obj->link.kind, obj->link.name); - if (obj->_link.udev.device) { - udi = g_udev_device_get_sysfs_path (obj->_link.udev.device); + if (obj->_link.udev.device) initialized = TRUE; - } else if (!use_udev) { + else if (!use_udev) { /* If we don't use udev, we immediately mark the link as initialized. * * For that, we consult @use_udev argument, that is cached via @@ -165,7 +163,6 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) } obj->link.driver = driver; - obj->link.udi = nm_ref_string_replace (obj->link.udi, udi); obj->link.initialized = initialized; } @@ -225,7 +222,6 @@ nmp_object_unref (NMPObject *obj) static void _vt_cmd_obj_dispose_link (NMPObject *obj) { - nm_ref_string_unref (obj->link.udi); g_clear_object (&obj->_link.udev.device); } @@ -587,10 +583,6 @@ _vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) if (src->_link.udev.device) g_object_ref (src->_link.udev.device); } - if (dst->link.udi != src->link.udi) { - nm_ref_string_unref (dst->link.udi); - nm_ref_string_ref (src->link.udi); - } dst->_link = src->_link; } @@ -1512,8 +1504,7 @@ nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, * a use-case */ nm_assert (NMP_OBJECT_GET_TYPE (obj) != OBJECT_TYPE_LINK || ( !obj->_link.udev.device - && !obj->link.driver - && !obj->link.udi)); + && !obj->link.driver)); old = g_hash_table_lookup (cache->idx_main, obj); diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index 5187d266f6..575ce6fb34 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -44,7 +44,7 @@ dump_interface (NMPlatformLink *link) printf ("\n"); if (link->driver) printf (" driver: %s\n", link->driver); - printf (" UDI: %s\n", link->udi); + printf (" UDI: %s\n", nm_platform_link_get_udi (NM_PLATFORM_GET, link->ifindex)); if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, link->ifindex, &vlan_parent, &vlan_id)) g_assert_not_reached (); if (vlan_parent) From 3359dddd2e301536aee389867e01d22880a99510 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 18:40:34 +0200 Subject: [PATCH 42/50] Revert "core: add NMRefString" After hiding the udi field, there are no more users of NMRefString. Remove the code by explitly reverting the patch so that in case of a future need, we can find and resurrect NMRefString. This reverts commit 430658b17a706d12b0538e0f62fa8e1d593738e3. --- src/NetworkManagerUtils.c | 124 --------------------------- src/NetworkManagerUtils.h | 6 -- src/tests/test-general-with-expect.c | 52 ----------- 3 files changed, 182 deletions(-) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 7757643b29..fdaa31cd77 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -2713,127 +2713,3 @@ nm_utils_setpgid (gpointer unused G_GNUC_UNUSED) pid = getpid (); setpgid (pid, pid); } - -/****************************************************************** - * NMRefString - ******************************************************************/ - -#ifdef NM_MORE_ASSERTS -#define NM_STRING_CANARY(s) (GPOINTER_TO_UINT (s) ^ ((guint) 30112031329)) -#endif - -typedef struct _NMString { -#ifdef NM_STRING_CANARY - guint _canary; -#endif - int ref_count; - char str[1]; -} _NMString; - -static inline _NMString * -_nm_ref_string_up_cast (NMRefString nmstr) -{ - _NMString *s; - - s = (_NMString *) (((char *) nmstr) - G_STRUCT_OFFSET (_NMString, str)); -#ifdef NM_STRING_CANARY - g_return_val_if_fail (s->_canary == NM_STRING_CANARY (s), NULL); -#endif - g_return_val_if_fail (s->ref_count > 0, NULL); - - nm_assert (s->str == nmstr); - - return s; -} - -NMRefString -nm_ref_string_new (const char *str) -{ - _NMString *s; - gsize len; - - if (!str) - return NULL; - - len = strlen (str) + 1; - - s = g_malloc (G_STRUCT_OFFSET (_NMString, str) + len); - s->ref_count = 1; -#ifdef NM_STRING_CANARY - s->_canary = NM_STRING_CANARY (s); -#endif - memcpy (s->str, str, len); - return s->str; -} - -NMRefString -nm_ref_string_ref (NMRefString nmstr) -{ - _NMString *s; - - if (!nmstr) - return NULL; - - s = _nm_ref_string_up_cast (nmstr); - g_return_val_if_fail (s, NULL); - - s->ref_count++; - return s->str; -} - -void -nm_ref_string_unref (NMRefString nmstr) -{ - _NMString *s; - - if (!nmstr) - return; - - s = _nm_ref_string_up_cast (nmstr); - g_return_if_fail (s); - - if (--s->ref_count <= 0) { -#ifdef NM_STRING_CANARY - s->_canary = 0; -#endif - g_free (s); - } -} - -NMRefString -nm_ref_string_replace (NMRefString nmstr, const char *str) -{ - _NMString *s, *s2; - gsize len; - - if (!nmstr) - return nm_ref_string_new (str); - if (!str) { - nm_ref_string_unref (nmstr); - return NULL; - } - - s = _nm_ref_string_up_cast (nmstr); - g_return_val_if_fail (s, NULL); - - if (strcmp (s->str, str) == 0) - return nmstr; - - if (s->ref_count == 1) { - len = strlen (str) + 1; - - s2 = g_realloc (s, G_STRUCT_OFFSET (_NMString, str) + len); - -#ifdef NM_STRING_CANARY - s2->_canary = NM_STRING_CANARY (s2); -#endif - memcpy (s2->str, str, len); - return s2->str; - } else { - s->ref_count--; - return nm_ref_string_new (str); - } -} - -/******************************************************************/ - diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index c0ce323781..7624715264 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -201,10 +201,4 @@ gboolean nm_utils_get_testing_initialized (void); NMUtilsTestFlags nm_utils_get_testing (void); void _nm_utils_set_testing (NMUtilsTestFlags flags); -typedef const char *NMRefString; -NMRefString nm_ref_string_new (const char *str); -NMRefString nm_ref_string_ref (NMRefString nmstr); -void nm_ref_string_unref (NMRefString nmstr); -NMRefString nm_ref_string_replace (NMRefString nmstr, const char *str); - #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index e7a1453942..f671c15deb 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -35,57 +35,6 @@ /*******************************************/ -static void -test_nm_ref_string (void) -{ - const char *nm_str, *s1, *s2; - - nm_str = nm_ref_string_new ("hallo"); - - g_assert_cmpstr (nm_str, ==, "hallo"); - - nm_ref_string_ref (nm_str); - g_assert_cmpstr (nm_str, ==, "hallo"); - - nm_ref_string_unref (nm_str); - g_assert_cmpstr (nm_str, ==, "hallo"); - - nm_str = nm_ref_string_replace (nm_str, "hallo"); - g_assert_cmpstr (nm_str, ==, "hallo"); - - nm_str = nm_ref_string_replace (nm_str, "hallo2"); - g_assert_cmpstr (nm_str, ==, "hallo2"); - - nm_ref_string_unref (nm_str); - - /* replace() reallocs old memory if ref-count is 1. */ - s1 = nm_ref_string_new ("abcdef"); - g_assert_cmpstr (s1, ==, "abcdef"); - s2 = nm_ref_string_replace (s1, "ABC"); - g_assert_cmpstr (s2, ==, "ABC"); - nm_ref_string_unref (s2); - - /* replace() reallocs old memory if ref-count is 1. */ - s1 = nm_ref_string_new ("ABC"); - g_assert_cmpstr (s1, ==, "ABC"); - s2 = nm_ref_string_replace (s1, "abcdef"); - g_assert_cmpstr (s2, ==, "abcdef"); - nm_ref_string_unref (s2); - - /* replace allocates new memory if ref-count larger 1. */ - s1 = nm_ref_string_new ("ABC"); - g_assert_cmpstr (s1, ==, "ABC"); - nm_ref_string_ref (s1); - s2 = nm_ref_string_replace (s1, "abcdef"); - g_assert_cmpstr (s2, ==, "abcdef"); - g_assert_cmpstr (s1, ==, "ABC"); - g_assert (s1 != s2); - nm_ref_string_unref (s2); - nm_ref_string_unref (s1); -} - -/*******************************************/ - static void test_nm_utils_monotonic_timestamp_as_boottime (void) { @@ -909,7 +858,6 @@ main (int argc, char **argv) { nmtst_init_assert_logging (&argc, &argv, "DEBUG", "DEFAULT"); - g_test_add_func ("/general/nm_ref_string", test_nm_ref_string); g_test_add_func ("/general/nm_utils_monotonic_timestamp_as_boottime", test_nm_utils_monotonic_timestamp_as_boottime); g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); From e7ee2fc139e40fd7e41945f14272727a25b11035 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 18:47:04 +0200 Subject: [PATCH 43/50] platform: invoke platform signals with clone of object Don't expose @obj directly but clone the public fields. A signal handler might call back into NMPlatform which could invalidate (or modify) @obj. --- src/platform/nm-linux-platform.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index b42b40feda..f00fdc226e 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1354,6 +1354,8 @@ static void do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason) { 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)); @@ -1394,13 +1396,18 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach return; } + klass = NMP_OBJECT_GET_CLASS (obj); + _LOGT ("emit signal %s %s: %s (%ld)", - NMP_OBJECT_GET_CLASS (obj)->signal_type, + 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), (long) reason); - g_signal_emit_by_name (platform, NMP_OBJECT_GET_CLASS (obj)->signal_type, obj->object.ifindex, &obj->object, (NMPlatformSignalChangeType) cache_op, reason); + /* 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_by_name (platform, klass->signal_type, obj_clone.object.ifindex, &obj_clone.object, (NMPlatformSignalChangeType) cache_op, reason); } /******************************************************************/ From b74e620f2d8d6f28b833607994fb35d13b792d9e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 15:19:28 +0200 Subject: [PATCH 44/50] platform: expose GUdevDevice instance for platform links --- src/devices/nm-device-ethernet.c | 21 +++++++-------------- src/platform/nm-linux-platform.c | 15 +++++++++++++++ src/platform/nm-platform.c | 13 +++++++++++++ src/platform/nm-platform.h | 3 +++ 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 70e9e70c47..82cab00fe3 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -52,6 +52,7 @@ #include "nm-connection-provider.h" #include "nm-device-factory.h" #include "nm-core-internal.h" +#include "NetworkManagerUtils.h" #include "nm-device-ethernet-glue.h" @@ -151,28 +152,21 @@ static void _update_s390_subchannels (NMDeviceEthernet *self) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - GUdevClient *client; GUdevDevice *dev; GUdevDevice *parent = NULL; const char *parent_path, *item, *driver; - const char *subsystems[] = { "net", NULL }; - const char *iface; + int ifindex; GDir *dir; GError *error = NULL; - client = g_udev_client_new (subsystems); - if (!client) { - _LOGW (LOGD_DEVICE | LOGD_HW, "failed to initialize GUdev client"); - return; - } - - iface = nm_device_get_iface (NM_DEVICE (self)); - dev = iface ? g_udev_client_query_by_subsystem_and_name (client, "net", iface) : NULL; + ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + dev = (GUdevDevice *) nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex); if (!dev) { - _LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device '%s' with udev", - iface ? iface : "(null)"); + _LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device %d '%s' with udev", + ifindex, str_if_set (nm_device_get_iface (NM_DEVICE (self)), "(null)")); goto out; } + g_object_ref (dev); /* Try for the "ccwgroup" parent */ parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL); @@ -244,7 +238,6 @@ out: g_object_unref (parent); if (dev) g_object_unref (dev); - g_object_unref (client); } static GObject* diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f00fdc226e..d12b87b6a4 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2969,6 +2969,20 @@ link_get_udi (NMPlatform *platform, int ifindex) return g_udev_device_get_sysfs_path (obj->_link.udev.device); } +static GObject * +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 ? (GObject *) obj_cache->_link.udev.device : NULL; +} + static gboolean link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) { @@ -4999,6 +5013,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_uses_arp = link_uses_arp; platform_class->link_get_udi = link_get_udi; + platform_class->link_get_udev_device = link_get_udev_device; platform_class->link_get_ipv6_token = link_get_ipv6_token; platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index b5a2099012..0243fe8d94 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -928,6 +928,19 @@ nm_platform_link_get_udi (NMPlatform *self, int ifindex) return NULL; } +GObject * +nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) +{ + _CHECK_SELF (self, klass, FALSE); + reset_error (self); + + g_return_val_if_fail (ifindex >= 0, NULL); + + if (klass->link_get_udev_device) + return klass->link_get_udev_device (self, ifindex); + return NULL; +} + /** * nm_platform_link_get_user_ip6vll_enabled: * @self: platform instance diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index fb9ed34fec..6bc9d42524 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -433,6 +433,7 @@ typedef struct { gboolean (*link_uses_arp) (NMPlatform *, int ifindex); const char *(*link_get_udi) (NMPlatform *self, int ifindex); + GObject *(*link_get_udev_device) (NMPlatform *self, int ifindex); gboolean (*link_get_ipv6_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId *iid); gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex); @@ -624,6 +625,8 @@ gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex); gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid); const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex); +GObject *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex); + gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled); From f7fb68755c30d984aa844ecadbc2eb8715e2ecc8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 15:50:59 +0200 Subject: [PATCH 45/50] platform: rework NMPlatformError codes - rename "NONE" to "SUCCESS", what it really is. - change the to-string result not to contain spaces and being closer the name of the enum value. - add new error reasons "UNSPECIFIED" and "BUG". - remove the code comments around the enum definition. They add no further description about why this error happens and only paraphrase the name of the enum. - reserve negative integers for 'errno'. This is neat because if we get a system error we can pass on the underlying errno as cause. --- src/platform/nm-platform.c | 56 ++++++++++++++++++++++---------- src/platform/nm-platform.h | 22 ++++++++----- src/platform/tests/test-common.h | 2 +- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 0243fe8d94..21118a889e 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -173,11 +173,46 @@ void nm_platform_set_error (NMPlatform *self, NMPlatformError error) NMPlatformError nm_platform_get_error (NMPlatform *self) { - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_NONE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); return self->error; } +/** + * 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. + */ +const char * +nm_platform_error_to_string (NMPlatformError error) +{ + switch (error) { + case NM_PLATFORM_ERROR_SUCCESS: + return "success"; + case NM_PLATFORM_ERROR_BUG: + return "bug"; + case NM_PLATFORM_ERROR_UNSPECIFIED: + return "unspecified"; + case NM_PLATFORM_ERROR_NOT_FOUND: + return "not-found"; + case NM_PLATFORM_ERROR_EXISTS: + return "exists"; + case NM_PLATFORM_ERROR_WRONG_TYPE: + return "wrong-type"; + case NM_PLATFORM_ERROR_NOT_SLAVE: + return "not-slave"; + case NM_PLATFORM_ERROR_NO_FIRMWARE: + return "no-firmware"; + default: + if (error < 0) + return g_strerror (- ((int) error)); + return "unknown"; + } +} + /** * nm_platform_get_error_message: * @self: platform instance @@ -189,29 +224,14 @@ nm_platform_get_error_msg (NMPlatform *self) { _CHECK_SELF (self, klass, NULL); - switch (self->error) { - case NM_PLATFORM_ERROR_NONE: - return "unknown error"; - case NM_PLATFORM_ERROR_NOT_FOUND: - return "object not found"; - case NM_PLATFORM_ERROR_EXISTS: - return "object already exists"; - case NM_PLATFORM_ERROR_WRONG_TYPE: - return "object is wrong type"; - case NM_PLATFORM_ERROR_NOT_SLAVE: - return "link not a slave"; - case NM_PLATFORM_ERROR_NO_FIRMWARE: - return "firmware not found"; - default: - return "invalid error number"; - } + return nm_platform_error_to_string (self->error); } static void reset_error (NMPlatform *self) { g_assert (self); - self->error = NM_PLATFORM_ERROR_NONE; + self->error = NM_PLATFORM_ERROR_SUCCESS; } #define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 6bc9d42524..34cd6a38af 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -55,18 +55,23 @@ typedef struct _NMPlatform NMPlatform; #endif typedef enum { - /* no error specified, sometimes this means the arguments were wrong */ - NM_PLATFORM_ERROR_NONE, - /* object was not found */ + + /* dummy value, to enforce that the enum type is signed and has a size + * to hold an integer. We want to encode errno from as negative + * values. */ + _NM_PLATFORM_ERROR_MININT = G_MININT, + + NM_PLATFORM_ERROR_SUCCESS = 0, + + NM_PLATFORM_ERROR_BUG, + + NM_PLATFORM_ERROR_UNSPECIFIED, + NM_PLATFORM_ERROR_NOT_FOUND, - /* object already exists */ NM_PLATFORM_ERROR_EXISTS, - /* object is wrong type */ NM_PLATFORM_ERROR_WRONG_TYPE, - /* object is not a slave */ NM_PLATFORM_ERROR_NOT_SLAVE, - /* firmware is not found */ - NM_PLATFORM_ERROR_NO_FIRMWARE + NM_PLATFORM_ERROR_NO_FIRMWARE, } NMPlatformError; typedef enum { @@ -584,6 +589,7 @@ nm_platform_route_scope_inv (guint8 scope) const char *nm_link_type_to_string (NMLinkType link_type); +const char *nm_platform_error_to_string (NMPlatformError error); void nm_platform_set_error (NMPlatform *self, NMPlatformError error); NMPlatformError nm_platform_get_error (NMPlatform *self); const char *nm_platform_get_error_msg (NMPlatform *self); diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 563d9fb4f6..5f72a1480d 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -16,7 +16,7 @@ #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) #define error(err) g_assert (nm_platform_get_error (NM_PLATFORM_GET) == err) -#define no_error() error (NM_PLATFORM_ERROR_NONE) +#define no_error() error (NM_PLATFORM_ERROR_SUCCESS) typedef struct { int handler_id; From d7fe907c322a3b447ebefcf2be1e3a803253f55a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 16:19:19 +0200 Subject: [PATCH 46/50] platform: return NMPlatformError from link-add functions Later remove nm_platform_get_error() and signal errors via return error codes. Also, fix nm_platform_infiniband_partition_add() and nm_platform_vlan_add() to check the type of the existing link and fail with WRONG_TYPE otherwise. --- src/devices/nm-device-bond.c | 7 +- src/devices/nm-device-bridge.c | 9 +- src/devices/nm-device-infiniband.c | 7 +- src/devices/nm-device-vlan.c | 9 +- src/devices/team/nm-device-team.c | 7 +- src/platform/nm-platform.c | 156 +++++++++++++++++------------ src/platform/nm-platform.h | 12 +-- src/platform/tests/platform.c | 10 +- src/platform/tests/test-address.c | 2 +- src/platform/tests/test-cleanup.c | 2 +- src/platform/tests/test-link.c | 25 ++--- src/platform/tests/test-route.c | 2 +- src/tests/test-route-manager.c | 4 +- 13 files changed, 145 insertions(+), 107 deletions(-) diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 50094189d9..7f1394248d 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -571,16 +571,17 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, GError **error) { const char *iface = nm_connection_get_interface_name (connection); + NMPlatformError plerr; g_assert (iface); - if ( !nm_platform_bond_add (NM_PLATFORM_GET, iface, NULL) - && nm_platform_get_error (NM_PLATFORM_GET) != NM_PLATFORM_ERROR_EXISTS) { + plerr = nm_platform_bond_add (NM_PLATFORM_GET, iface, NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create bond interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_get_error_msg (NM_PLATFORM_GET)); + nm_platform_error_to_string (plerr)); return NULL; } diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index ece01c6d6d..76b78c0122 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -498,6 +498,7 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMSettingBridge *s_bridge; const char *mac_address_str; guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX]; + NMPlatformError plerr; g_assert (iface); @@ -510,17 +511,17 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, mac_address_str = NULL; } - if ( !nm_platform_bridge_add (NM_PLATFORM_GET, + plerr = nm_platform_bridge_add (NM_PLATFORM_GET, iface, mac_address_str ? mac_address : NULL, mac_address_str ? ETH_ALEN : 0, - NULL) - && nm_platform_get_error (NM_PLATFORM_GET) != NM_PLATFORM_ERROR_EXISTS) { + NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create bridge interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_get_error_msg (NM_PLATFORM_GET)); + nm_platform_error_to_string (plerr)); return NULL; } diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index e901b91a13..020dc53346 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -326,6 +326,7 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMSettingInfiniband *s_infiniband; int p_key, parent_ifindex; const char *iface; + NMPlatformError plerr; if (!NM_IS_DEVICE_INFINIBAND (parent)) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, @@ -342,13 +343,13 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, parent_ifindex = nm_device_get_ifindex (parent); p_key = nm_setting_infiniband_get_p_key (s_infiniband); - if ( !nm_platform_infiniband_partition_add (NM_PLATFORM_GET, parent_ifindex, p_key, NULL) - && nm_platform_get_error (NM_PLATFORM_GET) != NM_PLATFORM_ERROR_EXISTS) { + plerr = nm_platform_infiniband_partition_add (NM_PLATFORM_GET, parent_ifindex, p_key, NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create InfiniBand P_Key interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_get_error_msg (NM_PLATFORM_GET)); + nm_platform_error_to_string (plerr)); return NULL; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 94827e4c17..158e7de5bf 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -656,6 +656,7 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMDevice *device; NMSettingVlan *s_vlan; gs_free char *iface = NULL; + NMPlatformError plerr; if (!NM_IS_DEVICE (parent)) { g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, @@ -672,18 +673,18 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, nm_setting_vlan_get_id (s_vlan)); } - if ( !nm_platform_vlan_add (NM_PLATFORM_GET, + plerr = nm_platform_vlan_add (NM_PLATFORM_GET, iface, nm_device_get_ifindex (parent), nm_setting_vlan_get_id (s_vlan), nm_setting_vlan_get_flags (s_vlan), - NULL) - && nm_platform_get_error (NM_PLATFORM_GET) != NM_PLATFORM_ERROR_EXISTS) { + NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create VLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_get_error_msg (NM_PLATFORM_GET)); + nm_platform_error_to_string (plerr)); return NULL; } diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 4256bec2b4..aedeebfdb2 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -689,16 +689,17 @@ NMDevice * nm_device_team_new_for_connection (NMConnection *connection, GError **error) { const char *iface = nm_connection_get_interface_name (connection); + NMPlatformError plerr; g_assert (iface); - if ( !nm_platform_team_add (NM_PLATFORM_GET, iface, NULL) - && nm_platform_get_error (NM_PLATFORM_GET) != NM_PLATFORM_ERROR_EXISTS) { + plerr = nm_platform_team_add (NM_PLATFORM_GET, iface, NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create team master interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_get_error_msg (NM_PLATFORM_GET)); + nm_platform_error_to_string (plerr)); return NULL; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 21118a889e..b95eb5106e 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -573,6 +573,38 @@ nm_platform_link_get_by_address (NMPlatform *self, return !!klass->link_get_by_address (self, address, length, link); } +static NMPlatformError +_link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, NMPlatformLink *out_link) +{ + int ifindex; + NMPlatformLink pllink; + + ifindex = nm_platform_link_get_ifindex (self, name); + if (ifindex > 0) { + if (nm_platform_link_get (self, ifindex, &pllink)) { + gboolean wrong_type; + + wrong_type = type != NM_LINK_TYPE_NONE && pllink.type != type; + debug ("link: skip adding link due to existing interface '%s' of type %s%s%s", + name, + nm_link_type_to_string (pllink.type), + wrong_type ? ", expected " : "", + wrong_type ? nm_link_type_to_string (type) : ""); + if (out_link) + *out_link = pllink; + if (wrong_type) { + nm_platform_set_error (self, NM_PLATFORM_ERROR_WRONG_TYPE); + return NM_PLATFORM_ERROR_WRONG_TYPE; + } + nm_platform_set_error (self, NM_PLATFORM_ERROR_EXISTS); + return NM_PLATFORM_ERROR_EXISTS; + } + /* strange, nm_platform_link_get_ifindex() returned a valid ifindex, but nm_platform_link_get() failed. + * This is unexpected... proceed with "SUCCESS". */ + } + return NM_PLATFORM_ERROR_SUCCESS; +} + /** * nm_platform_link_add: * @self: platform instance @@ -583,12 +615,16 @@ nm_platform_link_get_by_address (NMPlatform *self, * @out_link: on success, the link object * * Add a software interface. If the interface already exists and is of type - * @type, sets platform->error to NM_PLATFORM_ERROR_EXISTS and returns the link + * @type, return NM_PLATFORM_ERROR_EXISTS and returns the link * in @out_link. If the interface already exists and is not of type @type, - * sets platform->error to NM_PLATFORM_ERROR_WRONG_TYPE. Any link-changed ADDED - * signal will be emitted directly, before this function finishes. + * return NM_PLATFORM_ERROR_WRONG_TYPE. + * + * Any link-changed ADDED signal will be emitted directly, before this + * function finishes. + * + * Returns: the error reason or NM_PLATFORM_ERROR_SUCCESS. */ -static gboolean +static NMPlatformError nm_platform_link_add (NMPlatform *self, const char *name, NMLinkType type, @@ -596,29 +632,27 @@ nm_platform_link_add (NMPlatform *self, size_t address_len, NMPlatformLink *out_link) { - int ifindex; + NMPlatformError plerr; - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); reset_error (self); - g_return_val_if_fail (name, FALSE); - g_return_val_if_fail (klass->link_add, FALSE); - g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , FALSE); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->link_add, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); - ifindex = nm_platform_link_get_ifindex (self, name); - if (ifindex > 0) { - debug ("link: already exists"); - if (nm_platform_link_get_type (self, ifindex) != type) - self->error = NM_PLATFORM_ERROR_WRONG_TYPE; - else { - self->error = NM_PLATFORM_ERROR_EXISTS; - (void) nm_platform_link_get (self, ifindex, out_link); - } - return FALSE; - } + plerr = _link_add_check_existing (self, name, type, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + debug ("link: adding %s '%s'", nm_link_type_to_string (type), name); reset_error(self); - return klass->link_add (self, name, type, address, address_len, out_link); + if (!klass->link_add (self, name, type, address, address_len, out_link)) { + nm_platform_set_error (self, NM_PLATFORM_ERROR_UNSPECIFIED); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } + reset_error (self); + return NM_PLATFORM_ERROR_SUCCESS; } /** @@ -629,12 +663,9 @@ nm_platform_link_add (NMPlatform *self, * * Create a software ethernet-like interface */ -gboolean +NMPlatformError nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - g_return_val_if_fail (name, FALSE); - - debug ("link: adding dummy '%s'", name); return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, 0, out_link); } @@ -1416,14 +1447,13 @@ nm_platform_link_get_master (NMPlatform *self, int slave) * * Create a software bridge. */ -gboolean +NMPlatformError nm_platform_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, NMPlatformLink *out_link) { - debug ("link: adding bridge '%s'", name); return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, address, address_len, out_link); } @@ -1435,10 +1465,9 @@ nm_platform_bridge_add (NMPlatform *self, * * Create a software bonding device. */ -gboolean +NMPlatformError nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - debug ("link: adding bond '%s'", name); return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, 0, out_link); } @@ -1450,10 +1479,9 @@ nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_li * * Create a software teaming device. */ -gboolean +NMPlatformError nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - debug ("link: adding team '%s'", name); return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, 0, out_link); } @@ -1467,7 +1495,7 @@ nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_li * * Create a software VLAN device. */ -gboolean +NMPlatformError nm_platform_vlan_add (NMPlatform *self, const char *name, int parent, @@ -1475,23 +1503,25 @@ nm_platform_vlan_add (NMPlatform *self, guint32 vlanflags, NMPlatformLink *out_link) { - _CHECK_SELF (self, klass, FALSE); + NMPlatformError plerr; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); reset_error (self); - g_return_val_if_fail (parent >= 0, FALSE); - g_return_val_if_fail (vlanid >= 0, FALSE); - g_return_val_if_fail (name, FALSE); - g_return_val_if_fail (klass->vlan_add, FALSE); + g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (vlanid >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->vlan_add, NM_PLATFORM_ERROR_BUG); - if (nm_platform_link_exists (self, name)) { - debug ("link already exists: %s", name); - self->error = NM_PLATFORM_ERROR_EXISTS; - return FALSE; - } + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VLAN, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; debug ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x", - name, parent, vlanid, vlanflags); - return klass->vlan_add (self, name, parent, vlanid, vlanflags, out_link); + name, parent, vlanid, vlanflags); + if (!klass->vlan_add (self, name, parent, vlanid, vlanflags, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } gboolean @@ -1591,35 +1621,37 @@ nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to return klass->vlan_set_egress_map (self, ifindex, from, to); } -gboolean +NMPlatformError nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link) { - const char *parent_name; - char *name; + gs_free char *parent_name = NULL; + gs_free char *name = NULL; + NMPlatformError plerr; - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); reset_error (self); - g_return_val_if_fail (parent >= 0, FALSE); - g_return_val_if_fail (p_key >= 0, FALSE); - g_return_val_if_fail (klass->infiniband_partition_add, FALSE); + g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (p_key >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->infiniband_partition_add, NM_PLATFORM_ERROR_BUG); - if (nm_platform_link_get_type (self, parent) != NM_LINK_TYPE_INFINIBAND) { + parent_name = g_strdup (nm_platform_link_get_name (self, parent)); + if ( !parent_name + || nm_platform_link_get_type (self, parent) != NM_LINK_TYPE_INFINIBAND) { self->error = NM_PLATFORM_ERROR_WRONG_TYPE; - return FALSE; + return NM_PLATFORM_ERROR_WRONG_TYPE; } - parent_name = nm_platform_link_get_name (self, parent); name = g_strdup_printf ("%s.%04x", parent_name, p_key); - if (nm_platform_link_exists (self, name)) { - debug ("infiniband: already exists"); - self->error = NM_PLATFORM_ERROR_EXISTS; - g_free (name); - return FALSE; - } - g_free (name); + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; - return klass->infiniband_partition_add (self, parent, p_key, out_link); + debug ("link: adding infiniband partition %s for parent '%s' (%d), key %d", + name, parent_name, parent, p_key); + if (!klass->infiniband_partition_add (self, parent, p_key, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } gboolean diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 34cd6a38af..48d6802e27 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -604,10 +604,10 @@ gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char gboolean nm_platform_link_get (NMPlatform *self, int ifindex, NMPlatformLink *link); GArray *nm_platform_link_get_all (NMPlatform *self); gboolean nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length, NMPlatformLink *link); -gboolean nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); -gboolean nm_platform_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, NMPlatformLink *out_link); -gboolean nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); -gboolean nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); +NMPlatformError nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); +NMPlatformError nm_platform_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, NMPlatformLink *out_link); +NMPlatformError nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); +NMPlatformError nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); gboolean nm_platform_link_exists (NMPlatform *self, const char *name); gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); @@ -662,12 +662,12 @@ char *nm_platform_master_get_option (NMPlatform *self, int ifindex, const char * gboolean nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); char *nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option); -gboolean nm_platform_vlan_add (NMPlatform *self, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); +NMPlatformError nm_platform_vlan_add (NMPlatform *self, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); gboolean nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid); gboolean nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to); gboolean nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to); -gboolean nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link); +NMPlatformError nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link); gboolean nm_platform_infiniband_get_info (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); gboolean nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *properties); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index d46d140abd..c025642f2e 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -93,25 +93,25 @@ do_link_get_all (char **argv) static gboolean do_dummy_add (char **argv) { - return nm_platform_dummy_add (NM_PLATFORM_GET, argv[0], NULL); + return nm_platform_dummy_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_bridge_add (char **argv) { - return nm_platform_bridge_add (NM_PLATFORM_GET, argv[0], NULL, 0, NULL); + return nm_platform_bridge_add (NM_PLATFORM_GET, argv[0], NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_bond_add (char **argv) { - return nm_platform_bond_add (NM_PLATFORM_GET, argv[0], NULL); + return nm_platform_bond_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_team_add (char **argv) { - return nm_platform_team_add (NM_PLATFORM_GET, argv[0], NULL); + return nm_platform_team_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -122,7 +122,7 @@ do_vlan_add (char **argv) int vlanid = strtol (*argv++, NULL, 10); guint32 vlan_flags = strtol (*argv++, NULL, 10); - return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent, vlanid, vlan_flags, NULL); + return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent, vlanid, vlan_flags, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 37a7c3bc58..2a8971c310 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -255,7 +255,7 @@ setup_tests (void) nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 75c057c89b..cc3b615030 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -35,7 +35,7 @@ test_cleanup_internal (void) inet_pton (AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6); /* Create and set up device */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL)); + g_assert (nm_platform_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))); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 6d88bd4240..4742a85bdb 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -85,36 +85,37 @@ test_loopback (void) g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, LO_INDEX)); } -static int +static gboolean software_add (NMLinkType link_type, const char *name) { switch (link_type) { case NM_LINK_TYPE_DUMMY: - return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL); + return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BRIDGE: - return nm_platform_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL); + return nm_platform_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BOND: { gboolean bond0_exists = nm_platform_link_exists (NM_PLATFORM_GET, "bond0"); - gboolean result = nm_platform_bond_add (NM_PLATFORM_GET, name, NULL); - NMPlatformError error = nm_platform_get_error (NM_PLATFORM_GET); + NMPlatformError plerr; + + plerr = nm_platform_bond_add (NM_PLATFORM_GET, name, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, "bond0")); - nm_platform_set_error (NM_PLATFORM_GET, error); - return result; + nm_platform_set_error (NM_PLATFORM_GET, plerr); + return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: - return nm_platform_team_add (NM_PLATFORM_GET, name, NULL); + return nm_platform_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_VLAN: { SignalData *parent_added; SignalData *parent_changed; /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); - if (nm_platform_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL)) + if (nm_platform_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) accept_signal (parent_added); free_signal (parent_added); @@ -131,7 +132,7 @@ software_add (NMLinkType link_type, const char *name) accept_signal (parent_changed); free_signal (parent_changed); - return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL); + return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } } default: @@ -440,12 +441,12 @@ test_internal (void) error (NM_PLATFORM_ERROR_NOT_FOUND); /* Add device */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); no_error (); accept_signal (link_added); /* Try to add again */ - g_assert (!nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); error (NM_PLATFORM_ERROR_EXISTS); /* Check device index, name and type */ diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 90406f8ed7..595f462881 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -323,7 +323,7 @@ setup_tests (void) nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index 9b5f187232..c3eca9b672 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -648,7 +648,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data) "nm-test-device0"); nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0")); g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"); @@ -660,7 +660,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data) "nm-test-device1"); nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1")); g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"); From 8334171a4e8c423f524ea530e8bd1b4ff40d9e6e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 17:25:04 +0200 Subject: [PATCH 47/50] device: don't check for NM_PLATFORM_ERROR_NOT_FOUND in set_nm_ipv6ll() Unconditionally log a warning if the function fails. We are about to drop nm_platform_get_error(), it's anyway unclear why we don't want to log a warning about non-existing interface. --- src/devices/nm-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 04472847be..59056b5bca 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -4723,8 +4723,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) const char *detail = enable ? "enable" : "disable"; _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); - if ( !nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable) - && nm_platform_get_error (NM_PLATFORM_GET) != NM_PLATFORM_ERROR_NOT_FOUND) + if (!nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable)) _LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail); if (enable) { From c1a945b95d323fd2612179b80f03121f3c4591cb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 17:41:27 +0200 Subject: [PATCH 48/50] platform: signal missing firmware in nm_platform_set_up() Don't use nm_platform_get_error() anymore. --- src/devices/adsl/nm-device-adsl.c | 2 +- src/devices/nm-device.c | 10 +++----- src/main.c | 2 +- src/platform/nm-fake-platform.c | 5 +++- src/platform/nm-linux-platform.c | 39 ++++++++++++++++------------- src/platform/nm-platform.c | 5 ++-- src/platform/nm-platform.h | 4 +-- src/platform/tests/platform.c | 9 ++++++- src/platform/tests/test-address.c | 2 +- src/platform/tests/test-cleanup.c | 2 +- src/platform/tests/test-link.c | 10 ++++---- src/platform/tests/test-route.c | 2 +- src/tests/test-route-manager.c | 4 +-- src/vpn-manager/nm-vpn-connection.c | 2 +- 14 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index fe26adbfcf..6c4db8dbf3 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -352,7 +352,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason) _LOGD (LOGD_ADSL, "ATM setup successful"); /* otherwise we're good for stage3 */ - nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL); ret = NM_ACT_STAGE_RETURN_SUCCESS; } else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) { diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 59056b5bca..535a666c20 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -598,7 +598,7 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ip_ifindex, TRUE); if (!nm_platform_link_is_up (NM_PLATFORM_GET, priv->ip_ifindex)) - nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL); } else { /* Device IP interface must always be a kernel network interface */ _LOGW (LOGD_HW, "failed to look up interface index"); @@ -5527,7 +5527,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) { - nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL); if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex)) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } @@ -5648,7 +5648,7 @@ nm_device_activate_ip6_config_commit (gpointer user_data) /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) { - nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL); if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex)) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } @@ -6792,9 +6792,7 @@ bring_up (NMDevice *self, gboolean *no_firmware) return TRUE; } - result = nm_platform_link_set_up (NM_PLATFORM_GET, ifindex); - if (no_firmware) - *no_firmware = nm_platform_get_error (NM_PLATFORM_GET) == NM_PLATFORM_ERROR_NO_FIRMWARE; + result = nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, no_firmware); /* Store carrier immediately. */ if (result && nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) diff --git a/src/main.c b/src/main.c index 61e58a8ad5..b758d33a12 100644 --- a/src/main.c +++ b/src/main.c @@ -477,7 +477,7 @@ main (int argc, char *argv[]) * physical interfaces. */ nm_log_dbg (LOGD_CORE, "setting up local loopback"); - nm_platform_link_set_up (NM_PLATFORM_GET, 1); + nm_platform_link_set_up (NM_PLATFORM_GET, 1, NULL); success = TRUE; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 36c6898b48..ff97d551c1 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -363,11 +363,14 @@ link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_s } static gboolean -link_set_up (NMPlatform *platform, int ifindex) +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; + if (!device) return FALSE; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d12b87b6a4..389208604f 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2697,7 +2697,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl return nle >= 0; } -static gboolean +static NMPlatformError do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache) { int nle; @@ -2706,7 +2706,7 @@ do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_f ifindex = rtnl_link_get_ifindex (nlo); if (ifindex <= 0) - g_return_val_if_reached (FALSE); + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); nle = kernel_change_link (platform, nlo, &complete_from_cache2); @@ -2726,13 +2726,13 @@ do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_f _LOGD ("do-change-link: failure changing link %d: link does not exist in cache", ifindex); } else _LOGE ("do-change-link: failure changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); - return FALSE; + return nle == -NLE_OBJ_NOTFOUND ? NM_PLATFORM_ERROR_NO_FIRMWARE : NM_PLATFORM_ERROR_UNSPECIFIED; } /* FIXME: as we modify the link via a separate socket, the cache is not in * sync and we have to refetch the link. */ do_request_link (platform, ifindex, NULL, TRUE); - return TRUE; + return NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -2894,7 +2894,7 @@ link_uses_arp (NMPlatform *platform, int ifindex) return !(link_get_flags (platform, ifindex) & IFF_NOARP); } -static gboolean +static NMPlatformError link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value) { auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); @@ -2903,7 +2903,7 @@ link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboole obj_cache = cache_lookup_link (platform, ifindex); if (!obj_cache) - return FALSE; + return NM_PLATFORM_ERROR_NOT_FOUND; rtnl_link_set_flags (change, obj_cache->link.flags); if (value) @@ -2920,27 +2920,32 @@ link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboole } static gboolean -link_set_up (NMPlatform *platform, int ifindex) +link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { - return link_change_flags (platform, ifindex, IFF_UP, TRUE); + NMPlatformError plerr; + + plerr = link_change_flags (platform, ifindex, IFF_UP, TRUE); + if (out_no_firmware) + *out_no_firmware = plerr == NM_PLATFORM_ERROR_NO_FIRMWARE; + return plerr == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_down (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_UP, FALSE); + return link_change_flags (platform, ifindex, IFF_UP, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_arp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, FALSE); + return link_change_flags (platform, ifindex, IFF_NOARP, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_noarp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, TRUE); + return link_change_flags (platform, ifindex, IFF_NOARP, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -3007,7 +3012,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable rtnl_link_inet6_set_addr_gen_mode (nlo, mode); debug ("link: change %d: set IPv6 address generation mode to %s", ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf))); - return do_change_link (platform, nlo, TRUE); + return do_change_link (platform, nlo, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } #endif return FALSE; @@ -3054,7 +3059,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size _LOGD ("link: change %d: address %s (%lu bytes)", ifindex, (mac = nm_utils_hwaddr_ntoa (address, length)), (unsigned long) length); - return do_change_link (platform, change, TRUE); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gconstpointer @@ -3096,7 +3101,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) rtnl_link_set_mtu (change, mtu); debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu); - return do_change_link (platform, change, TRUE); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static guint32 @@ -3206,7 +3211,7 @@ vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) debug ("link: change %d: vlan ingress map %d -> %d", ifindex, from, to); - return do_change_link (platform, change, TRUE); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -3219,7 +3224,7 @@ vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) debug ("link: change %d: vlan egress map %d -> %d", ifindex, from, to); - return do_change_link (platform, change, TRUE); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -3230,7 +3235,7 @@ link_enslave (NMPlatform *platform, int master, int slave) rtnl_link_set_master (change, master); debug ("link: change %d: enslave to master %d", slave, master); - return do_change_link (platform, change, TRUE); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index b95eb5106e..3118f11291 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1143,11 +1143,12 @@ nm_platform_link_supports_vlans (NMPlatform *self, int ifindex) * nm_platform_link_set_up: * @self: platform instance * @ifindex: Interface index + * @out_no_firmware: (allow-none): if the failure reason is due to missing firmware. * * Bring the interface up. */ gboolean -nm_platform_link_set_up (NMPlatform *self, int ifindex) +nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware) { _CHECK_SELF (self, klass, FALSE); reset_error (self); @@ -1156,7 +1157,7 @@ nm_platform_link_set_up (NMPlatform *self, int ifindex) g_return_val_if_fail (klass->link_set_up, FALSE); debug ("link: setting up '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); - return klass->link_set_up (self, ifindex); + return klass->link_set_up (self, ifindex, out_no_firmware); } /** diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 48d6802e27..57a97bcd12 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -429,7 +429,7 @@ typedef struct { gboolean (*link_refresh) (NMPlatform *, int ifindex); - gboolean (*link_set_up) (NMPlatform *, int ifindex); + gboolean (*link_set_up) (NMPlatform *, int ifindex, gboolean *out_no_firmware); gboolean (*link_set_down) (NMPlatform *, int ifindex); gboolean (*link_set_arp) (NMPlatform *, int ifindex); gboolean (*link_set_noarp) (NMPlatform *, int ifindex); @@ -620,7 +620,7 @@ gboolean nm_platform_link_supports_slaves (NMPlatform *self, int ifindex); gboolean nm_platform_link_refresh (NMPlatform *self, int ifindex); -gboolean nm_platform_link_set_up (NMPlatform *self, int ifindex); +gboolean nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware); gboolean nm_platform_link_set_down (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_arp (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_noarp (NMPlatform *self, int ifindex); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index c025642f2e..ee48a9d7cb 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -182,7 +182,14 @@ LINK_CMD_GET_FULL (get_type, decimal, value > 0) LINK_CMD_GET (is_software, boolean) LINK_CMD_GET (supports_slaves, boolean) -LINK_CMD (set_up) +static gboolean +do_link_set_up (char **argv) +{ + int ifindex = parse_ifindex (argv[0]); + + return ifindex ? nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL) : FALSE; +} + LINK_CMD (set_down) LINK_CMD (set_arp) LINK_CMD (set_noarp) diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 2a8971c310..06cd3aac14 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -175,7 +175,7 @@ test_ip4_address_external (void) /* Looks like addresses are not announced by kerenl when the interface * is down. Link-local IPv6 address is automatically added. */ - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); /* Add/delete notification */ run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index cc3b615030..640d3a6f95 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -38,7 +38,7 @@ test_cleanup_internal (void) g_assert (nm_platform_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))); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex > 0); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 4742a85bdb..40b4731b66 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -34,7 +34,7 @@ test_bogus(void) g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX)); error (NM_PLATFORM_ERROR_NOT_FOUND); @@ -124,7 +124,7 @@ software_add (NMLinkType link_type, const char *name) gboolean was_up = nm_platform_link_is_up (NM_PLATFORM_GET, parent_ifindex); parent_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, parent_ifindex)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, parent_ifindex, NULL)); if (was_up) { /* when NM is running in the background, it will mess with addrgenmode which might cause additional signals. */ accept_signals (parent_changed, 0, 1); @@ -183,7 +183,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); /* Set master up */ - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, master)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, master, NULL)); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, master)); accept_signals (master_changed, 1, 2); @@ -220,7 +220,7 @@ test_slave (int master, int type, SignalData *master_changed) } /* Set slave up and see if master gets up too */ - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex)); no_error (); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); no_error (); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); @@ -461,7 +461,7 @@ test_internal (void) /* Up/connected */ g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); no_error (); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); no_error (); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex)); no_error (); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); no_error (); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); no_error (); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); no_error (); accept_signal (link_changed); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 595f462881..1bf18ff12a 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -327,7 +327,7 @@ setup_tests (void) 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))); + 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); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index c3eca9b672..85397ae7fb 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -652,7 +652,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data) accept_signal (link_added); free_signal (link_added); fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, @@ -664,7 +664,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data) accept_signal (link_added); free_signal (link_added); fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); } static void diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 4329cb29f8..33fde937b6 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -921,7 +921,7 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection) NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); if (priv->ip_ifindex > 0) { - nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL); if (priv->ip4_config) { if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex, From baec89413916e9c7891a1e7a8e749b597a9c2a9d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 17:56:28 +0200 Subject: [PATCH 49/50] device: drop logging platform error on failure to set permanent MAC address The platform error is in many cases not meaningful anyway. --- src/devices/nm-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 535a666c20..9531ff2611 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -8771,8 +8771,7 @@ constructed (GObject *object) priv->perm_hw_addr); } else { /* Fall back to current address */ - _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)", - nm_platform_get_error (NM_PLATFORM_GET)); + _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address"); priv->perm_hw_addr = g_strdup (priv->hw_addr); } } From 68a4ffb4e2e38f9476e51da75770239493cb47b7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 15 Jun 2015 17:58:36 +0200 Subject: [PATCH 50/50] platform: drop nm_platform_get_error() For NMPlatform instances we had an error reporting mechanism which stores the last error reason in a private field. Later we would check it via nm_platform_get_error(). Remove this. It was not used much, and it is not a great way to report errors. One problem is that at the point where the error happens, you don't know whether anybody cares about an error code. So, you add code to set the error reason because somebody *might* need it (but in realitiy, almost no caller cares). Also, we tested this functionality which is hardly used in non-testing code. While this was a burden to maintain in the tests, it was likely still buggy because there were no real use-cases, beside the tests. Then, sometimes platform functions call each other which might overwrite the error reason. So, every function must be cautious to preserve/set the error reason according to it's own meaning. This can involve storing the error code, calling another function, and restoring it afterwards. This is harder to get right compared to a "return-error-code" pattern, where every function manages its error code independently. It is better to return the error reason whenever due. For that we already have our common glib patterns (1) gboolean fcn (...); (2) gboolean fcn (..., GError **error); In few cases, we need more details then a #gboolean, but don't want to bother constructing a #GError. Then we should do instead: (3) NMPlatformError fcn (...); --- src/platform/nm-fake-platform.c | 5 +- src/platform/nm-linux-platform.c | 38 +++---- src/platform/nm-platform.c | 168 ++---------------------------- src/platform/nm-platform.h | 5 - src/platform/tests/platform.c | 10 +- src/platform/tests/test-address.c | 18 ---- src/platform/tests/test-common.h | 3 - src/platform/tests/test-link.c | 75 +++---------- src/platform/tests/test-route.c | 28 ----- 9 files changed, 38 insertions(+), 312 deletions(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index ff97d551c1..1eac40d9e2 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -160,7 +160,6 @@ link_get (NMPlatform *platform, int ifindex) return device; not_found: debug ("link not found: %d", ifindex); - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; return NULL; } @@ -649,10 +648,8 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) g_return_val_if_fail (master, FALSE); g_return_val_if_fail (slave, FALSE); - if (slave->link.master != master->link.ifindex) { - platform->error = NM_PLATFORM_ERROR_NOT_SLAVE; + if (slave->link.master != master->link.ifindex) return FALSE; - } slave->link.master = 0; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 389208604f..46fad57941 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2477,10 +2477,8 @@ 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)) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + if (!nmp_object_is_visible (obj_cache)) return NULL; - } return obj_cache; } @@ -2718,13 +2716,11 @@ do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_f _LOGD ("do-change-link: success changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); break; case -NLE_OBJ_NOTFOUND: - platform->error = NM_PLATFORM_ERROR_NO_FIRMWARE; /* fall-through */ default: - if (complete_from_cache != complete_from_cache2) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + if (complete_from_cache != complete_from_cache2) _LOGD ("do-change-link: failure changing link %d: link does not exist in cache", ifindex); - } else + else _LOGE ("do-change-link: failure changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); return nle == -NLE_OBJ_NOTFOUND ? NM_PLATFORM_ERROR_NO_FIRMWARE : NM_PLATFORM_ERROR_UNSPECIFIED; } @@ -2780,10 +2776,8 @@ link_delete (NMPlatform *platform, int ifindex) const NMPObject *obj; obj = nmp_cache_lookup_link (priv->cache, ifindex); - if (!obj || !obj->_link.netlink.is_in_netlink) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; - } nmp_object_stackinit_id_link (&obj_needle, ifindex); return do_delete_object (platform, &obj_needle, NULL); @@ -2798,8 +2792,6 @@ link_get_ifindex (NMPlatform *platform, const char *ifname) obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); - if (!obj) - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; return obj ? obj->link.ifindex : 0; } @@ -2951,15 +2943,15 @@ link_set_noarp (NMPlatform *platform, int ifindex) static gboolean link_get_ipv6_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId *iid) { +#if HAVE_LIBNL_INET6_TOKEN const NMPObject *obj = cache_lookup_link (platform, ifindex); - /* Always lookup @obj, because it properly sets nm_platform_get_error(). - * Otherwise, we could save this '#ifndef HAVE_LIBNL_INET6_TOKEN' */ - if (!obj || !obj->link.inet6_token.is_valid) - return FALSE; - - *iid = obj->link.inet6_token.iid; - return TRUE; + if (obj && obj->link.inet6_token.is_valid) { + *iid = obj->link.inet6_token.iid; + return TRUE; + } +#endif + return FALSE; } static const char * @@ -2991,12 +2983,12 @@ link_get_udev_device (NMPlatform *platform, int ifindex) static gboolean link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) { +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE const NMPObject *obj = cache_lookup_link (platform, ifindex); - /* Always lookup @obj, because it properly sets nm_platform_get_error(). - * Otherwise, we could save this '#ifndef HAVE_LIBNL_INET6_ADDR_GEN_MODE' */ if (obj && obj->link.inet6_addr_gen_mode_inv) return (~obj->link.inet6_addr_gen_mode_inv) == IN6_ADDR_GEN_MODE_NONE; +#endif return FALSE; } @@ -3301,10 +3293,8 @@ slave_category (NMPlatform *platform, int slave) { int master = link_get_master (platform, slave); - if (master <= 0) { - platform->error = NM_PLATFORM_ERROR_NOT_SLAVE; + if (master <= 0) return NULL; - } switch (link_get_type (platform, master)) { case NM_LINK_TYPE_BRIDGE: diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 3118f11291..230d7580ad 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -145,39 +145,6 @@ nm_platform_try_get (void) /******************************************************************/ -/** - * nm_platform_set_error: - * @self: platform instance - * @error: The error code - * - * Convenience function to falsify self->error. It can be used for example - * by functions that want to save the error, execute some operations and - * restore it. - */ -void nm_platform_set_error (NMPlatform *self, NMPlatformError error) -{ - _CHECK_SELF_VOID (self, klass); - - self->error = error; -} - -/** - * nm_platform_get_error: - * @self: platform instance - * - * Convenience function to quickly retrieve the error code of the last - * operation. - * - * Returns: Integer error code. - */ -NMPlatformError -nm_platform_get_error (NMPlatform *self) -{ - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - - return self->error; -} - /** * nm_platform_error_to_string: * @error_code: the error code to stringify. @@ -213,26 +180,7 @@ nm_platform_error_to_string (NMPlatformError error) } } -/** - * nm_platform_get_error_message: - * @self: platform instance - * - * Returns: Static human-readable string for the error. Don't free. - */ -const char * -nm_platform_get_error_msg (NMPlatform *self) -{ - _CHECK_SELF (self, klass, NULL); - - return nm_platform_error_to_string (self->error); -} - -static void -reset_error (NMPlatform *self) -{ - g_assert (self); - self->error = NM_PLATFORM_ERROR_SUCCESS; -} +/******************************************************************/ #define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" #define IFA_F_NOPREFIXROUTE_STR "noprefixroute" @@ -299,8 +247,6 @@ nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value) g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->sysctl_set, FALSE); - reset_error (self); - return klass->sysctl_set (self, path, value); } @@ -351,8 +297,6 @@ nm_platform_sysctl_get (NMPlatform *self, const char *path) g_return_val_if_fail (path, NULL); g_return_val_if_fail (klass->sysctl_get, NULL); - reset_error (self); - return klass->sysctl_get (self, path); } @@ -430,7 +374,6 @@ nm_platform_link_get_all (NMPlatform *self) NMPlatformLink *item; _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (klass->link_get_all, NULL); @@ -592,11 +535,8 @@ _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, N wrong_type ? nm_link_type_to_string (type) : ""); if (out_link) *out_link = pllink; - if (wrong_type) { - nm_platform_set_error (self, NM_PLATFORM_ERROR_WRONG_TYPE); + if (wrong_type) return NM_PLATFORM_ERROR_WRONG_TYPE; - } - nm_platform_set_error (self, NM_PLATFORM_ERROR_EXISTS); return NM_PLATFORM_ERROR_EXISTS; } /* strange, nm_platform_link_get_ifindex() returned a valid ifindex, but nm_platform_link_get() failed. @@ -635,7 +575,6 @@ nm_platform_link_add (NMPlatform *self, NMPlatformError plerr; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - reset_error (self); g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (klass->link_add, NM_PLATFORM_ERROR_BUG); @@ -646,12 +585,8 @@ nm_platform_link_add (NMPlatform *self, return plerr; debug ("link: adding %s '%s'", nm_link_type_to_string (type), name); - reset_error(self); - if (!klass->link_add (self, name, type, address, address_len, out_link)) { - nm_platform_set_error (self, NM_PLATFORM_ERROR_UNSPECIFIED); + if (!klass->link_add (self, name, type, address, address_len, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; - } - reset_error (self); return NM_PLATFORM_ERROR_SUCCESS; } @@ -685,7 +620,6 @@ nm_platform_link_exists (NMPlatform *self, const char *name) ifindex = nm_platform_link_get_ifindex (self, name); - reset_error (self); return ifindex > 0; } @@ -693,9 +627,6 @@ nm_platform_link_exists (NMPlatform *self, const char *name) * nm_platform_link_delete: * @self: platform instance * @ifindex: Interface index - * - * Delete a software interface. Sets self->error to - * NM_PLATFORM_ERROR_NOT_FOUND if ifindex not available. */ gboolean nm_platform_link_delete (NMPlatform *self, int ifindex) @@ -703,7 +634,6 @@ nm_platform_link_delete (NMPlatform *self, int ifindex) const char *name; _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->link_delete, FALSE); @@ -730,17 +660,14 @@ nm_platform_link_get_ifindex (NMPlatform *self, const char *name) int ifindex; _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (name, 0); g_return_val_if_fail (klass->link_get_ifindex, 0); ifindex = klass->link_get_ifindex (self, name); - if (!ifindex) { + if (!ifindex) debug ("link not found: %s", name); - self->error = NM_PLATFORM_ERROR_NOT_FOUND; - } return ifindex; } @@ -759,7 +686,6 @@ nm_platform_link_get_name (NMPlatform *self, int ifindex) const char *name; _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (klass->link_get_name, NULL); @@ -767,7 +693,6 @@ nm_platform_link_get_name (NMPlatform *self, int ifindex) if (!name) { debug ("link not found: %d", ifindex); - self->error = NM_PLATFORM_ERROR_NOT_FOUND; return FALSE; } @@ -786,7 +711,6 @@ NMLinkType nm_platform_link_get_type (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NM_LINK_TYPE_NONE); - reset_error (self); g_return_val_if_fail (klass->link_get_type, NM_LINK_TYPE_NONE); @@ -806,7 +730,6 @@ const char * nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (klass->link_get_type_name, NULL); @@ -827,7 +750,6 @@ gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *managed) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->link_get_unmanaged, FALSE); @@ -873,7 +795,6 @@ gboolean nm_platform_link_refresh (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); @@ -894,7 +815,6 @@ gboolean nm_platform_link_is_up (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_is_up, FALSE); @@ -913,7 +833,6 @@ gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_is_connected, FALSE); @@ -932,7 +851,6 @@ gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_uses_arp, FALSE); @@ -956,7 +874,6 @@ gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (iid, FALSE); @@ -970,7 +887,6 @@ const char * nm_platform_link_get_udi (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, NULL); @@ -983,7 +899,6 @@ GObject * nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, NULL); @@ -1007,7 +922,6 @@ gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); @@ -1032,7 +946,6 @@ gboolean nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); @@ -1054,7 +967,6 @@ gboolean nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (address, FALSE); @@ -1078,7 +990,6 @@ gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) { _CHECK_SELF (self, klass, NULL); - reset_error (self); if (length) *length = 0; @@ -1104,7 +1015,6 @@ gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); if (length) *length = 0; @@ -1151,7 +1061,6 @@ gboolean nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (klass->link_set_up, FALSE); @@ -1171,7 +1080,6 @@ gboolean nm_platform_link_set_down (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (klass->link_set_down, FALSE); @@ -1191,7 +1099,6 @@ gboolean nm_platform_link_set_arp (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_set_arp, FALSE); @@ -1211,7 +1118,6 @@ gboolean nm_platform_link_set_noarp (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_set_noarp, FALSE); @@ -1232,7 +1138,6 @@ gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (mtu > 0, FALSE); @@ -1253,7 +1158,6 @@ guint32 nm_platform_link_get_mtu (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex >= 0, 0); g_return_val_if_fail (klass->link_get_mtu, 0); @@ -1279,7 +1183,6 @@ char * nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex >= 0, NULL); g_return_val_if_fail (klass->link_get_physical_port_id, NULL); @@ -1303,7 +1206,6 @@ guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex >= 0, 0); g_return_val_if_fail (klass->link_get_dev_id, 0); @@ -1322,7 +1224,6 @@ gboolean nm_platform_link_get_wake_on_lan (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_get_wake_on_lan, FALSE); @@ -1350,7 +1251,6 @@ nm_platform_link_get_driver_info (NMPlatform *self, char **out_fw_version) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_get_driver_info, FALSE); @@ -1374,7 +1274,6 @@ gboolean nm_platform_link_enslave (NMPlatform *self, int master, int slave) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (master > 0, FALSE); g_return_val_if_fail (slave> 0, FALSE); @@ -1398,16 +1297,13 @@ gboolean nm_platform_link_release (NMPlatform *self, int master, int slave) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (master > 0, FALSE); g_return_val_if_fail (slave > 0, FALSE); g_return_val_if_fail (klass->link_release, FALSE); - if (nm_platform_link_get_master (self, slave) != master) { - self->error = NM_PLATFORM_ERROR_NOT_SLAVE; + if (nm_platform_link_get_master (self, slave) != master) return FALSE; - } debug ("link: releasing '%s' (%d) from master '%s' (%d)", nm_platform_link_get_name (self, slave), slave, @@ -1426,15 +1322,12 @@ int nm_platform_link_get_master (NMPlatform *self, int slave) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (slave >= 0, FALSE); g_return_val_if_fail (klass->link_get_master, FALSE); - if (!nm_platform_link_get_name (self, slave)) { - self->error = NM_PLATFORM_ERROR_NOT_FOUND; + if (!nm_platform_link_get_name (self, slave)) return 0; - } return klass->link_get_master (self, slave); } @@ -1507,7 +1400,6 @@ nm_platform_vlan_add (NMPlatform *self, NMPlatformError plerr; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - reset_error (self); g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (vlanid >= 0, NM_PLATFORM_ERROR_BUG); @@ -1529,7 +1421,6 @@ gboolean nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); @@ -1543,7 +1434,6 @@ char * nm_platform_master_get_option (NMPlatform *self, int ifindex, const char *option) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); @@ -1556,7 +1446,6 @@ gboolean nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); @@ -1570,7 +1459,6 @@ char * nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); @@ -1583,7 +1471,6 @@ gboolean nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->vlan_get_info, FALSE); @@ -1602,7 +1489,6 @@ gboolean nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE); @@ -1614,7 +1500,6 @@ gboolean nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->vlan_set_egress_map, FALSE); @@ -1630,7 +1515,6 @@ nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, N NMPlatformError plerr; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - reset_error (self); g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (p_key >= 0, NM_PLATFORM_ERROR_BUG); @@ -1638,10 +1522,8 @@ nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, N parent_name = g_strdup (nm_platform_link_get_name (self, parent)); if ( !parent_name - || nm_platform_link_get_type (self, parent) != NM_LINK_TYPE_INFINIBAND) { - self->error = NM_PLATFORM_ERROR_WRONG_TYPE; + || nm_platform_link_get_type (self, parent) != NM_LINK_TYPE_INFINIBAND) return NM_PLATFORM_ERROR_WRONG_TYPE; - } name = g_strdup_printf ("%s.%04x", parent_name, p_key); plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link); @@ -1663,7 +1545,6 @@ nm_platform_infiniband_get_info (NMPlatform *self, const char **mode) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (klass->infiniband_get_info, FALSE); @@ -1675,7 +1556,6 @@ gboolean nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *props) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); @@ -1687,7 +1567,6 @@ gboolean nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); @@ -1699,7 +1578,6 @@ gboolean nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); @@ -1711,7 +1589,6 @@ gboolean nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); @@ -1723,7 +1600,6 @@ gboolean nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); @@ -1735,7 +1611,6 @@ gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); @@ -1746,7 +1621,6 @@ gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); @@ -1757,7 +1631,6 @@ guint32 nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex > 0, 0); @@ -1768,7 +1641,6 @@ int nm_platform_wifi_get_quality (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex > 0, 0); @@ -1779,7 +1651,6 @@ guint32 nm_platform_wifi_get_rate (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex > 0, 0); @@ -1790,7 +1661,6 @@ NM80211Mode nm_platform_wifi_get_mode (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NM_802_11_MODE_UNKNOWN); - reset_error (self); g_return_val_if_fail (ifindex > 0, NM_802_11_MODE_UNKNOWN); @@ -1801,7 +1671,6 @@ void nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode) { _CHECK_SELF_VOID (self, klass); - reset_error (self); g_return_if_fail (ifindex > 0); @@ -1818,7 +1687,6 @@ void nm_platform_wifi_set_powersave (NMPlatform *self, int ifindex, guint32 powersave) { _CHECK_SELF_VOID (self, klass); - reset_error (self); g_return_if_fail (ifindex > 0); @@ -1829,7 +1697,6 @@ guint32 nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex > 0, 0); g_return_val_if_fail (freqs != NULL, 0); @@ -1841,7 +1708,6 @@ void nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running) { _CHECK_SELF_VOID (self, klass); - reset_error (self); g_return_if_fail (ifindex > 0); @@ -1852,7 +1718,6 @@ guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, 0); - reset_error (self); g_return_val_if_fail (ifindex > 0, 0); @@ -1863,7 +1728,6 @@ gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); @@ -1874,7 +1738,6 @@ gboolean nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (ssid != NULL, FALSE); @@ -1912,7 +1775,6 @@ GArray * nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (klass->ip4_address_get_all, NULL); @@ -1924,7 +1786,6 @@ GArray * nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (klass->ip6_address_get_all, NULL); @@ -1943,7 +1804,6 @@ nm_platform_ip4_address_add (NMPlatform *self, const char *label) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -1981,7 +1841,6 @@ nm_platform_ip6_address_add (NMPlatform *self, guint flags) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -2013,7 +1872,6 @@ nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address char str_peer[NM_UTILS_INET_ADDRSTRLEN]; _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -2035,7 +1893,6 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a char str_dev[TO_STRING_DEV_BUF_SIZE]; _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -2051,7 +1908,6 @@ gboolean nm_platform_ip4_address_exists (NMPlatform *self, int ifindex, in_addr_t address, int plen) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip4_address_exists, FALSE); @@ -2063,7 +1919,6 @@ gboolean nm_platform_ip6_address_exists (NMPlatform *self, int ifindex, struct in6_addr address, int plen) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip6_address_exists, FALSE); @@ -2177,7 +2032,6 @@ gboolean nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); if ( ifindex <= 0 || address->plen <= 0 @@ -2338,7 +2192,6 @@ GArray * nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteMode mode) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex >= 0, NULL); g_return_val_if_fail (klass->ip4_route_get_all, NULL); @@ -2350,7 +2203,6 @@ GArray * nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteMode mode) { _CHECK_SELF (self, klass, NULL); - reset_error (self); g_return_val_if_fail (ifindex >= 0, NULL); g_return_val_if_fail (klass->ip6_route_get_all, NULL); @@ -2366,7 +2218,6 @@ nm_platform_ip4_route_add (NMPlatform *self, guint32 metric, guint32 mss) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (0 <= plen && plen <= 32, FALSE); g_return_val_if_fail (klass->ip4_route_add, FALSE); @@ -2398,7 +2249,6 @@ nm_platform_ip6_route_add (NMPlatform *self, guint32 metric, guint32 mss) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (0 <= plen && plen <= 128, FALSE); g_return_val_if_fail (klass->ip6_route_add, FALSE); @@ -2425,7 +2275,6 @@ nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, char str_dev[TO_STRING_DEV_BUF_SIZE]; _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->ip4_route_delete, FALSE); @@ -2441,7 +2290,6 @@ nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr net char str_dev[TO_STRING_DEV_BUF_SIZE]; _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->ip6_route_delete, FALSE); @@ -2455,7 +2303,6 @@ gboolean nm_platform_ip4_route_exists (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->ip4_route_exists, FALSE); @@ -2466,7 +2313,6 @@ gboolean nm_platform_ip6_route_exists (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric) { _CHECK_SELF (self, klass, FALSE); - reset_error (self); g_return_val_if_fail (klass->ip6_route_exists, FALSE); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 57a97bcd12..afb94c1ae9 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -401,8 +401,6 @@ typedef struct { struct _NMPlatform { GObject parent; - - NMPlatformError error; }; typedef struct { @@ -590,9 +588,6 @@ nm_platform_route_scope_inv (guint8 scope) const char *nm_link_type_to_string (NMLinkType link_type); const char *nm_platform_error_to_string (NMPlatformError error); -void nm_platform_set_error (NMPlatform *self, NMPlatformError error); -NMPlatformError nm_platform_get_error (NMPlatform *self); -const char *nm_platform_get_error_msg (NMPlatform *self); gboolean nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value); char *nm_platform_sysctl_get (NMPlatform *self, const char *path); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index ee48a9d7cb..fe148518c2 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -865,7 +865,6 @@ main (int argc, char **argv) const char *arg0 = *argv++; const command_t *command = NULL; gboolean status = TRUE; - int error; #if !GLIB_CHECK_VERSION (2, 35, 0) g_type_init (); @@ -899,12 +898,5 @@ main (int argc, char **argv) error ("\n"); } - error = nm_platform_get_error (NM_PLATFORM_GET); - if (error) { - const char *msg = nm_platform_get_error_msg (NM_PLATFORM_GET); - - error ("nm-platform: %s\n", msg); - } - - return !!error; + return EXIT_SUCCESS; } diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 06cd3aac14..6d7a47def4 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -65,22 +65,17 @@ test_ip4_address (void) /* Add address */ g_assert (!nm_platform_ip4_address_exists (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); - no_error (); g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); g_assert (nm_platform_ip4_address_exists (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); - no_error (); accept_signal (address_added); /* Add address again (aka update) */ g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); accept_signals (address_changed, 0, 1); /* Test address listing */ addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); - no_error (); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP4Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); @@ -90,13 +85,11 @@ test_ip4_address (void) /* Remove address */ g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); - no_error (); g_assert (!nm_platform_ip4_address_exists (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_removed); /* Remove address again */ g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); - no_error (); free_signal (address_added); free_signal (address_changed); @@ -121,22 +114,17 @@ test_ip6_address (void) /* Add address */ g_assert (!nm_platform_ip6_address_exists (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - no_error (); g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); g_assert (nm_platform_ip6_address_exists (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - no_error (); accept_signal (address_added); /* Add address again (aka update) */ g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); accept_signals (address_changed, 0, 1); /* Test address listing */ addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); - no_error (); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP6Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); @@ -146,13 +134,11 @@ test_ip6_address (void) /* Remove address */ g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - no_error (); g_assert (!nm_platform_ip6_address_exists (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_removed); /* Remove address again */ g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - no_error (); free_signal (address_added); free_signal (address_changed); @@ -190,12 +176,10 @@ test_ip4_address_external (void) run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); g_assert (nm_platform_ip4_address_exists (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_added); /*run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - no_error (); g_assert (!nm_platform_ip4_address_exists (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_removed);*/ @@ -229,12 +213,10 @@ test_ip6_address_external (void) run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred); g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); g_assert (nm_platform_ip6_address_exists (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_added); /*run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - no_error (); g_assert (!nm_platform_ip6_address_exists (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); wait_signal (address_removed);*/ diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 5f72a1480d..4a1814f85a 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -15,9 +15,6 @@ #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) -#define error(err) g_assert (nm_platform_get_error (NM_PLATFORM_GET) == err) -#define no_error() error (NM_PLATFORM_ERROR_SUCCESS) - typedef struct { int handler_id; const char *name; diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 40b4731b66..e09e1eb9d1 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -22,54 +22,32 @@ test_bogus(void) size_t addrlen; g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, BOGUS_NAME)); - no_error (); g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, BOGUS_NAME)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, &addrlen)); g_assert (!addrlen); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_vlan_get_info (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); - error (NM_PLATFORM_ERROR_NOT_FOUND); } static void @@ -103,8 +81,6 @@ software_add (NMLinkType link_type, const char *name) /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, "bond0")); - - nm_platform_set_error (NM_PLATFORM_GET, plerr); return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: @@ -170,8 +146,8 @@ test_slave (int master, int type, SignalData *master_changed) /* Enslave */ link_changed->ifindex = ifindex; - g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); - g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, master); no_error (); + g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); + g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, master); accept_signals (link_changed, 1, 3); accept_signals (master_changed, 0, 1); @@ -220,7 +196,7 @@ test_slave (int master, int type, SignalData *master_changed) } /* Set slave up and see if master gets up too */ - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); no_error (); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); @@ -232,7 +208,7 @@ test_slave (int master, int type, SignalData *master_changed) * Gracefully succeed if already enslaved. */ ensure_no_signal (link_changed); - g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); no_error (); + g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); accept_signals (link_changed, 0, 2); ensure_no_signal (master_changed); @@ -241,9 +217,7 @@ test_slave (int master, int type, SignalData *master_changed) case NM_LINK_TYPE_BRIDGE: if (nmtst_platform_is_sysfs_writable ()) { g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); - no_error (); value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); - no_error (); g_assert_cmpstr (value, ==, "789"); g_free (value); } @@ -255,7 +229,7 @@ test_slave (int master, int type, SignalData *master_changed) /* Release */ ensure_no_signal (link_changed); g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); - g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0); no_error (); + g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0); accept_signals (link_changed, 1, 3); if (link_type != NM_LINK_TYPE_TEAM) accept_signals (master_changed, 1, 2); @@ -267,14 +241,12 @@ test_slave (int master, int type, SignalData *master_changed) /* Release again */ ensure_no_signal (link_changed); g_assert (!nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); - error (NM_PLATFORM_ERROR_NOT_SLAVE); ensure_no_signal (master_changed); /* Remove */ ensure_no_signal (link_changed); g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - no_error (); accept_signals (master_changed, 0, 1); accept_signals (link_changed, 0, 1); accept_signal (link_removed); @@ -296,7 +268,6 @@ test_software (NMLinkType link_type, const char *link_typename) /* Add */ link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); g_assert (software_add (link_type, DEVICE_NAME)); - no_error (); accept_signal (link_added); g_assert (nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -309,12 +280,10 @@ test_software (NMLinkType link_type, const char *link_typename) g_assert (nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &vlan_parent, &vlan_id)); g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); g_assert_cmpint (vlan_id, ==, VLAN_ID); - no_error (); } /* Add again */ g_assert (!software_add (link_type, DEVICE_NAME)); - error (NM_PLATFORM_ERROR_EXISTS); /* Set ARP/NOARP */ g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); @@ -330,9 +299,7 @@ test_software (NMLinkType link_type, const char *link_typename) case NM_LINK_TYPE_BRIDGE: if (nmtst_platform_is_sysfs_writable ()) { g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); - no_error (); value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); - no_error (); g_assert_cmpstr (value, ==, "789"); g_free (value); } @@ -340,9 +307,7 @@ test_software (NMLinkType link_type, const char *link_typename) case NM_LINK_TYPE_BOND: if (nmtst_platform_is_sysfs_writable ()) { g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); - no_error (); value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); - no_error (); /* When reading back, the output looks slightly different. */ g_assert (g_str_has_prefix (value, "active-backup")); g_free (value); @@ -368,17 +333,13 @@ test_software (NMLinkType link_type, const char *link_typename) /* Delete */ g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - no_error (); - g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); no_error (); + g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_NONE); - error (NM_PLATFORM_ERROR_NOT_FOUND); g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, ifindex)); - error (NM_PLATFORM_ERROR_NOT_FOUND); accept_signal (link_removed); /* Delete again */ g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); - error (NM_PLATFORM_ERROR_NOT_FOUND); /* VLAN: Delete parent */ if (link_type == NM_LINK_TYPE_VLAN) { @@ -436,18 +397,15 @@ test_internal (void) int ifindex; /* Check the functions for non-existent devices */ - g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); no_error (); + g_assert (!nm_platform_link_exists (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - error (NM_PLATFORM_ERROR_NOT_FOUND); /* Add device */ g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); - no_error (); accept_signal (link_added); /* Try to add again */ g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); - error (NM_PLATFORM_ERROR_EXISTS); /* Check device index, name and type */ ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -459,15 +417,15 @@ test_internal (void) link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); /* Up/connected */ - g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); no_error (); - g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); no_error (); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); no_error (); - g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); no_error (); - g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); no_error (); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); - g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); no_error (); - g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); no_error (); - g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); no_error (); + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); /* arp/noarp */ @@ -494,18 +452,15 @@ test_internal (void) /* Set MTU */ g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU)); - no_error (); g_assert_cmpint (nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex), ==, MTU); accept_signal (link_changed); /* Delete device */ g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - no_error (); accept_signal (link_removed); /* Try to delete again */ g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - error (NM_PLATFORM_ERROR_NOT_FOUND); free_signal (link_added); free_signal (link_changed); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 1bf18ff12a..cd4217d248 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -66,7 +66,6 @@ test_ip4_route_metric0 (void) /* add the first route */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); - no_error (); accept_signal (route_added); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -74,7 +73,6 @@ test_ip4_route_metric0 (void) /* Deleting route with metric 0 does nothing */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); - no_error (); ensure_no_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -82,7 +80,6 @@ test_ip4_route_metric0 (void) /* add the second route */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); - no_error (); accept_signal (route_added); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, 0); @@ -90,7 +87,6 @@ test_ip4_route_metric0 (void) /* Delete route with metric 0 */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); - no_error (); accept_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -98,7 +94,6 @@ test_ip4_route_metric0 (void) /* 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)); - no_error (); ensure_no_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -106,7 +101,6 @@ test_ip4_route_metric0 (void) /* Delete the other route */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); accept_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -138,35 +132,26 @@ test_ip4_route (void) /* Add route to gateway */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss)); - no_error (); accept_signal (route_added); /* Add route */ assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); - no_error (); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); - no_error (); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); - no_error (); accept_signal (route_added); /* Add route again */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); - no_error (); accept_signals (route_changed, 0, 1); /* Add default route */ assert_ip4_route_exists (FALSE, DEVICE_NAME, 0, 0, metric); - no_error (); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); - no_error (); assert_ip4_route_exists (TRUE, DEVICE_NAME, 0, 0, metric); - no_error (); accept_signal (route_added); /* Add default route again */ g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); - no_error (); accept_signals (route_changed, 0, 1); /* Test route listing */ @@ -202,13 +187,11 @@ test_ip4_route (void) /* Remove route */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); accept_signal (route_removed); /* Remove route again */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); free_signal (route_added); free_signal (route_changed); @@ -236,35 +219,26 @@ test_ip6_route (void) /* Add route to gateway */ g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss)); - no_error (); accept_signal (route_added); /* Add route */ g_assert (!nm_platform_ip6_route_exists (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); - no_error (); g_assert (nm_platform_ip6_route_exists (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); accept_signal (route_added); /* Add route again */ g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); - no_error (); accept_signals (route_changed, 0, 1); /* Add default route */ g_assert (!nm_platform_ip6_route_exists (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); - no_error (); g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); - no_error (); g_assert (nm_platform_ip6_route_exists (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); - no_error (); accept_signal (route_added); /* Add default route again */ g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); - no_error (); accept_signals (route_changed, 0, 1); /* Test route listing */ @@ -297,13 +271,11 @@ test_ip6_route (void) /* Remove route */ g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); g_assert (!nm_platform_ip6_route_exists (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); /* Remove route again */ g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - no_error (); free_signal (route_added); free_signal (route_changed);