summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-06-17 11:44:37 +0200
committerThomas Haller <thaller@redhat.com>2015-06-17 11:50:53 +0200
commit35dcd8ac33d631c54532aa3bd0b3d2e026ec6407 (patch)
treecca77823f38f029e844a3350be3f2d072ac2f62e
parent135999c2ec567683b6d71cc328584a2126167144 (diff)
parent68a4ffb4e2e38f9476e51da75770239493cb47b7 (diff)
platform: merge branch 'th/platform_refact_caching-bgo747981'
Refactor caching of NMPlatform and the way how to load objects via libnl/netlink. https://bugzilla.gnome.org/show_bug.cgi?id=747981 https://bugzilla.gnome.org/show_bug.cgi?id=747985 https://bugzilla.redhat.com/show_bug.cgi?id=1211133
-rw-r--r--include/nm-glib-compat.h49
-rw-r--r--include/nm-macros-internal.h17
-rw-r--r--libnm-core/nm-core-internal.h2
-rw-r--r--libnm-core/nm-utils.c26
-rw-r--r--libnm-core/tests/test-general.c127
-rw-r--r--src/Makefile.am8
-rw-r--r--src/devices/adsl/nm-device-adsl.c2
-rw-r--r--src/devices/nm-device-bond.c7
-rw-r--r--src/devices/nm-device-bridge.c9
-rw-r--r--src/devices/nm-device-ethernet.c23
-rw-r--r--src/devices/nm-device-infiniband.c7
-rw-r--r--src/devices/nm-device-vlan.c9
-rw-r--r--src/devices/nm-device.c34
-rw-r--r--src/devices/team/nm-device-team.c7
-rw-r--r--src/main.c2
-rw-r--r--src/nm-multi-index.c441
-rw-r--r--src/nm-multi-index.h109
-rw-r--r--src/nm-types.h7
-rw-r--r--src/platform/nm-fake-platform.c152
-rw-r--r--src/platform/nm-linux-platform.c4047
-rw-r--r--src/platform/nm-linux-platform.h4
-rw-r--r--src/platform/nm-platform.c564
-rw-r--r--src/platform/nm-platform.h111
-rw-r--r--src/platform/nmp-object.c1941
-rw-r--r--src/platform/nmp-object.h383
-rw-r--r--src/platform/tests/.gitignore2
-rw-r--r--src/platform/tests/Makefile.am7
-rw-r--r--src/platform/tests/dump.c9
-rw-r--r--src/platform/tests/platform.c29
-rw-r--r--src/platform/tests/test-address.c26
-rw-r--r--src/platform/tests/test-cleanup.c6
-rw-r--r--src/platform/tests/test-common.h3
-rw-r--r--src/platform/tests/test-general.c15
-rw-r--r--src/platform/tests/test-link.c177
-rw-r--r--src/platform/tests/test-nmp-object.c426
-rw-r--r--src/platform/tests/test-route.c47
-rw-r--r--src/tests/test-general-with-expect.c358
-rw-r--r--src/tests/test-route-manager.c25
-rw-r--r--src/vpn-manager/nm-vpn-connection.c2
-rw-r--r--valgrind.suppressions18
40 files changed, 6804 insertions, 2434 deletions
diff --git a/include/nm-glib-compat.h b/include/nm-glib-compat.h
index 1dc939feac..da4e2c2ce0 100644
--- a/include/nm-glib-compat.h
+++ b/include/nm-glib-compat.h
@@ -163,4 +163,53 @@ 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
+}
+
+
+#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/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/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)
{
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 1aca515a88..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 <glib.h>
@@ -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"
@@ -4358,6 +4361,128 @@ 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);
+ }
+ }
+}
+
+/******************************************************************************/
+
+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)
@@ -4459,6 +4584,8 @@ 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/_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);
diff --git a/src/Makefile.am b/src/Makefile.am
index 4abd92a53b..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 \
@@ -331,6 +333,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 \
@@ -473,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 \
@@ -498,6 +504,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/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-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-ethernet.c b/src/devices/nm-device-ethernet.c
index a0aa02d1ac..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*
@@ -1559,7 +1552,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-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/nm-device.c b/src/devices/nm-device.c
index 63baa63270..9531ff2611 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");
@@ -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);
}
@@ -1406,15 +1408,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 +1439,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.
*/
@@ -4721,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) {
@@ -5526,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));
}
@@ -5647,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));
}
@@ -6791,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))
@@ -8772,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);
}
}
@@ -8938,11 +8936,11 @@ 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;
- 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/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/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/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 <string.h>
+
+#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 <glib.h>
+
+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/nm-types.h b/src/nm-types.h
index 3fa587932e..2f36bee45a 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -51,6 +51,13 @@ 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 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,
+
NM_IP_CONFIG_SOURCE_KERNEL,
NM_IP_CONFIG_SOURCE_SHARED,
NM_IP_CONFIG_SOURCE_IP4LL,
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index d5843a1edf..1eac40d9e2 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -24,12 +24,16 @@
#include <unistd.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
+#include <linux/rtnetlink.h>
#include "gsystem-local-alloc.h"
+#include "nm-utils.h"
#include "NetworkManagerUtils.h"
#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 {
@@ -48,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))
@@ -56,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)
{
@@ -103,24 +117,30 @@ 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->udi = g_strdup_printf ("fake:%d", ifindex);
device->link.initialized = TRUE;
+ device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr);
if (name)
strcpy (device->link.name, name);
switch (device->link.type) {
case NM_LINK_TYPE_DUMMY:
- device->link.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;
}
@@ -140,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;
}
@@ -205,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;
@@ -303,55 +325,76 @@ 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;
+
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, TRUE);
+ }
}
}
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;
- 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 ( NM_FLAGS_HAS (device->link.flags, IFF_UP) != !!up
+ || device->link.connected != connected) {
+ device->link.flags = NM_FLAGS_ASSIGN (device->link.flags, IFF_UP, up);
+ device->link.connected = connected;
+ link_changed (platform, device, TRUE);
+ }
return TRUE;
}
@@ -364,10 +407,12 @@ link_set_down (NMPlatform *platform, int ifindex)
if (!device)
return FALSE;
- device->link.up = FALSE;
- device->link.connected = 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);
+ link_changed (platform, device, TRUE);
+ }
return TRUE;
}
@@ -380,9 +425,9 @@ 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);
+ link_changed (platform, device, TRUE);
return TRUE;
}
@@ -395,9 +440,9 @@ 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);
+ link_changed (platform, device, TRUE);
return TRUE;
}
@@ -407,7 +452,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
@@ -423,7 +468,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
@@ -436,7 +481,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;
}
@@ -468,7 +513,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;
@@ -500,6 +545,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)
{
@@ -565,12 +620,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.flags = NM_FLAGS_SET (device->link.flags, IFF_UP);
+ device->link.connected = TRUE;
+ }
+
+ link_changed (platform, device, TRUE);
+ }
return TRUE;
}
@@ -584,15 +648,13 @@ 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;
- link_changed (platform, slave);
- link_changed (platform, master);
+ link_changed (platform, slave, TRUE);
+ link_changed (platform, master, TRUE);
return TRUE;
}
@@ -982,8 +1044,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;
}
@@ -1196,6 +1260,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 +1273,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++) {
@@ -1218,8 +1286,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;
}
}
@@ -1285,8 +1353,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;
}
}
@@ -1453,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 f1b863e1e9..46fad57941 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"
@@ -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"
@@ -108,7 +109,38 @@
#define warning(...) _LOG (LOGL_WARN , _LOG_DOMAIN, NULL, __VA_ARGS__)
#define error(...) _LOG (LOGL_ERR , _LOG_DOMAIN, NULL, __VA_ARGS__)
+/******************************************************************
+ * Forward declarations and enums
+ ******************************************************************/
+
+typedef enum {
+ 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 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
@@ -157,21 +189,9 @@ _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)))
+#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;
@@ -181,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;
@@ -196,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;
@@ -208,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;
@@ -224,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;
@@ -237,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 ();
@@ -247,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;
@@ -259,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;
@@ -269,66 +289,231 @@ 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
+
+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;
+}
/******************************************************************/
-static guint32
-_get_expiry (guint32 now_s, guint32 lifetime_s)
+/* _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().
+ */
+
+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
+_nl_link_parse_info_data_cb (struct nl_msg *msg, void *arg)
{
- gint64 t = ((gint64) now_s) + ((gint64) lifetime_s);
+ 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;
- return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1);
+ 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
+_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;
+ 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, _nl_link_parse_info_data_cb, &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)
+{
+ 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);
+ 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;
+
+ 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;
}
-/* 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)
+_nl_msg_set_seq (struct nl_sock *sk, struct nl_msg *msg, guint32 *out_seq)
{
- guint32 a_valid = rtnl_addr_get_valid_lifetime (rtnladdr);
- guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr);
- guint32 now;
+ guint32 seq;
- if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT &&
- a_preferred == NM_PLATFORM_LIFETIME_PERMANENT)
- return;
+ /* 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);
- now = (guint32) nm_utils_get_monotonic_timestamp_s ();
+ nlmsg_hdr (msg)->nlmsg_seq = seq;
+ if (out_seq)
+ *out_seq = seq;
+}
- if (a_preferred > a_valid)
- a_preferred = a_valid;
+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;
- 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));
+ _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
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
@@ -345,7 +530,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) {
@@ -401,325 +586,50 @@ _support_kernel_extended_ifa_flags_get (void)
}
/******************************************************************
- * NMPlatform types and functions
+ * Object type specific utilities
******************************************************************/
-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;
- struct nl_cache *link_cache;
- struct nl_cache *address_cache;
- struct nl_cache *route_cache;
- GIOChannel *event_channel;
- guint event_id;
-
- GUdevClient *udev_client;
- GHashTable *udev_devices;
-
- GHashTable *wifi_data;
-} NMLinuxPlatformPrivate;
-
-#define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate))
-
-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)
-{
- nm_platform_setup (g_object_new (NM_TYPE_LINUX_PLATFORM, NULL));
-}
-
-/******************************************************************/
-
-static ObjectType
-_nlo_get_object_type (const struct nl_object *object)
-{
- const char *type_str;
-
- if (!object || !(type_str = nl_object_get_type (object)))
- return OBJECT_TYPE_UNKNOWN;
-
- if (!strcmp (type_str, "route/link"))
- return OBJECT_TYPE_LINK;
- else if (!strcmp (type_str, "route/addr")) {
- switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) {
- case AF_INET:
- return OBJECT_TYPE_IP4_ADDRESS;
- case AF_INET6:
- return OBJECT_TYPE_IP6_ADDRESS;
- default:
- return OBJECT_TYPE_UNKNOWN;
- }
- } else if (!strcmp (type_str, "route/route")) {
- switch (rtnl_route_get_family ((struct rtnl_route *) object)) {
- case AF_INET:
- return OBJECT_TYPE_IP4_ROUTE;
- case AF_INET6:
- return OBJECT_TYPE_IP6_ROUTE;
- default:
- return OBJECT_TYPE_UNKNOWN;
- }
- } else
- 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)
+static guint
+_nm_ip_config_source_to_rtprot (NMIPConfigSource source)
{
- 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);
+ 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;
- 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;
+ return RTPROT_STATIC;
}
}
-/* 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)
+static NMIPConfigSource
+_nm_ip_config_source_from_rtprot (guint rtprot)
{
- 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);
+ 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:
- g_return_val_if_reached (-NLE_INVAL);
- return -NLE_INVAL;
+ return NM_IP_CONFIG_SOURCE_USER;
}
}
-/* 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
-check_support_kernel_extended_ifa_flags (NMPlatform *platform)
-{
- g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
-
- return _support_kernel_extended_ifa_flags_get ();
-}
-
-static gboolean
-check_support_user_ipv6ll (NMPlatform *platform)
-{
- g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
-
- return _support_user_ipv6ll_get ();
-}
-
-/* Object type specific utilities */
-
typedef struct {
const NMLinkType nm_type;
const char *type_string;
@@ -796,6 +706,109 @@ nm_link_type_to_string (NMLinkType type)
g_return_val_if_reached (NULL);
}
+/******************************************************************
+ * NMPlatform types and functions
+ ******************************************************************/
+
+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;
+
+ GUdevClient *udev_client;
+
+ struct {
+ 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;
+};
+
+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)
+
+void
+nm_linux_platform_setup (void)
+{
+ g_object_new (NM_TYPE_LINUX_PLATFORM,
+ NM_PLATFORM_REGISTER_SINGLETON, TRUE,
+ NULL);
+}
+
+/******************************************************************/
+
+ObjectType
+_nlo_get_object_type (const struct nl_object *object)
+{
+ const char *type_str;
+
+ if (!object || !(type_str = nl_object_get_type (object)))
+ return OBJECT_TYPE_UNKNOWN;
+
+ if (!strcmp (type_str, "route/link"))
+ return OBJECT_TYPE_LINK;
+ else if (!strcmp (type_str, "route/addr")) {
+ switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) {
+ case AF_INET:
+ return OBJECT_TYPE_IP4_ADDRESS;
+ case AF_INET6:
+ return OBJECT_TYPE_IP6_ADDRESS;
+ default:
+ return OBJECT_TYPE_UNKNOWN;
+ }
+ } else if (!strcmp (type_str, "route/route")) {
+ switch (rtnl_route_get_family ((struct rtnl_route *) object)) {
+ case AF_INET:
+ return OBJECT_TYPE_IP4_ROUTE;
+ case AF_INET6:
+ return OBJECT_TYPE_IP6_ROUTE;
+ default:
+ return OBJECT_TYPE_UNKNOWN;
+ }
+ } else
+ return OBJECT_TYPE_UNKNOWN;
+}
+
+/******************************************************************/
+
+static gboolean
+check_support_kernel_extended_ifa_flags (NMPlatform *platform)
+{
+ g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
+
+ return _support_kernel_extended_ifa_flags_get ();
+}
+
+static gboolean
+check_support_user_ipv6ll (NMPlatform *platform)
+{
+ g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
+
+ return _support_user_ipv6ll_get ();
+}
+
+/******************************************************************/
+
#define DEVTYPE_PREFIX "DEVTYPE="
static char *
@@ -822,15 +835,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)
@@ -921,119 +953,86 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink)
return NM_LINK_TYPE_UNKNOWN;
}
-static gboolean
-init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllink)
+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)
{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- GUdevDevice *udev_device;
+ NMPlatformLink *obj = (NMPlatformLink *) _obj;
+ NMPObjectLink *obj_priv = (NMPObjectLink *) _obj;
+ struct rtnl_link *nlo = (struct rtnl_link *) _nlo;
const char *name;
- char *tmp;
+ struct nl_addr *nladdr;
+ const char *kind;
+
+ nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0);
- g_return_val_if_fail (rtnllink, FALSE);
+ 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));
- name = rtnl_link_get_name (rtnllink);
- memset (info, 0, sizeof (*info));
+ obj->ifindex = rtnl_link_get_ifindex (nlo);
+
+ if (id_only)
+ return TRUE;
- info->ifindex = rtnl_link_get_ifindex (rtnllink);
+ name = rtnl_link_get_name (nlo);
if (name)
- g_strlcpy (info->name, name, sizeof (info->name));
- else
- 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->master = rtnl_link_get_master (rtnllink);
- info->parent = rtnl_link_get_link (rtnllink);
- info->mtu = rtnl_link_get_mtu (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);
- info->udi = g_udev_device_get_sysfs_path (udev_device);
- info->initialized = TRUE;
- }
+ g_strlcpy (obj->name, name, sizeof (obj->name));
+ 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->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP);
+ 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;
- 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);
+ 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 (!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;
-}
-
-/* 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);
+#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
+ if (_support_user_ipv6ll_get ()) {
+ guint8 mode = 0;
- type = rtnl_link_get_type (rtnllink);
- if (!type || (strcmp (type, "bridge") != 0 && strcmp (type, "bond") != 0))
- return;
+ if (rtnl_link_inet6_get_addr_gen_mode (nlo, &mode) == 0)
+ obj->inet6_addr_gen_mode_inv = ~mode;
+ }
+#endif
- 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;
+#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
- rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP);
-}
+ obj_priv->netlink.is_in_netlink = TRUE;
-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;
+ return TRUE;
}
/* _timestamp_nl_to_ms:
@@ -1069,15 +1068,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 +1090,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:
*
@@ -1105,655 +1110,928 @@ _rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr)
return result / 1000;
}
-static void
-_init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr)
+static guint32
+_extend_lifetime (guint32 lifetime, guint32 seconds)
{
- guint32 a_valid = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr);
- guint32 a_preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr);
+ guint64 v;
- /* 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;
- }
+ if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT
+ || seconds == 0)
+ return lifetime;
- /* 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;
- }
+ v = (guint64) lifetime + (guint64) seconds;
+ return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1);
+}
- /* _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);
+/* 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;
- /* 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);
+ 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)
+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)
{
- struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr);
- struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr);
+ 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;
- g_return_val_if_fail (nladdr, FALSE);
+ if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address))
+ g_return_val_if_reached (FALSE);
- memset (address, 0, sizeof (*address));
+ 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));
- 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 (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 (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));
+ 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 (rtnladdr);
+ label = rtnl_addr_get_label (nlo);
/* Check for ':'; we're only interested in labels used as interface aliases */
if (label && strchr (label, ':'))
- g_strlcpy (address->label, label, sizeof (address->label));
+ g_strlcpy (obj->label, label, sizeof (obj->label));
return TRUE;
}
-static gboolean
-init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
+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)
{
- struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr);
- struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr);
-
- memset (address, 0, sizeof (*address));
+ 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);
- 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)) {
+ if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->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;
-}
+ 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));
-static guint
-source_to_rtprot (NMIPConfigSource source)
-{
- switch (source) {
- case NM_IP_CONFIG_SOURCE_UNKNOWN:
- return RTPROT_UNSPEC;
- case NM_IP_CONFIG_SOURCE_KERNEL:
- return RTPROT_KERNEL;
- case NM_IP_CONFIG_SOURCE_DHCP:
- return RTPROT_DHCP;
- case NM_IP_CONFIG_SOURCE_RDISC:
- return RTPROT_RA;
+ if (id_only)
+ return TRUE;
- default:
- return RTPROT_STATIC;
- }
-}
+ 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);
-static NMIPConfigSource
-rtprot_to_source (guint rtprot)
-{
- switch (rtprot) {
- case RTPROT_UNSPEC:
- return NM_IP_CONFIG_SOURCE_UNKNOWN;
- case RTPROT_REDIRECT:
- case RTPROT_KERNEL:
- 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;
+ 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));
}
-}
-
-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;
+ return TRUE;
}
-static gboolean
-init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute)
+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;
- memset (route, 0, sizeof (*route));
-
- /* Multi-hop routes not supported. */
- if (rtnl_route_get_nnexthops (rtnlroute) != 1)
+ 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 (rtnlroute, 0);
- dst = rtnl_route_get_dst (rtnlroute);
- gw = rtnl_route_nh_get_gateway (nexthop);
+ 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);
- 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)) {
+ if (nl_addr_get_len (dst) != sizeof (obj->network))
g_return_val_if_reached (FALSE);
- return FALSE;
- }
- memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
+ 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 (route->network)) {
- g_return_val_if_reached (FALSE);
- return FALSE;
- }
- memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway));
+ 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));
}
- 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));
+ 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 = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo));
return TRUE;
}
-static gboolean
-init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
+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;
- memset (route, 0, sizeof (*route));
-
- /* Multi-hop routes not supported. */
- if (rtnl_route_get_nnexthops (rtnlroute) != 1)
+ 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 (rtnlroute, 0);
- dst = rtnl_route_get_dst (rtnlroute);
- gw = rtnl_route_nh_get_gateway (nexthop);
+ 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);
- 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)) {
+ if (nl_addr_get_len (dst) != sizeof (obj->network))
g_return_val_if_reached (FALSE);
- return FALSE;
- }
- memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
+ 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 (route->network)) {
- g_return_val_if_reached (FALSE);
- return FALSE;
- }
- memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway));
+ 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));
}
- 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));
+ 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 = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo));
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)
+static void
+do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason)
{
- NMPlatformLink pl_obj;
+ 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));
- if (init_link (platform, &pl_obj, obj))
- return nm_platform_link_to_string (&pl_obj);
- SET_AND_RETURN_STRING_BUFFER ("(invalid link %p)", obj);
+ 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;
+ }
+
+ klass = NMP_OBJECT_GET_CLASS (obj);
+
+ _LOGT ("emit signal %s %s: %s (%ld)",
+ 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);
+
+ /* 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);
}
-static const char *
-to_string_ip4_address (struct rtnl_addr *obj)
+/******************************************************************/
+
+static DelayedActionType
+delayed_action_refresh_from_object_type (ObjectType obj_type)
{
- NMPlatformIP4Address pl_obj;
+ 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);
+ }
+}
- 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 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 *
-to_string_ip6_address (struct rtnl_addr *obj)
+delayed_action_to_string (DelayedActionType action_type)
+{
+ 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(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)
{
- NMPlatformIP6Address pl_obj;
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ auto_nmp_obj NMPObject *obj_cache = NULL;
+ gboolean was_visible;
+ NMPCacheOpsType cache_op;
- 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);
+ 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 const char *
-to_string_ip4_route (struct rtnl_route *obj)
+static void
+delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex)
{
- 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);
+ do_request_link (platform, ifindex, NULL, FALSE);
}
-static const char *
-to_string_ip6_route (struct rtnl_route *obj)
+static void
+delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags)
{
- NMPlatformIP6Route pl_obj;
+ do_request_all (platform, flags, FALSE);
+}
- 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 void
+delayed_action_handle_READ_NETLINK (NMPlatform *platform)
+{
+ event_handler_read_netlink_all (platform, TRUE);
}
-static const char *
-to_string_object_with_type (NMPlatform *platform, struct nl_object *obj, ObjectType type)
+static gboolean
+delayed_action_handle_one (NMPlatform *platform)
{
- 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);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ gpointer user_data;
+
+ if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) {
+ 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 (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 (NM_FLAGS_ANY (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) {
+ DelayedActionType flags, iflags;
+
+ 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;
+ }
+
+ nm_assert (priv->delayed_action.flags == DELAYED_ACTION_TYPE_REFRESH_LINK);
+ nm_assert (priv->delayed_action.list_refresh_link->len > 0);
+
+ 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);
+
+ _LOGT_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle");
+
+ delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data));
+
+ return TRUE;
}
-static const char *
-to_string_object (NMPlatform *platform, struct nl_object *obj)
+static gboolean
+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--;
+ return any;
+}
+
+static gboolean
+delayed_action_handle_idle (gpointer user_data)
{
- return to_string_object_with_type (platform, obj, _nlo_get_object_type (obj));
+ NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0;
+ delayed_action_handle_all (user_data, FALSE);
+ return G_SOURCE_REMOVE;
}
-#undef SET_AND_RETURN_STRING_BUFFER
+static void
+delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ DelayedActionType iflags;
+
+ 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;
-/* Object and cache manipulation */
+ 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");
+ }
+ }
-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,
-};
+ 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);
+}
-static struct nl_cache *
-choose_cache_by_type (NMPlatform *platform, ObjectType object_type)
+/******************************************************************/
+
+static void
+cache_prune_candidates_record_all (NMPlatform *platform, ObjectType obj_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;
- }
+ 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 struct nl_cache *
-choose_cache (NMPlatform *platform, struct nl_object *object)
+static void
+cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj)
{
- return choose_cache_by_type (platform, _nlo_get_object_type (object));
+ 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 gboolean
-object_has_ifindex (struct nl_object *object, int ifindex)
+static void
+cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj)
{
- 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;
+ NMLinuxPlatformPrivate *priv;
- if (rtnl_route_get_nnexthops (rtnlroute) != 1)
- return FALSE;
- nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
+ if (!obj)
+ return;
- return ifindex == rtnl_route_nh_get_ifindex (nexthop);
- }
- default:
- g_assert_not_reached ();
+ 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 gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason);
+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
-check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex)
+cache_delayed_deletion_prune (NMPlatform *platform)
{
- 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);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ GPtrArray *prune_list = NULL;
+ GHashTableIter iter;
guint i;
+ NMPObject *obj;
- 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);
+ 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));
}
}
- for (i = 0; i < objects_to_refresh->len; i++)
- refresh_object (platform, objects_to_refresh->pdata[i], TRUE, NM_PLATFORM_REASON_CACHE_CHECK);
+ g_hash_table_remove_all (priv->delayed_deletion);
- g_ptr_array_free (objects_to_refresh, TRUE);
+ 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
-announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatformSignalChangeType change_type, NMPlatformReason reason)
+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);
- ObjectType object_type = _nlo_get_object_type (object);
- const char *sig = signal_by_type_and_status[object_type];
-
- switch (object_type) {
+ 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:
{
- 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);
+ /* 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));
}
- 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);
+ /* 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));
}
- 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;
+ 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_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);
}
- 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 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_ALL_IP4_ROUTES |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+ NULL);
}
- 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;
+ /* 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));
+ }
- 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;
+ }
+ 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) {
+ delayed_action_schedule (platform,
+ (klass->obj_type == OBJECT_TYPE_IP4_ADDRESS)
+ ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
+ : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+ NULL);
}
- 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 ();
+ break;
}
}
-static struct nl_object * build_rtnl_link (int ifindex, const char *name, NMLinkType type);
+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;
-static gboolean
-refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason)
+ 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);
- auto_nl_object struct nl_object *cached_object = NULL;
- auto_nl_object struct nl_object *kernel_object = NULL;
- struct nl_cache *cache;
- int nle;
+ NMPObject *obj_cache;
+ gboolean was_visible;
+ NMPCacheOpsType cache_op;
- cache = choose_cache (platform, object);
- cached_object = nm_nl_cache_search (cache, object);
- kernel_object = get_kernel_object (priv->nlh, object);
+ /* This is basically a convenience method to call nmp_cache_update() and do_emit_signal()
+ * at once. */
- if (removed) {
- if (kernel_object)
- return TRUE;
+ 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);
- /* Only announce object if it was still in the cache. */
- if (cached_object) {
- nl_cache_remove (cached_object);
+ if (out_obj_cache)
+ *out_obj_cache = obj_cache;
+ else
+ nmp_object_unref (obj_cache);
+ if (out_was_visible)
+ *out_was_visible = was_visible;
- announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, reason);
- }
- } else {
- ObjectType type;
+ return cache_op;
+}
- 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);
+static void
+_new_sequence_number (NMPlatform *platform, guint32 seq)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- hack_empty_master_iff_lower_up (platform, kernel_object);
+ _LOGT ("_new_sequence_number(): new sequence number %u", seq);
- 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;
- }
+ priv->nlh_seq_expect = seq;
+}
+
+static void
+do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ guint32 seq;
- announce_object (platform, kernel_object, cached_object ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, reason);
+ _LOGT ("do_request_link (%d,%s)", ifindex, name ? name : "");
- 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;
+ if (ifindex > 0) {
+ NMPObject *obj;
- /* 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);
- }
+ 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);
+ }
+
+ event_handler_read_netlink_all (platform, FALSE);
+
+ if (_nl_sock_request_link (platform, priv->nlh_event, ifindex, name, &seq) == 0)
+ _new_sequence_number (platform, seq);
- /* 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;
+ event_handler_read_netlink_all (platform, TRUE);
+
+ cache_delayed_deletion_prune (platform);
+ cache_prune_candidates_prune (platform);
+
+ if (handle_delayed_action)
+ delayed_action_handle_all (platform, FALSE);
+}
+
+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);
+}
+
+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));
+ }
+
+ 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);
+
+ /* 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);
}
+
+ event_handler_read_netlink_all (platform, FALSE);
+
+ 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);
- return TRUE;
+ cache_prune_candidates_prune (platform);
+
+ if (handle_delayed_action)
+ delayed_action_handle_all (platform, FALSE);
}
-/* Decreases the reference count if @obj for convenience */
static gboolean
-add_object (NMPlatform *platform, struct nl_object *obj)
+kernel_add_object (NMPlatform *platform, ObjectType obj_type, const struct nl_object *nlo)
{
- 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);
+ 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);
+ }
- nle = add_kernel_object (priv->nlh, object);
+ _LOGT ("kernel-add-%s: returned %s (%d)",
+ nmp_class_from_type (obj_type)->obj_type_name, nl_geterror (nle), -nle);
- /* 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:
+ return -NLE_SUCCESS;
case -NLE_EXIST:
- break;
+ /* 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:
- 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 nle;
}
-
- return refresh_object (platform, object, FALSE, NM_PLATFORM_REASON_INTERNAL);
}
-/* Decreases the reference count if @obj for convenience */
-static gboolean
-delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refresh_object)
+static int
+kernel_delete_object (NMPlatform *platform, ObjectType object_type, const struct nl_object *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:
@@ -1773,40 +2051,134 @@ delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refre
switch (nle) {
case -NLE_SUCCESS:
- break;
+ return NLE_SUCCESS;
case -NLE_OBJ_NOTFOUND:
- debug("delete_object failed with \"%s\" (%d), meaning the object was already removed",
- nl_geterror (nle), nle);
- break;
+ _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) */
- debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
- nl_geterror (nle), nle);
- break;
+ _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;
}
- goto DEFAULT;
+ break;
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;
+ _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;
}
- goto DEFAULT;
- DEFAULT:
+ break;
default:
- error ("Netlink error deleting %s: %s (%d)", to_string_object (platform, object), nl_geterror (nle), nle);
- goto out;
+ 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);
- if (do_refresh_object)
- refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL);
+ g_return_val_if_fail (ifindex > 0, FALSE);
- result = TRUE;
+ /* 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. */
-out:
- nl_object_put (object);
- return result;
+ 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
@@ -1818,78 +2190,42 @@ ref_object (struct nl_object *obj, void *data)
*out = obj;
}
-static gboolean
-_rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2)
+static int
+event_seq_check (struct nl_msg *msg, gpointer user_data)
{
- guint32 diff;
+ NMPlatform *platform = NM_PLATFORM (user_data);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
+ struct nlmsghdr *hdr;
- if (ts1 == ts2)
- return TRUE;
- if (ts1 == NM_PLATFORM_LIFETIME_PERMANENT ||
- ts2 == NM_PLATFORM_LIFETIME_PERMANENT)
- return FALSE;
+ hdr = nlmsg_hdr (msg);
- /** accept the timestamps as equal if they are within two seconds. */
- diff = ts1 > ts2 ? ts1 - ts2 : ts2 - ts1;
- return diff <= 2;
-}
+ if (hdr->nlmsg_seq == 0)
+ return NL_OK;
-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;
- }
+ priv->nlh_seq_last = hdr->nlmsg_seq;
-#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 (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);
- 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;
+ priv->nlh_seq_expect = 0;
+ } else
+ _LOGT ("event_seq_check(): seq %u received (wait for %u)", hdr->nlmsg_seq, priv->nlh_seq_last);
- /* 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 NL_OK;
+}
- return FALSE;
+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
@@ -1902,120 +2238,71 @@ 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;
- int event;
- int nle;
- ObjectType type;
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
+ auto_nl_object struct nl_object *nlo = NULL;
+ auto_nmp_obj NMPObject *obj = NULL;
+ struct nlmsghdr *msghdr;
+ char buf_nlmsg_type[16];
- 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, &object);
- if (!object)
+ nl_msg_parse (msg, ref_object, &nlo);
+ if (!nlo)
return NL_OK;
- type = _nlo_get_object_type (object);
-
- if (type == OBJECT_TYPE_LINK)
- _support_user_ipv6ll_detect ((struct rtnl_link *) object);
-
- if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
- if (type == OBJECT_TYPE_LINK) {
- const char *name = rtnl_link_get_name ((struct rtnl_link *) object);
+ if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK)
+ _support_user_ipv6ll_detect ((struct rtnl_link *) nlo);
- 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);
- }
+ obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE);
- cache = choose_cache_by_type (platform, type);
- cached_object = nm_nl_cache_search (cache, object);
- kernel_object = get_kernel_object (priv->nlh, object);
+ _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;
- hack_empty_master_iff_lower_up (platform, kernel_object);
+ switch (msghdr->nlmsg_type) {
- /* 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));
- }
+ 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;
- 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;
+ 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;
}
- 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;
+ /* fall-through */
+ case RTM_DELADDR:
+ case RTM_DELROUTE:
+ cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL);
+ break;
+
+ default:
+ break;
}
- 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;
+ cache_prune_candidates_drop (platform, obj_cache);
}
+
+ return NL_OK;
}
/******************************************************************/
@@ -2184,31 +2471,48 @@ 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))
+ return NULL;
+
+ return obj_cache;
+}
+
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
@@ -2217,22 +2521,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;
-
- 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;
-
- 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;
+ const NMPObject *obj;
+ struct _nm_platform_link_get_by_address_data d = {
+ .address = address,
+ .length = length,
+ };
+
+ if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX)
+ return FALSE;
+ if (!address)
+ 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 *
@@ -2241,7 +2546,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);
@@ -2249,182 +2554,294 @@ 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)
+do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo)
{
- int ifindex;
+ NMPObject obj_needle;
+ int nle;
- g_return_val_if_fail (name != NULL, FALSE);
+ event_handler_read_netlink_all (platform, 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);
+ 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));
+
+ 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.
+ *
+ * For that, you'd have to look at @out_obj. */
return TRUE;
}
static gboolean
-link_add (NMPlatform *platform,
- const char *name,
- NMLinkType type,
- const void *address,
- size_t address_len,
- NMPlatformLink *out_link)
+do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, NMLinkType expected_link_type, NMPlatformLink *out_link)
{
- struct nl_object *l;
+ const NMPObject *obj;
- if (type == NM_LINK_TYPE_BOND) {
- /* When the kernel loads the bond module, either via explicit modprobe
- * or automatically in response to creating a bond master, it will also
- * create a 'bond0' interface. Since the bond we're about to create may
- * or may not be named 'bond0' prevent potential confusion about a bond
- * that the user didn't want by telling the bonding module not to create
- * bond0 automatically.
- */
- if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
- nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL);
- }
+ do_add_link (platform, name, nlo);
- debug ("link: add link '%s' of type '%s' (%d)",
- name, nm_link_type_to_string (type), (int) type);
+ 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 !!obj;
+}
- l = build_rtnl_link (0, name, type);
+static gboolean
+do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo)
+{
+ int nle;
- 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);
+ 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));
- rtnl_link_set_addr ((struct rtnl_link *) l, nladdr);
- }
+ event_handler_read_netlink_all (platform, FALSE);
- if (!add_object (platform, l))
+ 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));
+
+ delayed_action_handle_all (platform, TRUE);
- return link_get_by_name (platform, name, out_link);
+ /* 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.
+ * Instead the return value says, whether kernel_add_object() succeeded. */
+ return TRUE;
}
-static struct rtnl_link *
-link_get (NMPlatform *platform, int ifindex)
+
+static gboolean
+do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+ auto_nl_object struct nl_object *nlo_free = NULL;
+ int nle;
- if (!rtnllink) {
- platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
- return NULL;
+ event_handler_read_netlink_all (platform, FALSE);
+
+ 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));
+
+ delayed_action_handle_all (platform, TRUE);
+
+ /* 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);
+ }
}
- return rtnllink;
+ /* 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;
}
-static gboolean
-link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change)
+static NMPlatformError
+do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache)
{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
int nle;
+ int ifindex;
+ gboolean complete_from_cache2 = complete_from_cache;
- if (!rtnllink)
- return FALSE;
- g_return_val_if_fail (rtnl_link_get_ifindex (change) > 0, FALSE);
+ ifindex = rtnl_link_get_ifindex (nlo);
+ if (ifindex <= 0)
+ g_return_val_if_reached (NM_PLATFORM_ERROR_BUG);
- nle = rtnl_link_change (priv->nlh, rtnllink, change, 0);
+ nle = kernel_change_link (platform, nlo, &complete_from_cache2);
- /* 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:
+ _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:
- 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;
+ /* fall-through */
default:
- error ("Netlink error changing link %s: %s", to_string_link (platform, change), nl_geterror (nle));
- return FALSE;
+ if (complete_from_cache != complete_from_cache2)
+ _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 nle == -NLE_OBJ_NOTFOUND ? NM_PLATFORM_ERROR_NO_FIRMWARE : NM_PLATFORM_ERROR_UNSPECIFIED;
}
- return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL);
+ /* 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 NM_PLATFORM_ERROR_SUCCESS;
+}
+
+static gboolean
+link_add (NMPlatform *platform,
+ const char *name,
+ NMLinkType type,
+ const void *address,
+ size_t address_len,
+ NMPlatformLink *out_link)
+{
+ 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
+ * or automatically in response to creating a bond master, it will also
+ * create a 'bond0' interface. Since the bond we're about to create may
+ * or may not be named 'bond0' prevent potential confusion about a bond
+ * that the user didn't want by telling the bonding module not to create
+ * bond0 automatically.
+ */
+ if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
+ nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL);
+ }
+
+ debug ("link: add link '%s' of type '%s' (%d)",
+ name, nm_link_type_to_string (type), (int) type);
+
+ l = build_rtnl_link (0, name, type);
+
+ g_assert ( (address != NULL) ^ (address_len == 0) );
+ if (address) {
+ auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, address_len);
+
+ rtnl_link_set_addr ((struct rtnl_link *) l, nladdr);
+ }
+
+ return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link);
}
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) {
- platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+ obj = nmp_cache_lookup_link (priv->cache, ifindex);
+ if (!obj || !obj->_link.netlink.is_in_netlink)
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;
+
+ g_return_val_if_fail (ifname, 0);
- return rtnl_link_name2i (priv->link_cache, ifname);
+ obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
+ 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL);
+ 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");
@@ -2437,20 +2854,16 @@ 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);
-
- return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL);
+ do_request_link (platform, ifindex, NULL, TRUE);
+ return !!cache_lookup_link (platform, ifindex);
}
static gboolean
@@ -2462,7 +2875,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
@@ -2471,98 +2886,108 @@ 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 = _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];
+
+ obj_cache = cache_lookup_link (platform, ifindex);
+ if (!obj_cache)
+ return NM_PLATFORM_ERROR_NOT_FOUND;
+ 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
-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
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;
- }
-
- 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);
+ if (obj && obj->link.inet6_token.is_valid) {
+ *iid = obj->link.inet6_token.iid;
return TRUE;
}
#endif
return FALSE;
}
+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 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)
{
#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;
- }
- }
+ 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;
}
@@ -2572,14 +2997,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 = _nl_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) == NM_PLATFORM_ERROR_SUCCESS;
}
#endif
return FALSE;
@@ -2603,50 +3028,47 @@ 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
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);
- 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) == NM_PLATFORM_ERROR_SUCCESS;
}
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)
@@ -2666,20 +3088,20 @@ 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);
- return link_change (platform, ifindex, change);
+ return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS;
}
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 *
@@ -2733,8 +3155,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;
@@ -2752,66 +3173,61 @@ 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) == NM_PLATFORM_ERROR_SUCCESS;
}
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) == NM_PLATFORM_ERROR_SUCCESS;
}
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);
- return link_change (platform, slave, change);
+ return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS;
}
static gboolean
@@ -2823,9 +3239,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 *
@@ -2877,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:
@@ -2918,30 +3332,31 @@ 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;
+ gs_free char *path = NULL;
+ gs_free char *id = NULL;
+ gs_free char *ifname = NULL;
+
+ obj_parent = nmp_cache_lookup_link (priv->cache, parent);
+ if (!obj_parent || !obj_parent->link.name[0])
+ g_return_val_if_reached (FALSE);
- parent_name = nm_platform_link_get_name (platform, parent);
- g_return_val_if_fail (parent_name != NULL, FALSE);
+ 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 (parent_name));
+ 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 (success) {
- gs_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key);
- auto_nl_object struct rtnl_link *rtnllink = NULL;
+ if (!nm_platform_sysctl_set (platform, path, id))
+ return FALSE;
- 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_request_link (platform, 0, ifname, TRUE);
- return success;
+ 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 !!obj;
}
typedef struct {
@@ -3003,21 +3418,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);
+ 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;
/* Fall back to reading sysfs */
@@ -3188,20 +3603,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);
+ err = _nl_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);
}
@@ -3328,8 +3743,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));
@@ -3382,8 +3797,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));
@@ -3593,53 +4008,27 @@ link_get_driver_info (NMPlatform *platform,
/******************************************************************/
-static gboolean
-_address_match (struct rtnl_addr *addr, int family, int ifindex)
+static GArray *
+ipx_address_get_all (NMPlatform *platform, int ifindex, gboolean is_v4)
{
- g_return_val_if_fail (addr, FALSE);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ ObjectType obj_type = is_v4 ? OBJECT_TYPE_IP4_ADDRESS : OBJECT_TYPE_IP6_ADDRESS;
- return rtnl_addr_get_family (addr) == family &&
- (ifindex == 0 || rtnl_addr_get_ifindex (addr) == ifindex);
+ 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)
{
- 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))
@@ -3663,10 +4052,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 */
@@ -3678,7 +4067,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) {
@@ -3686,14 +4075,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) {
@@ -3703,7 +4092,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
@@ -3735,6 +4124,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,
@@ -3745,10 +4168,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
@@ -3761,83 +4190,81 @@ 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;
-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);
+ 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
-ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen)
+ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr 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);
+ NMPObject obj_needle;
- return !!cached_object;
+ nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen);
+ return do_delete_object (platform, &obj_needle, NULL);
}
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;
@@ -3845,85 +4272,36 @@ ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPla
/******************************************************************/
-static gboolean
-_route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel)
+static GArray *
+ipx_route_get_all (NMPlatform *platform, int ifindex, gboolean is_v4, NMPlatformGetRouteMode mode)
{
- 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;
+ 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);
- nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
- return rtnl_route_nh_get_ifindex (nexthop) == ifindex;
+ 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)
{
- 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
@@ -3956,26 +4334,26 @@ 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);
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 = _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);
@@ -3989,111 +4367,113 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source,
return (struct nl_object *) rtnlroute;
}
-static gboolean
-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)
+struct nl_object *
+_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
- return add_object (platform, build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss));
+ 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);
}
-static gboolean
-ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
- struct in6_addr network, int plen, struct in6_addr gateway,
- guint32 metric, guint32 mss)
+struct nl_object *
+_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
- metric = nm_utils_ip6_route_metric_normalize (metric);
+ const NMPlatformIP6Route *obj = (const NMPlatformIP6Route *) _obj;
- return add_object (platform, build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss));
+ return build_rtnl_route (AF_INET6,
+ obj->ifindex,
+ obj->source,
+ &obj->network,
+ obj->plen,
+ &obj->gateway,
+ NULL,
+ obj->metric,
+ obj->mss);
}
-static struct rtnl_route *
-route_search_cache (struct nl_cache *cache, int family, int ifindex, const void *network, int plen, guint32 metric)
+static gboolean
+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)
{
- 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);
+ NMPObject obj_needle;
+ auto_nl_object struct nl_object *nlo = NULL;
- 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;
+ 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
-refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, guint32 metric)
+ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
+ struct in6_addr network, int plen, struct in6_addr gateway,
+ guint32 metric, guint32 mss)
{
- struct nl_cache *cache;
- auto_nl_object struct rtnl_route *cached_object = NULL;
+ NMPObject obj_needle;
+ auto_nl_object struct nl_object *nlo = 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);
+ metric = nm_utils_ip6_route_metric_normalize (metric);
- if (cached_object)
- return refresh_object (platform, (struct nl_object *) cached_object, TRUE, NM_PLATFORM_REASON_INTERNAL);
- return TRUE;
+ 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 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.
* 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. */
- if (!refresh_object (platform, (struct nl_object *) route, FALSE, _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)) {
- rtnl_route_put ((struct rtnl_route *) route);
- return TRUE;
- }
+ * 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);
}
- /* 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);
+ 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;
+ }
if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) {
/* When searching for a matching IPv4 route to delete, the kernel
@@ -4110,8 +4490,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.
@@ -4120,7 +4500,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. */
@@ -4132,179 +4512,43 @@ 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);
-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);
+ nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric);
- if (!cached_object)
- cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric);
- return !!cached_object;
+ return do_delete_object (platform, &obj_needle, nlo);
}
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)
{
- metric = nm_utils_ip6_route_metric_normalize (metric);
-
- return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, metric);
-}
-
-/******************************************************************/
-
-/* 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);
- }
-}
+ NMPObject obj_needle;
-/* 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);
- }
+ metric = nm_utils_ip6_route_metric_normalize (metric);
- /* 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);
+ 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));
}
/******************************************************************/
@@ -4334,35 +4578,42 @@ 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:
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;
-
- 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);
- cache_repopulate_all (platform);
+ _nl_sock_flush_data (priv->nlh_event);
+ 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);
@@ -4371,6 +4622,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)
{
@@ -4387,7 +4703,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);
@@ -4407,11 +4724,21 @@ 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)
{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- auto_nl_object struct rtnl_link *rtnllink = NULL;
const char *ifname;
int ifindex;
@@ -4437,49 +4764,37 @@ udev_device_added (NMPlatform *platform,
return;
}
- g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
- g_object_ref (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;
- }
+ cache_update_link_udev (platform, ifindex, udev_device);
+}
- 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
@@ -4514,8 +4829,20 @@ handle_udev_event (GUdevClient *client,
/******************************************************************/
static void
-nm_linux_platform_init (NMLinuxPlatform *platform)
+nm_linux_platform_init (NMLinuxPlatform *self)
{
+ NMLinuxPlatformPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate);
+
+ 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_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);
}
static void
@@ -4530,10 +4857,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);
@@ -4550,7 +4879,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);
@@ -4561,37 +4890,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);
-
- 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
+ (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);
- 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. */
- 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));
+ /* complete construction of the GObject instance before populating the cache. */
+ G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object);
- priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
+ _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);
+
+ delayed_action_handle_all (platform, FALSE);
/* And read initial device list */
enumerator = g_udev_enumerator_new (priv->udev_client);
@@ -4606,8 +4924,26 @@ constructed (GObject *_object)
}
g_list_free (devices);
g_object_unref (enumerator);
+}
- G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object);
+static void
+dispose (GObject *object)
+{
+ NMPlatform *platform = NM_PLATFORM (object);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+ _LOGD ("dispose");
+
+ 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);
}
static void
@@ -4615,17 +4951,18 @@ nm_linux_platform_finalize (GObject *object)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object);
+ nmp_cache_free (priv->cache);
+
+ g_ptr_array_unref (priv->delayed_action.list_master_connected);
+ g_ptr_array_unref (priv->delayed_action.list_refresh_link);
+
/* Free netlink resources */
g_source_remove (priv->event_id);
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);
@@ -4643,6 +4980,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;
@@ -4669,6 +5007,8 @@ 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_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;
@@ -4748,3 +5088,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;
}
+
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 {
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 7439b1ef42..230d7580ad 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -27,6 +27,7 @@
#include <arpa/inet.h>
#include <string.h>
#include <netlink/route/addr.h>
+#include <netlink/route/rtnl.h>
#include "gsystem-local-alloc.h"
#include "NetworkManagerUtils.h"
@@ -37,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))
@@ -55,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 */
@@ -133,73 +146,41 @@ 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_NONE);
-
- return self->error;
-}
-
-/**
- * nm_platform_get_error_message:
- * @self: platform instance
+ * nm_platform_error_to_string:
+ * @error_code: the error code to stringify.
*
- * Returns: Static human-readable string for the error. Don't free.
+ * Returns: A string representation of the error.
+ * For negative numbers, this function interprets
+ * the code as -errno.
*/
const char *
-nm_platform_get_error_msg (NMPlatform *self)
-{
- _CHECK_SELF (self, klass, NULL);
-
- switch (self->error) {
- case NM_PLATFORM_ERROR_NONE:
- return "unknown error";
+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 "object not found";
+ return "not-found";
case NM_PLATFORM_ERROR_EXISTS:
- return "object already exists";
+ return "exists";
case NM_PLATFORM_ERROR_WRONG_TYPE:
- return "object is wrong type";
+ return "wrong-type";
case NM_PLATFORM_ERROR_NOT_SLAVE:
- return "link not a slave";
+ return "not-slave";
case NM_PLATFORM_ERROR_NO_FIRMWARE:
- return "firmware not found";
+ return "no-firmware";
default:
- return "invalid error number";
+ if (error < 0)
+ return g_strerror (- ((int) error));
+ return "unknown";
}
}
-static void
-reset_error (NMPlatform *self)
-{
- g_assert (self);
- self->error = NM_PLATFORM_ERROR_NONE;
-}
+/******************************************************************/
#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr"
#define IFA_F_NOPREFIXROUTE_STR "noprefixroute"
@@ -266,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);
}
@@ -318,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);
}
@@ -397,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);
@@ -540,6 +516,35 @@ 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)
+ return NM_PLATFORM_ERROR_WRONG_TYPE;
+ 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
@@ -550,12 +555,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,
@@ -563,29 +572,22 @@ nm_platform_link_add (NMPlatform *self,
size_t address_len,
NMPlatformLink *out_link)
{
- int ifindex;
+ NMPlatformError plerr;
- _CHECK_SELF (self, klass, FALSE);
- reset_error (self);
+ _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
- 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;
- reset_error(self);
- return klass->link_add (self, name, type, address, address_len, out_link);
+ debug ("link: adding %s '%s'", nm_link_type_to_string (type), name);
+ if (!klass->link_add (self, name, type, address, address_len, out_link))
+ return NM_PLATFORM_ERROR_UNSPECIFIED;
+ return NM_PLATFORM_ERROR_SUCCESS;
}
/**
@@ -596,12 +598,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);
}
@@ -621,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;
}
@@ -629,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)
@@ -639,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);
@@ -666,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;
}
@@ -695,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);
@@ -703,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;
}
@@ -722,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);
@@ -742,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);
@@ -763,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);
@@ -809,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);
@@ -830,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);
@@ -849,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);
@@ -868,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);
@@ -892,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);
@@ -902,6 +883,29 @@ 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);
+
+ g_return_val_if_fail (ifindex >= 0, NULL);
+
+ if (klass->link_get_udi)
+ return klass->link_get_udi (self, ifindex);
+ return NULL;
+}
+
+GObject *
+nm_platform_link_get_udev_device (NMPlatform *self, int ifindex)
+{
+ _CHECK_SELF (self, klass, FALSE);
+
+ g_return_val_if_fail (ifindex >= 0, NULL);
+
+ if (klass->link_get_udev_device)
+ return klass->link_get_udev_device (self, ifindex);
+ return NULL;
+}
/**
* nm_platform_link_get_user_ip6vll_enabled:
@@ -918,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);
@@ -943,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);
@@ -965,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);
@@ -989,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;
@@ -1015,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;
@@ -1054,20 +1053,20 @@ 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);
g_return_val_if_fail (ifindex > 0, FALSE);
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);
}
/**
@@ -1081,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);
@@ -1101,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);
@@ -1121,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);
@@ -1142,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);
@@ -1163,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);
@@ -1189,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);
@@ -1213,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);
@@ -1232,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);
@@ -1260,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);
@@ -1284,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);
@@ -1308,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,
@@ -1336,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);
}
@@ -1358,14 +1341,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);
}
@@ -1377,10 +1359,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);
}
@@ -1392,10 +1373,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);
}
@@ -1409,7 +1389,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,
@@ -1417,30 +1397,30 @@ nm_platform_vlan_add (NMPlatform *self,
guint32 vlanflags,
NMPlatformLink *out_link)
{
- _CHECK_SELF (self, klass, FALSE);
- reset_error (self);
+ NMPlatformError plerr;
- 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);
+ _CHECK_SELF (self, klass, 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;
- }
+ 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);
+
+ 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
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);
@@ -1454,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);
@@ -1467,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);
@@ -1481,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);
@@ -1494,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);
@@ -1513,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);
@@ -1525,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);
@@ -1533,35 +1507,34 @@ 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);
- reset_error (self);
+ _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
- 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) {
- self->error = NM_PLATFORM_ERROR_WRONG_TYPE;
- return FALSE;
- }
+ 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)
+ 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
@@ -1572,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);
@@ -1584,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);
@@ -1596,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);
@@ -1608,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);
@@ -1620,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);
@@ -1632,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);
@@ -1644,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);
@@ -1655,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);
@@ -1666,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);
@@ -1677,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);
@@ -1688,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);
@@ -1699,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);
@@ -1710,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);
@@ -1727,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);
@@ -1738,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);
@@ -1750,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);
@@ -1761,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);
@@ -1772,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);
@@ -1783,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);
@@ -1821,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);
@@ -1833,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);
@@ -1852,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);
@@ -1890,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);
@@ -1922,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);
@@ -1944,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);
@@ -1960,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);
@@ -1972,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);
@@ -2086,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
@@ -2247,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);
@@ -2259,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);
@@ -2275,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);
@@ -2307,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);
@@ -2334,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);
@@ -2350,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);
@@ -2364,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);
@@ -2375,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);
@@ -2388,6 +2325,10 @@ 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_RTM_F_CLONED:
+ return "rtm-f-cloned";
case NM_IP_CONFIG_SOURCE_KERNEL:
return "kernel";
case NM_IP_CONFIG_SOURCE_SHARED:
@@ -2432,28 +2373,38 @@ _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, g
return buf;
}
-static char to_string_buffer[256];
+char _nm_platform_to_string_buffer[256];
const char *
nm_platform_link_to_string (const NMPlatformLink *link)
{
char master[20];
char parent[20];
- char *driver, *udi;
- GString *str;
+ char str_vlan[16];
+ 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);
- if (!link->arp)
- g_string_append (str, "NOARP,");
- if (link->up)
- g_string_append (str, "UP");
+ str_flags = g_string_new (NULL);
+ if (NM_FLAGS_HAS (link->flags, IFF_NOARP))
+ g_string_append (str_flags, "NOARP,");
+ if (NM_FLAGS_HAS (link->flags, IFF_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) {
+ 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)
g_snprintf (master, sizeof (master), " master %d", link->master);
@@ -2465,23 +2416,69 @@ nm_platform_link_to_string (const NMPlatformLink *link)
else
parent[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 */
+ if (link->vlan_id)
+ g_snprintf (str_vlan, sizeof (str_vlan), " vlan %u", (guint) link->vlan_id);
+ else
+ str_vlan[0] = '\0';
+
+ 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';
+
+ 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 */
+ " arp %u" /* arptype */
+ "%s%s" /* link->type */
"%s%s" /* kind */
- "%s%s",
- link->ifindex, link->name, parent, str->str,
+ "%s" /* is-in-udev */
+ "%s" /* addr-gen-mode */
+ "%s%s" /* addr */
+ "%s%s" /* inet6_token */
+ "%s%s" /* driver */
+ ,
+ 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 : "",
- driver ? driver : "", udi ? udi : "");
- g_string_free (str, TRUE);
- g_free (driver);
- g_free (udi);
- return to_string_buffer;
+ str_vlan,
+ 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 : "");
+ g_string_free (str_flags, TRUE);
+ return _nm_platform_to_string_buffer;
}
/**
@@ -2534,14 +2531,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;
}
/**
@@ -2618,7 +2615,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,
@@ -2626,7 +2623,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;
}
/**
@@ -2646,6 +2643,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,12 +2652,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",
+ 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));
- return to_string_buffer;
+ 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 _nm_platform_to_string_buffer;
}
/**
@@ -2687,12 +2687,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) \
@@ -2741,6 +2741,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), \
@@ -2758,14 +2766,21 @@ 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);
+ _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;
}
@@ -2812,6 +2827,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;
}
@@ -3029,6 +3045,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)
{
}
@@ -3046,8 +3091,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 a4482d2458..afb94c1ae9 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -29,6 +29,7 @@
#include <nm-dbus-interface.h>
#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))
@@ -39,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. */
@@ -50,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 <errno.h> 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 {
@@ -90,19 +100,44 @@ 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). */
- const char *udi;
-
/* NMPlatform initializes this field with a static string. */
const char *driver;
gboolean initialized;
int master;
int parent;
- gboolean up;
+
+ /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */
+ guint32 arptype;
+
+ /* rtnl_link_get_addr() */
+ struct {
+ guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */
+ 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.*/
+ guint8 inet6_addr_gen_mode_inv;
+
+ /* 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;
+
+ /* @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;
- gboolean arp;
+
guint mtu;
};
@@ -237,6 +272,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));
@@ -275,6 +314,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;
@@ -361,8 +401,6 @@ typedef struct {
struct _NMPlatform {
GObject parent;
-
- NMPlatformError error;
};
typedef struct {
@@ -389,7 +427,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);
@@ -397,6 +435,8 @@ 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);
+ 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);
@@ -529,11 +569,25 @@ 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);
-NMPlatformError nm_platform_get_error (NMPlatform *self);
-const char *nm_platform_get_error_msg (NMPlatform *self);
+const char *nm_platform_error_to_string (NMPlatformError error);
gboolean nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value);
char *nm_platform_sysctl_get (NMPlatform *self, const char *path);
@@ -545,10 +599,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);
@@ -561,7 +615,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);
@@ -570,6 +624,9 @@ 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);
+
+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);
@@ -600,12 +657,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/nmp-object.c b/src/platform/nmp-object.c
new file mode 100644
index 0000000000..2765d113d1
--- /dev/null
+++ b/src/platform/nmp-object.c
@@ -0,0 +1,1941 @@
+/* -*- 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 <unistd.h>
+
+#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;
+ 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)
+ 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.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)
+{
+ 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);
+ }
+ 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));
+
+ 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
new file mode 100644
index 0000000000..99f0eaf0c8
--- /dev/null
+++ b/src/platform/nmp-object.h
@@ -0,0 +1,383 @@
+/* -*- 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"
+#include "nm-multi-index.h"
+#include "nm-macros-internal.h"
+
+#include <netlink/netlink.h>
+#include <gudev/gudev.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;
+
+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/.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/dump.c b/src/platform/tests/dump.c
index d28d517936..575ce6fb34 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);
@@ -43,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)
diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c
index d46d140abd..fe148518c2 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
@@ -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)
@@ -858,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 ();
@@ -892,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 c3bc0dcb2b..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_signal (address_changed);
+ 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_signal (address_changed);
+ 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);
@@ -175,7 +161,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",
@@ -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);*/
@@ -255,7 +237,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 962730463f..640d3a6f95 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -35,10 +35,10 @@ 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)));
+ 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);
@@ -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-common.h b/src/platform/tests/test-common.h
index 563d9fb4f6..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_NONE)
-
typedef struct {
int handler_id;
const char *name;
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,6 +20,9 @@
#include "nm-platform-utils.h"
+#include <linux/rtnetlink.h>
+
+#include "nm-linux-platform.h"
#include "nm-logging.h"
#include "nm-test-utils.h"
@@ -27,6 +30,16 @@
/******************************************************************/
+static void
+test_init_linux_platform ()
+{
+ gs_unref_object NMPlatform *platform = NULL;
+
+ platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL);
+}
+
+/******************************************************************/
+
NMTST_DEFINE ();
int
@@ -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 ();
}
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 102fe795f7..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));
- error (NM_PLATFORM_ERROR_NOT_FOUND);
+ g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
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
@@ -85,48 +63,52 @@ 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;
+ 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);
{
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);
+ 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);
+ } else
+ 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:
@@ -142,6 +124,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,20 +139,29 @@ 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);
+ 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);
+
+ /* 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));
- accept_signal (master_changed);
+ 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);
/* Master with a disconnected slave is disconnected
*
@@ -179,7 +173,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;
@@ -202,28 +196,28 @@ 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));
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);
+ accept_signals (link_changed, 1, 3);
+ /* 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);
+ g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex));
+ accept_signals (link_changed, 0, 2);
+ ensure_no_signal (master_changed);
/* Set slave option */
switch (type) {
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);
}
@@ -233,18 +227,28 @@ 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_signal (master_changed);
+ 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);
+ 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);
@@ -264,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);
@@ -277,18 +280,16 @@ 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));
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);
@@ -298,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);
}
@@ -308,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);
@@ -332,20 +329,17 @@ 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));
- 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) {
@@ -358,7 +352,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);
}
@@ -404,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));
- no_error ();
+ g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS);
accept_signal (link_added);
/* Try to add again */
- g_assert (!nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL));
- error (NM_PLATFORM_ERROR_EXISTS);
+ g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS);
/* Check device index, name and type */
ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
@@ -427,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)); 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 */
@@ -462,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);
@@ -522,23 +509,17 @@ 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);
+ 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-nmp-object.c b/src/platform/tests/test-nmp-object.c
new file mode 100644
index 0000000000..b922599cb3
--- /dev/null
+++ b/src/platform/tests/test-nmp-object.c
@@ -0,0 +1,426 @@
+/* -*- 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"
+
+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);
+}
+
+/******************************************************************/
+
+NMTST_DEFINE ();
+
+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;
+}
+
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 70bd1a1040..cd4217d248 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -1,5 +1,7 @@
#include "config.h"
+#include <linux/rtnetlink.h>
+
#include "test-common.h"
#include "nm-test-utils.h"
#include "NetworkManagerUtils.h"
@@ -64,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);
@@ -72,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);
@@ -80,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);
@@ -88,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);
@@ -96,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);
@@ -104,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);
@@ -136,36 +132,27 @@ 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_signal (route_changed);
+ 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_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);
@@ -177,6 +164,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 +172,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,20 +180,18 @@ 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);
/* 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);
@@ -232,36 +219,27 @@ 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_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));
- 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_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);
@@ -288,19 +266,16 @@ 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);
/* 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);
@@ -320,11 +295,11 @@ 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);
- 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-general-with-expect.c b/src/tests/test-general-with-expect.c
index 9491801d42..f671c15deb 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"
@@ -494,6 +495,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
@@ -505,6 +862,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 ();
}
diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c
index c7b60e68e0..85397ae7fb 100644
--- a/src/tests/test-route-manager.c
+++ b/src/tests/test-route-manager.c
@@ -22,6 +22,7 @@
#include <glib.h>
#include <arpa/inet.h>
+#include <linux/rtnetlink.h>
#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,11 +245,12 @@ 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),
},
};
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 ();
@@ -253,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 ();
@@ -575,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 ();
@@ -589,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);
@@ -639,11 +648,11 @@ 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");
- 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,
@@ -651,11 +660,11 @@ 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");
- 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,
diff --git a/valgrind.suppressions b/valgrind.suppressions
index 6bcd4c9d09..5a345fd93c 100644
--- a/valgrind.suppressions
+++ b/valgrind.suppressions
@@ -419,24 +419,6 @@
{
# 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().
libnl_rtnl_link_set_type_02
Memcheck:Leak