diff options
| author | Michael Biebl <biebl@debian.org> | 2015-07-14 19:38:58 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-07-14 19:38:58 +0200 |
| commit | 50a58f0fabd8a34c1b6108a107e08abe3c1ccd24 (patch) | |
| tree | 6790165f39daee79e2b6c6617483320613493367 /src/platform | |
| parent | f408e27bccfacf347605a8d98649975a68f38a17 (diff) | |
Imported Upstream version 1.0.4 upstream/1.0.4
Diffstat (limited to 'src/platform')
24 files changed, 8772 insertions, 4331 deletions
diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in index a41de1ae..0616c88b 100644 --- a/src/platform/Makefile.in +++ b/src/platform/Makefile.in @@ -195,6 +195,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -308,6 +309,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 7b0d2d64..d5aeb030 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 { @@ -45,8 +49,9 @@ typedef struct { NMPlatformLink link; char *udi; - 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)) @@ -55,11 +60,12 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) /******************************************************************/ -void -nm_fake_platform_setup (void) -{ - nm_platform_setup (NM_TYPE_FAKE_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); /******************************************************************/ @@ -110,25 +116,31 @@ 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.type_name = type_to_type_name (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; } static NMFakePlatformLink * @@ -146,7 +158,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; } @@ -164,18 +175,60 @@ link_get_all (NMPlatform *platform) return links; } -static gboolean -_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l) +static const NMPlatformLink * +_nm_platform_link_get (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (device) - *l = device->link; - return !!device; + return device ? &device->link : NULL; +} + +static const NMPlatformLink * +_nm_platform_link_get_by_ifname (NMPlatform *platform, const char *ifname) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + guint i; + + for (i = 0; i < priv->links->len; i++) { + NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + + if (!strcmp (device->link.name, ifname)) + return &device->link; + } + return NULL; +} + +static const NMPlatformLink * +_nm_platform_link_get_by_address (NMPlatform *platform, + gconstpointer address, + size_t length) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + guint i; + + if ( length == 0 + || length > NM_UTILS_HWADDR_LEN_MAX + || !address) + g_return_val_if_reached (NULL); + + for (i = 0; i < priv->links->len; i++) { + NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + + if ( device->link.addr.len == length + && memcmp (device->link.addr.data, address, length) == 0) { + return &device->link; + } + } + return NULL; } static gboolean -link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) +link_add (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMFakePlatformLink device; @@ -184,9 +237,14 @@ link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *a g_array_append_val (priv->links, device); - 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); + if (device.link.ifindex) { + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, 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; } @@ -230,99 +288,88 @@ link_delete (NMPlatform *platform, int ifindex) memset (route, 0, sizeof (*route)); } - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } -static int -link_get_ifindex (NMPlatform *platform, const char *name) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); - int i; - - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - - if (device && !g_strcmp0 (device->link.name, name)) - return device->link.ifindex; - } - - return 0; -} - -static const char * -link_get_name (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.name : NULL; -} - -static NMLinkType -link_get_type (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.type : NM_LINK_TYPE_NONE; -} - static const char * link_get_type_name (NMPlatform *platform, int ifindex) { - return type_to_type_name (link_get_type (platform, ifindex)); + return type_to_type_name (nm_platform_link_get_type (platform, ifindex)); } 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, NMP_OBJECT_TYPE_LINK, 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; } @@ -335,10 +382,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; } @@ -351,9 +400,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; } @@ -366,64 +415,32 @@ 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; } static gboolean -link_is_up (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.up : FALSE; -} - -static gboolean -link_is_connected (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.connected : FALSE; -} - -static gboolean -link_uses_arp (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.arp : FALSE; -} - -static gboolean link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (device->address) - g_bytes_unref (device->address); - - device->address = g_bytes_new (addr, len); - - link_changed (platform, link_get (platform, ifindex)); - - return TRUE; -} - -static gconstpointer -link_get_address (NMPlatform *platform, int ifindex, size_t *length) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - if (!device || !device->address) { - if (length) - *length = 0; - return NULL; + if ( len == 0 + || len > NM_UTILS_HWADDR_LEN_MAX + || !addr) + g_return_val_if_reached (FALSE); + + if ( device->link.addr.len != len + || ( len > 0 + && memcmp (device->link.addr.data, addr, len) != 0)) { + memcpy (device->link.addr.data, addr, len); + device->link.addr.len = len; + link_changed (platform, link_get (platform, ifindex), TRUE); } - return g_bytes_get_data (device->address, length); + return TRUE; } static gboolean @@ -433,45 +450,37 @@ 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; } -static guint32 -link_get_mtu (NMPlatform *platform, int ifindex) +static const char * +link_get_udi (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - return device ? device->link.mtu : 0; -} - -static char * -link_get_physical_port_id (NMPlatform *platform, int ifindex) -{ - /* We call link_get just to cause an error to be set if @ifindex is bad. */ - link_get (platform, ifindex); - - return NULL; -} - -static guint -link_get_dev_id (NMPlatform *platform, int ifindex) -{ - /* We call link_get just to cause an error to be set if @ifindex is bad. */ - link_get (platform, ifindex); - - return 0; + if (!device) + return NULL; + return device->udi; } static gboolean -link_get_wake_on_lan (NMPlatform *platform, int ifindex) -{ - /* We call link_get just to cause an error to be set if @ifindex is bad. */ - link_get (platform, ifindex); +link_get_driver_info (NMPlatform *platform, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) +{ + if (out_driver_name) + *out_driver_name = NULL; + if (out_driver_version) + *out_driver_version = NULL; + if (out_fw_version) + *out_fw_version = NULL; - return FALSE; + return TRUE; } static gboolean @@ -510,12 +519,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; } @@ -529,29 +547,17 @@ 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; } -static int -link_get_master (NMPlatform *platform, int slave) -{ - NMFakePlatformLink *device = link_get (platform, slave); - - g_return_val_if_fail (device, FALSE); - - return device->link.master; -} - static gboolean master_set_option (NMPlatform *platform, int master, const char *option, const char *value) { @@ -585,20 +591,22 @@ slave_get_option (NMPlatform *platform, int slave, const char *option) } static gboolean -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags) +vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, NMPlatformLink *out_link) { NMFakePlatformLink *device; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0)) + if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0, NULL)) return FALSE; - device = link_get (platform, link_get_ifindex (platform, name)); + device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); g_return_val_if_fail (device, FALSE); device->vlan_id = vlan_id; device->link.parent = parent; + if (out_link) + *out_link = device->link; return TRUE; } @@ -630,20 +638,44 @@ vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) } static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key) +infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatformLink *out_link) { - NMFakePlatformLink *parent_device; - char *name; - gboolean success; + NMFakePlatformLink *device, *parent_device; + gs_free char *name = NULL; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key); - success = link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0); - g_free (name); + if (!link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0, out_link)) + return FALSE; + + device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); + g_return_val_if_fail (device, FALSE); + + device->ib_p_key = p_key; + device->link.parent = parent; - return success; + return TRUE; +} + +static gboolean +infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, const char **mode) +{ + NMFakePlatformLink *device; + + device = link_get (platform, ifindex); + g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (device->link.type == NM_LINK_TYPE_INFINIBAND, FALSE); + + if (parent) + *parent = device->link.parent; + if (p_key) + *p_key = device->ib_p_key; + if (mode) + *mode = "datagram"; + + return TRUE; } static gboolean @@ -861,12 +893,12 @@ ip4_address_add (NMPlatform *platform, int ifindex, continue; memcpy (item, &address, sizeof (address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } g_array_append_val (priv->ip4_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -901,13 +933,15 @@ 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, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + } return TRUE; } g_array_append_val (priv->ip6_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -927,7 +961,7 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, memcpy (&deleted_address, address, sizeof (deleted_address)); memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } } @@ -950,7 +984,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int memcpy (&deleted_address, address, sizeof (deleted_address)); memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } } @@ -958,8 +992,8 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int return TRUE; } -static gboolean -ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +static const NMPlatformIP4Address * +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; @@ -968,14 +1002,14 @@ ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); if (address->ifindex == ifindex && address->plen == plen && address->address == addr) - return TRUE; + return address; } - return FALSE; + return NULL; } -static gboolean -ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +static const NMPlatformIP6Address * +ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; @@ -985,41 +1019,36 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int if (address->ifindex == ifindex && address->plen == plen && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) - return TRUE; + return address; } - return FALSE; -} - -static gboolean -ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - return FALSE; + return NULL; } /******************************************************************/ static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); GArray *routes; NMPlatformIP4Route *route; guint i; - 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 (TRUE, TRUE, sizeof (NMPlatformIP4Route)); + if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) + flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; + /* Fill routes */ for (i = 0; i < priv->ip4_routes->len; i++) { route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); if (route && (!ifindex || route->ifindex == ifindex)) { if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) g_array_append_val (routes, *route); } else { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) g_array_append_val (routes, *route); } } @@ -1029,26 +1058,27 @@ ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mod } static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); GArray *routes; NMPlatformIP6Route *route; guint i; - 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 (TRUE, TRUE, sizeof (NMPlatformIP6Route)); + if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) + flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; + /* Fill routes */ for (i = 0; i < priv->ip6_routes->len; i++) { route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); if (route && (!ifindex || route->ifindex == ifindex)) { if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) g_array_append_val (routes, *route); } else { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) g_array_append_val (routes, *route); } } @@ -1058,6 +1088,56 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mod } static gboolean +ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip4_routes->len; i++) { + NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + NMPlatformIP4Route deleted_route; + + if ( route->ifindex != ifindex + || route->network != network + || route->plen != plen + || route->metric != metric) + continue; + + memcpy (&deleted_route, route, sizeof (deleted_route)); + g_array_remove_index (priv->ip4_routes, i); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + } + + return TRUE; +} + +static gboolean +ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + metric = nm_utils_ip6_route_metric_normalize (metric); + + for (i = 0; i < priv->ip6_routes->len; i++) { + NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); + NMPlatformIP6Route deleted_route; + + if ( route->ifindex != ifindex + || !IN6_ARE_ADDR_EQUAL (&route->network, &network) + || route->plen != plen + || route->metric != metric) + continue; + + memcpy (&deleted_route, route, sizeof (deleted_route)); + g_array_remove_index (priv->ip6_routes, i); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + } + + return TRUE; +} + +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) @@ -1065,22 +1145,41 @@ 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; route.ifindex = ifindex; route.source = source; - route.network = network; + route.network = nm_utils_ip4_address_clear_host_address (network, plen); route.plen = plen; 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++) { + NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, + NMPlatformIP4Route, i); + guint32 gate = ntohl (item->network) >> (32 - item->plen); + guint32 host = ntohl (gateway) >> (32 - item->plen); + + if (ifindex == item->ifindex && gate == host) + break; + } + if (i == priv->ip4_routes->len) { + 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; + } + } for (i = 0; i < priv->ip4_routes->len; i++) { NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - if (item->ifindex != route.ifindex) - continue; if (item->network != route.network) continue; if (item->plen != route.plen) @@ -1088,13 +1187,19 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, if (item->metric != metric) continue; + if (item->ifindex != route.ifindex) { + ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); + i--; + continue; + } + memcpy (item, &route, sizeof (route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } g_array_append_val (priv->ip4_routes, route); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -1114,17 +1219,34 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, route.source = NM_IP_CONFIG_SOURCE_KERNEL; route.ifindex = ifindex; route.source = source; - route.network = network; + nm_utils_ip6_address_clear_host_address (&route.network, &network, plen); route.plen = plen; route.gateway = gateway; route.metric = metric; route.mss = mss; + if (!IN6_IS_ADDR_UNSPECIFIED(&gateway)) { + for (i = 0; i < priv->ip6_routes->len; i++) { + NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, + NMPlatformIP6Route, i); + guint8 gate_bits = gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); + guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8); + + if ( ifindex == item->ifindex + && memcmp (&gateway, &item->network, item->plen / 8) == 0 + && gate_bits == host_bits) + break; + } + if (i == priv->ip6_routes->len) { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", + route.ifindex, nm_utils_inet6_ntop (&route.network, NULL), route.plen, route.metric); + return FALSE; + } + } + for (i = 0; i < priv->ip6_routes->len; i++) { NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (item->ifindex != route.ifindex) - continue; if (!IN6_ARE_ADDR_EQUAL (&item->network, &route.network)) continue; if (item->plen != route.plen) @@ -1132,18 +1254,24 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, if (item->metric != metric) continue; + if (item->ifindex != route.ifindex) { + ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); + i--; + continue; + } + memcpy (item, &route, sizeof (route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } g_array_append_val (priv->ip6_routes, route); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } -static NMPlatformIP4Route * +static const NMPlatformIP4Route * ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -1162,7 +1290,7 @@ ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, g return NULL; } -static NMPlatformIP6Route * +static const NMPlatformIP6Route * ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -1183,48 +1311,6 @@ ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int p return NULL; } -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) -{ - NMPlatformIP4Route *route = ip4_route_get (platform, ifindex, network, plen, metric); - NMPlatformIP4Route deleted_route; - - if (route) { - memcpy (&deleted_route, route, sizeof (deleted_route)); - memset (route, 0, sizeof (*route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); - } - - return TRUE; -} - -static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - NMPlatformIP6Route *route = ip6_route_get (platform, ifindex, network, plen, metric); - NMPlatformIP6Route deleted_route; - - if (route) { - memcpy (&deleted_route, route, sizeof (deleted_route)); - memset (route, 0, sizeof (*route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); - } - - return TRUE; -} - -static gboolean -ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) -{ - return !!ip4_route_get (platform, ifindex, network, plen, metric); -} - -static gboolean -ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - return !!ip6_route_get (platform, ifindex, network, plen, metric); -} - /******************************************************************/ static void @@ -1240,21 +1326,25 @@ nm_fake_platform_init (NMFakePlatform *fake_platform) priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); } -static gboolean -setup (NMPlatform *platform) +void +nm_fake_platform_setup (void) { + NMPlatform *platform; + + platform = g_object_new (NM_TYPE_FAKE_PLATFORM, NULL); + + nm_platform_setup (platform); + /* skip zero element */ - link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0); + link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0, NULL); /* add loopback interface */ - link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0); + link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0, NULL); /* add some ethernets */ - link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0); - link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0); - link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0); - - return TRUE; + link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); + link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); + link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); } static void @@ -1267,7 +1357,6 @@ nm_fake_platform_finalize (GObject *object) for (i = 0; i < priv->links->len; i++) { NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - g_bytes_unref (device->address); g_free (device->udi); } g_array_unref (priv->links); @@ -1290,43 +1379,34 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) /* virtual methods */ object_class->finalize = nm_fake_platform_finalize; - platform_class->setup = setup; - platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; platform_class->link_get = _nm_platform_link_get; + platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; + platform_class->link_get_by_address = _nm_platform_link_get_by_address; platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_ifindex = link_get_ifindex; - platform_class->link_get_name = link_get_name; - platform_class->link_get_type = link_get_type; platform_class->link_get_type_name = link_get_type_name; + 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; platform_class->link_set_noarp = link_set_noarp; - platform_class->link_is_up = link_is_up; - platform_class->link_is_connected = link_is_connected; - platform_class->link_uses_arp = link_uses_arp; platform_class->link_set_address = link_set_address; - platform_class->link_get_address = link_get_address; - platform_class->link_get_mtu = link_get_mtu; platform_class->link_set_mtu = link_set_mtu; - platform_class->link_get_physical_port_id = link_get_physical_port_id; - platform_class->link_get_dev_id = link_get_dev_id; - platform_class->link_get_wake_on_lan = link_get_wake_on_lan; + platform_class->link_get_driver_info = link_get_driver_info; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; - platform_class->link_get_master = link_get_master; platform_class->master_set_option = master_set_option; platform_class->master_get_option = master_get_option; platform_class->slave_set_option = slave_set_option; @@ -1338,6 +1418,7 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->vlan_set_egress_map = vlan_set_egress_map; platform_class->infiniband_partition_add = infiniband_partition_add; + platform_class->infiniband_get_info = infiniband_get_info; platform_class->veth_get_properties = veth_get_properties; platform_class->tun_get_properties = tun_get_properties; @@ -1360,23 +1441,21 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; + platform_class->ip4_address_get = ip4_address_get; + platform_class->ip6_address_get = ip6_address_get; platform_class->ip4_address_get_all = ip4_address_get_all; platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_address_exists = ip4_address_exists; - platform_class->ip6_address_exists = ip6_address_exists; - - platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route; + platform_class->ip4_route_get = ip4_route_get; + platform_class->ip6_route_get = ip6_route_get; platform_class->ip4_route_get_all = ip4_route_get_all; platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; platform_class->ip6_route_add = ip6_route_add; platform_class->ip4_route_delete = ip4_route_delete; platform_class->ip6_route_delete = ip6_route_delete; - platform_class->ip4_route_exists = ip4_route_exists; - platform_class->ip6_route_exists = ip6_route_exists; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index c337ee9c..80757f4e 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" @@ -31,10 +31,6 @@ #include <linux/if_link.h> #include <linux/if_tun.h> #include <linux/if_tunnel.h> -#include <sys/ioctl.h> -#include <linux/sockios.h> -#include <linux/ethtool.h> -#include <linux/mii.h> #include <netlink/netlink.h> #include <netlink/object.h> #include <netlink/cache.h> @@ -44,9 +40,9 @@ #include <netlink/route/route.h> #include <gudev/gudev.h> -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN #include <netlink/route/link/inet6.h> -#if HAVE_KERNEL_INET6_ADDR_GEN_MODE +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE #include <linux/if_link.h> #else #define IN6_ADDR_GEN_MODE_EUI64 0 @@ -55,21 +51,101 @@ #endif #include "gsystem-local-alloc.h" +#include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-linux-platform.h" +#include "nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #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" -#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) -#define warning(...) nm_log_warn (LOGD_PLATFORM, __VA_ARGS__) -#define error(...) nm_log_err (LOGD_PLATFORM, __VA_ARGS__) +/*********************************************************************************************/ + +#define _LOG_DOMAIN LOGD_PLATFORM +#define _LOG_PREFIX_NAME "platform-linux" + +#define _LOG(level, domain, self, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + char __prefix[32]; \ + const char *__p_prefix = _LOG_PREFIX_NAME; \ + const void *const __self = (self); \ + \ + if (__self && __self != nm_platform_try_get ()) { \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log (__level, __domain, 0, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __p_prefix _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(...) _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__) +#else +#define _LOGT_ENABLED() FALSE +#define _LOGT(...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__); } } G_STMT_END +#endif + +#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, platform, __VA_ARGS__) +#define _LOGI(...) _LOG (LOGL_INFO , _LOG_DOMAIN, platform, __VA_ARGS__) +#define _LOGW(...) _LOG (LOGL_WARN , _LOG_DOMAIN, platform, __VA_ARGS__) +#define _LOGE(...) _LOG (LOGL_ERR , _LOG_DOMAIN, platform, __VA_ARGS__) + +#define debug(...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, NULL, __VA_ARGS__) +#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 gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); +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 + ******************************************************************/ struct libnl_vtable { @@ -78,41 +154,6 @@ struct libnl_vtable int (*f_nl_has_capability) (int capability); }; - -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; - - int support_kernel_extended_ifa_flags; - int support_user_ipv6ll; -} 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 (NM_TYPE_LINUX_PLATFORM); -} - -/******************************************************************/ - static int _nl_f_nl_has_capability (int capability) { @@ -148,73 +189,10 @@ _nl_has_capability (int capability) return (_nl_get_vtable ()->f_nl_has_capability) (capability); } -/******************************************************************/ - -static guint32 -_get_expiry (guint32 now_s, guint32 lifetime_s) -{ - gint64 t = ((gint64) now_s) + ((gint64) lifetime_s); - - return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1); -} - -/* The rtnl_addr object contains relative lifetimes @valid and @preferred - * that count in seconds, starting from the moment when the kernel constructed - * the netlink message. - * - * There is also a field rtnl_addr_last_update_time(), which is the absolute - * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address - * was modified (wrapping every 497 days). - * Immediately at the time when the address was last modified, #NOW and @last_update_time - * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. - * However, this is not true in general. As time goes by, whenever kernel sends a new address - * via netlink, the lifetimes keep counting down. - * - * As we cache the rtnl_addr object we must know the absolute expiries. - * As a hack, modify the relative timestamps valid and preferred into absolute - * timestamps of scale nm_utils_get_monotonic_timestamp_s(). - **/ -static void -_rtnl_addr_hack_lifetimes_rel_to_abs (struct rtnl_addr *rtnladdr) -{ - guint32 a_valid = rtnl_addr_get_valid_lifetime (rtnladdr); - guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr); - guint32 now; - - if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && - a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) - return; - - now = (guint32) nm_utils_get_monotonic_timestamp_s (); - - if (a_preferred > a_valid) - a_preferred = a_valid; - - if (a_valid != NM_PLATFORM_LIFETIME_PERMANENT) - rtnl_addr_set_valid_lifetime (rtnladdr, _get_expiry (now, a_valid)); - rtnl_addr_set_preferred_lifetime (rtnladdr, _get_expiry (now, a_preferred)); -} - -/******************************************************************/ - -/* libnl library workarounds and additions */ - /* Automatic deallocation of local variables */ -#define auto_nl_cache __attribute__((cleanup(put_nl_cache))) +#define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object))) static void -put_nl_cache (void *ptr) -{ - struct nl_cache **cache = ptr; - - if (cache && *cache) { - nl_cache_free (*cache); - *cache = NULL; - } -} - -#define auto_nl_object __attribute__((cleanup(put_nl_object))) -static void -put_nl_object (void *ptr) +_nl_auto_nl_object (void *ptr) { struct nl_object **object = ptr; @@ -224,9 +202,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; @@ -236,12 +214,10 @@ 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; @@ -253,7 +229,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; @@ -269,7 +245,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; @@ -282,7 +258,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 (); @@ -292,7 +268,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; @@ -302,11 +278,9 @@ _nm_rtnl_route_nh_alloc (void) return nexthop; } -/*******************************************************************/ - /* 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; @@ -316,188 +290,30 @@ 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 - -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, -} ObjectType; - -static ObjectType -object_type_from_nl_object (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 || object_type_from_nl_object (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 const char * +_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) { - struct nl_object *object = NULL; - ObjectType type = object_type_from_nl_object (needle); + const char *str_type = NULL; 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: - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - name = rtnl_link_get_name ((struct rtnl_link *) object); - debug ("get_kernel_object for link: %s (%d, family %d)", - name ? name : "(unknown)", - rtnl_link_get_ifindex ((struct rtnl_link *) object), - rtnl_link_get_family ((struct rtnl_link *) object)); - } - - _nl_link_family_unset (object, &nle); - return object; - case -NLE_NODEV: - debug ("get_kernel_object for link %s (%d) had no result", - name ? name : "(unknown)", ifindex); - return NULL; - default: - error ("get_kernel_object for link %s (%d) failed: %s (%d)", - name ? name : "(unknown)", ifindex, nl_geterror (nle), nle); - return NULL; - } - } - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - /* Fallback to a one-time cache allocation. */ - { - struct nl_cache *cache; - int nle; - - /* FIXME: every time we refresh *one* object, we request an - * entire dump. E.g. check_cache_items() gets O(n2) complexitly. */ - - nle = nl_cache_alloc_and_fill ( - nl_cache_ops_lookup (nl_object_get_type (needle)), - sock, &cache); - if (nle) { - error ("get_kernel_object for type %d failed: %s (%d)", - type, nl_geterror (nle), nle); - return NULL; - } - - object = nl_cache_search (cache, needle); - - nl_cache_free (cache); - - if (object && (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS)) - _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); - - if (object) - debug ("get_kernel_object for type %d returned %p", type, object); - else - debug ("get_kernel_object for type %d had no result", type); - return object; - } - default: - g_return_val_if_reached (NULL); - return NULL; + 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; } -/* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */ -static int -add_kernel_object (struct nl_sock *sock, struct nl_object *object) -{ - switch (object_type_from_nl_object (object)) { - case OBJECT_TYPE_LINK: - return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE); - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE | NLM_F_REPLACE); - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE | NLM_F_REPLACE); - default: - g_return_val_if_reached (-NLE_INVAL); - return -NLE_INVAL; - } -} +/******************************************************************/ -/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel +/* _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(). @@ -519,7 +335,7 @@ static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { }; static int -info_data_parser (struct nl_msg *msg, void *arg) +_nl_link_parse_info_data_cb (struct nl_msg *msg, void *arg) { NMNLInfoDataClosure *closure = arg; struct nlmsghdr *n = nlmsg_hdr (msg); @@ -548,8 +364,8 @@ info_data_parser (struct nl_msg *msg, void *arg) } static int -nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, - NMNLInfoDataParser parser, gpointer parser_data) +_nl_link_parse_info_data (struct nl_sock *sk, int ifindex, + NMNLInfoDataParser parser, gpointer parser_data) { NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; struct nl_msg *msg = NULL; @@ -568,7 +384,7 @@ nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, cb = nl_cb_clone (nl_socket_get_cb (sk)); if (cb == NULL) return -NLE_NOMEM; - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data); + nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, _nl_link_parse_info_data_cb, &data); err = nl_recvmsgs (sk, cb); nl_cb_put (cb); @@ -581,75 +397,171 @@ nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, /******************************************************************/ -static gboolean -ethtool_get (const char *name, gpointer edata) +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; +} + +static void +_nl_msg_set_seq (struct nl_sock *sk, struct nl_msg *msg, guint32 *out_seq) { - struct ifreq ifr; - int fd; + guint32 seq; - memset (&ifr, 0, sizeof (ifr)); - strncpy (ifr.ifr_name, name, IFNAMSIZ); - ifr.ifr_data = edata; + /* 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); - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - error ("ethtool: Could not open socket."); - return FALSE; - } + nlmsg_hdr (msg)->nlmsg_seq = seq; + if (out_seq) + *out_seq = seq; +} - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - debug ("ethtool: Request failed: %s", strerror (errno)); - close (fd); - return FALSE; - } +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; - close (fd); - return TRUE; + if (name && !name[0]) + name = NULL; + + g_return_val_if_fail (ifindex > 0 || name, -NLE_INVAL); + + _LOGT ("sock: request-link %d%s%s%s", ifindex, name ? ", \"" : "", name ? name : "", name ? "\"" : ""); + + if ((err = rtnl_link_build_get_request (ifindex, name, &msg)) < 0) + return err; + + _nl_msg_set_seq (sk, msg, out_seq); + + err = nl_send_auto (sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return 0; } static int -ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) +_nl_sock_request_all (NMPlatform *platform, struct nl_sock *sk, NMPObjectType obj_type, guint32 *out_seq) { - gs_free struct ethtool_sset_info *info = NULL; - gs_free struct ethtool_gstrings *strings = NULL; - guint32 len, i; + const NMPClass *klass; + struct rtgenmsg gmsg = { 0 }; + struct nl_msg *msg; + int err; - info = g_malloc0 (sizeof (*info) + sizeof (guint32)); - info->cmd = ETHTOOL_GSSET_INFO; - info->reserved = 0; - info->sset_mask = 1ULL << stringset_id; + klass = nmp_class_from_type (obj_type); - if (!ethtool_get (ifname, info)) - return -1; - if (!info->sset_mask) - return -1; + _LOGT ("sock: request-all-%s", klass->obj_type_name); - len = info->data[0]; + /* 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; - strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = stringset_id; - strings->len = len; - if (!ethtool_get (ifname, strings)) - return -1; + _nl_msg_set_seq (sk, msg, out_seq); - for (i = 0; i < len; i++) { - if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) - return i; - } + err = nl_send_auto (sk, msg); +errout: + nlmsg_free(msg); + + return err >= 0 ? 0 : err; +} + +/******************************************************************/ - return -1; +#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 (_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 + return _support_user_ipv6ll > 0; +#endif + + return FALSE; +} + +static void +_support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) +{ +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + /* If we ever see a link with valid IPv6 link-local address + * generation modes, the kernel supports it. + */ + if (_support_user_ipv6ll_still_undecided ()) { + uint8_t mode; + + if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) rtnl_link, &mode) == 0) { + _support_user_ipv6ll = 1; + nm_log_dbg (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "detected"); + } else { + _support_user_ipv6ll = -1; + nm_log_dbg (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "not detected"); + } + } +#endif } /******************************************************************/ +static int _support_kernel_extended_ifa_flags = 0; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) + static void -_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg) +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) { - struct nlmsghdr *msg_hdr = nlmsg_hdr (msg); + struct nlmsghdr *msg_hdr; + + if (!_support_kernel_extended_ifa_flags_still_undecided ()) + return; - g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0); - g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR); + msg_hdr = nlmsg_hdr (msg); + if (msg_hdr->nlmsg_type != RTM_NEWADDR) + return; /* the extended address flags are only set for AF_INET6 */ if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) @@ -659,424 +571,507 @@ _check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, str * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR * and IFA_F_NOPREFIXROUTE (they were added together). **/ - priv->support_kernel_extended_ifa_flags = + _support_kernel_extended_ifa_flags = nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */) ? 1 : -1; } static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) +_support_kernel_extended_ifa_flags_get (void) { - NMLinuxPlatformPrivate *priv; + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support."); + _support_kernel_extended_ifa_flags = -1; + } + return _support_kernel_extended_ifa_flags > 0; +} - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); +/****************************************************************** + * Object type specific utilities + ******************************************************************/ - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); +static guint +_nm_ip_config_source_to_rtprot (NMIPConfigSource source) +{ + switch (source) { + case NM_IP_CONFIG_SOURCE_UNKNOWN: + return RTPROT_UNSPEC; + case NM_IP_CONFIG_SOURCE_KERNEL: + case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + return RTPROT_KERNEL; + case NM_IP_CONFIG_SOURCE_DHCP: + return RTPROT_DHCP; + case NM_IP_CONFIG_SOURCE_RDISC: + return RTPROT_RA; - if (priv->support_kernel_extended_ifa_flags == 0) { - nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support."); - priv->support_kernel_extended_ifa_flags = -1; + default: + return RTPROT_STATIC; } - - return priv->support_kernel_extended_ifa_flags > 0; } -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) +static NMIPConfigSource +_nm_ip_config_source_from_rtprot (guint rtprot) { - NMLinuxPlatformPrivate *priv; + 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; - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + default: + return NM_IP_CONFIG_SOURCE_USER; + } +} - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); +/******************************************************************/ + +typedef struct { + const NMLinkType nm_type; + const char *type_string; + + /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type + * should only be specified if the device type can be created without + * additional parameters, and if the device type can be determined from + * the rtnl_type. eg, tun/tap should not be specified since both + * tun and tap devices use "tun", and InfiniBand should not be + * specified because a PKey is required at creation. Drivers set this + * value from their 'struct rtnl_link_ops' structure. + */ + const char *rtnl_type; + + /* uevent DEVTYPE where applicable, from /sys/class/net/<ifname>/uevent; + * drivers set this value from their SET_NETDEV_DEV() call and the + * 'struct device_type' name member. + */ + const char *devtype; +} LinkDesc; + +static const LinkDesc linktypes[] = { + { NM_LINK_TYPE_NONE, "none", NULL, NULL }, + { NM_LINK_TYPE_UNKNOWN, "unknown", NULL, NULL }, + + { NM_LINK_TYPE_ETHERNET, "ethernet", NULL, NULL }, + { NM_LINK_TYPE_INFINIBAND, "infiniband", NULL, NULL }, + { NM_LINK_TYPE_OLPC_MESH, "olpc-mesh", NULL, NULL }, + { NM_LINK_TYPE_WIFI, "wifi", NULL, "wlan" }, + { NM_LINK_TYPE_WWAN_ETHERNET, "wwan", NULL, "wwan" }, + { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, + + { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, + { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, + { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, + { NM_LINK_TYPE_IFB, "ifb", "ifb", NULL }, + { NM_LINK_TYPE_LOOPBACK, "loopback", NULL, NULL }, + { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, + { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, + { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", NULL }, + { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, + { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, + { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, + { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, + { NM_LINK_TYPE_VXLAN, "vxlan", "vxlan", "vxlan" }, + { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, + + { NM_LINK_TYPE_BRIDGE, "bridge", "bridge", "bridge" }, + { NM_LINK_TYPE_BOND, "bond", "bond", "bond" }, + { NM_LINK_TYPE_TEAM, "team", "team", NULL }, +}; + +static const char * +nm_link_type_to_rtnl_type_string (NMLinkType type) +{ + int i; - if (priv->support_user_ipv6ll == 0) { - nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for IFLA_INET6_ADDR_GEN_MODE. Assume no kernel support."); - priv->support_user_ipv6ll = -1; + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (type == linktypes[i].nm_type) + return linktypes[i].rtnl_type; } + g_return_val_if_reached (NULL); +} + +const char * +nm_link_type_to_string (NMLinkType type) +{ + int i; - return priv->support_user_ipv6ll > 0; + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (type == linktypes[i].nm_type) + return linktypes[i].type_string; + } + g_return_val_if_reached (NULL); } +/****************************************************************** + * NMPlatform types and functions + ******************************************************************/ -/* Object type specific utilities */ +typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; -static const char * -type_to_string (NMLinkType type) +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) { - /* Note that this only has to support virtual types */ - switch (type) { - case NM_LINK_TYPE_DUMMY: - return "dummy"; - case NM_LINK_TYPE_GRE: - return "gre"; - case NM_LINK_TYPE_GRETAP: - return "gretap"; - case NM_LINK_TYPE_IFB: - return "ifb"; - case NM_LINK_TYPE_MACVLAN: - return "macvlan"; - case NM_LINK_TYPE_MACVTAP: - return "macvtap"; - case NM_LINK_TYPE_TAP: - return "tap"; - case NM_LINK_TYPE_TUN: - return "tun"; - case NM_LINK_TYPE_VETH: - return "veth"; - case NM_LINK_TYPE_VLAN: - return "vlan"; - case NM_LINK_TYPE_VXLAN: - return "vxlan"; - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bond"; - case NM_LINK_TYPE_TEAM: - return "team"; - default: - g_warning ("Wrong type: %d", type); - return NULL; - } + nm_assert (NM_IS_LINUX_PLATFORM (self)); + + return ((NMLinuxPlatform *) self)->priv; } -#define return_type(t, name) \ - G_STMT_START { \ - if (out_name) \ - *out_name = name; \ - return t; \ - } G_STMT_END +G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) -static NMLinkType -link_type_from_udev (NMPlatform *platform, int ifindex, const char *ifname, int arptype, const char **out_name) +void +nm_linux_platform_setup (void) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GUdevDevice *udev_device; - const char *prop, *sysfs_path; + g_object_new (NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_REGISTER_SINGLETON, TRUE, + NULL); +} - g_assert (ifname); +/******************************************************************/ - udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex)); - if (!udev_device) - return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); +NMPObjectType +_nlo_get_object_type (const struct nl_object *object) +{ + const char *type_str; + + if (!object || !(type_str = nl_object_get_type (object))) + return NMP_OBJECT_TYPE_UNKNOWN; - if ( g_udev_device_get_property (udev_device, "ID_NM_OLPC_MESH") - || g_udev_device_get_sysfs_attr (udev_device, "anycast_mask")) - return_type (NM_LINK_TYPE_OLPC_MESH, "olpc-mesh"); + if (!strcmp (type_str, "route/link")) + return NMP_OBJECT_TYPE_LINK; + else if (!strcmp (type_str, "route/addr")) { + switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) { + case AF_INET: + return NMP_OBJECT_TYPE_IP4_ADDRESS; + case AF_INET6: + return NMP_OBJECT_TYPE_IP6_ADDRESS; + default: + return NMP_OBJECT_TYPE_UNKNOWN; + } + } else if (!strcmp (type_str, "route/route")) { + switch (rtnl_route_get_family ((struct rtnl_route *) object)) { + case AF_INET: + return NMP_OBJECT_TYPE_IP4_ROUTE; + case AF_INET6: + return NMP_OBJECT_TYPE_IP6_ROUTE; + default: + return NMP_OBJECT_TYPE_UNKNOWN; + } + } else + return NMP_OBJECT_TYPE_UNKNOWN; +} - prop = g_udev_device_get_property (udev_device, "DEVTYPE"); - sysfs_path = g_udev_device_get_sysfs_path (udev_device); - if (wifi_utils_is_wifi (ifname, sysfs_path, prop)) - return_type (NM_LINK_TYPE_WIFI, "wifi"); - else if (g_strcmp0 (prop, "wwan") == 0) - return_type (NM_LINK_TYPE_WWAN_ETHERNET, "wwan"); - else if (g_strcmp0 (prop, "wimax") == 0) - return_type (NM_LINK_TYPE_WIMAX, "wimax"); +/******************************************************************/ - if (arptype == ARPHRD_ETHER) - return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); +static gboolean +check_support_kernel_extended_ifa_flags (NMPlatform *platform) +{ + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); - return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); + return _support_kernel_extended_ifa_flags_get (); } static gboolean -link_is_software (struct rtnl_link *rtnllink) +check_support_user_ipv6ll (NMPlatform *platform) { - const char *type; + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); - /* FIXME: replace somehow with NMLinkType or nm_platform_is_software(), but - * solve the infinite callstack problems that getting the type of a TUN/TAP - * device causes. - */ + return _support_user_ipv6ll_get (); +} - if ( rtnl_link_get_arptype (rtnllink) == ARPHRD_INFINIBAND - && strchr (rtnl_link_get_name (rtnllink), '.')) - return TRUE; +static void +process_events (NMPlatform *platform) +{ + delayed_action_handle_all (platform, TRUE); +} - type = rtnl_link_get_type (rtnllink); - if (type == NULL) - return FALSE; +/******************************************************************/ - if (!strcmp (type, "dummy") || - !strcmp (type, "gre") || - !strcmp (type, "gretap") || - !strcmp (type, "macvlan") || - !strcmp (type, "macvtap") || - !strcmp (type, "tun") || - !strcmp (type, "veth") || - !strcmp (type, "vlan") || - !strcmp (type, "vxlan") || - !strcmp (type, "bridge") || - !strcmp (type, "bond") || - !strcmp (type, "team")) - return TRUE; +#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ + ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, (obj_type), (visible_only)), \ + NULL)) - return FALSE; -} -static const char * -ethtool_get_driver (const char *ifname) -{ - struct ethtool_drvinfo drvinfo = { 0 }; +/******************************************************************/ - g_return_val_if_fail (ifname != NULL, NULL); +#define DEVTYPE_PREFIX "DEVTYPE=" - drvinfo.cmd = ETHTOOL_GDRVINFO; - if (!ethtool_get (ifname, &drvinfo)) - return NULL; +static char * +read_devtype (const char *sysfs_path) +{ + gs_free char *uevent = g_strdup_printf ("%s/uevent", sysfs_path); + char *contents = NULL; + char *cont, *end; - if (!*drvinfo.driver) + if (!g_file_get_contents (uevent, &contents, NULL, NULL)) return NULL; - - return g_intern_string (drvinfo.driver); + for (cont = contents; cont; cont = end) { + end = strpbrk (cont, "\r\n"); + if (end) + *end++ = '\0'; + if (strncmp (cont, DEVTYPE_PREFIX, STRLEN (DEVTYPE_PREFIX)) == 0) { + cont += STRLEN (DEVTYPE_PREFIX); + memmove (contents, cont, strlen (cont) + 1); + return contents; + } + } + g_free (contents); + return NULL; } -static gboolean -link_is_announceable (NMPlatform *platform, struct rtnl_link *rtnllink) +static const NMPObject * +_lookup_link_cached (NMPlatform *platform, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj; - /* Software devices are always visible outside the platform */ - if (link_is_software (rtnllink)) - return TRUE; + nm_assert (completed_from_cache && link_cached); - /* Hardware devices must be found by udev so rules get run and tags set */ - if (g_hash_table_lookup (priv->udev_devices, - GINT_TO_POINTER (rtnl_link_get_ifindex (rtnllink)))) - return TRUE; + if (!*completed_from_cache) { + obj = ifindex > 0 ? nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex) : NULL; - return FALSE; + if (obj && !obj->_link.netlink.is_in_netlink) + *link_cached = obj; + else + *link_cached = NULL; + *completed_from_cache = TRUE; + } + return *link_cached; } static NMLinkType -link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, const char **out_name) +link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *completed_from_cache, const NMPObject **link_cached, const char **out_kind) { - const char *type; + const char *rtnl_type, *ifname; + int i, arptype; - if (!rtnllink) - return_type (NM_LINK_TYPE_NONE, NULL); + if (!rtnllink) { + if (out_kind) + *out_kind = NULL; + return NM_LINK_TYPE_NONE; + } + + rtnl_type = rtnl_link_get_type (rtnllink); + if (!rtnl_type && completed_from_cache) { + const NMPObject *obj; + + obj = _lookup_link_cached (platform, rtnl_link_get_ifindex (rtnllink), completed_from_cache, link_cached); + if (obj && obj->link.kind) { + rtnl_type = obj->link.kind; + _LOGT ("link_extract_type(): complete kind from cache: ifindex=%d, kind=%s", rtnl_link_get_ifindex (rtnllink), 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) + return linktypes[i].nm_type; + } - type = rtnl_link_get_type (rtnllink); + if (!strcmp (rtnl_type, "tun")) { + NMPlatformTunProperties props; + guint flags; - if (!type) { - int arptype = rtnl_link_get_arptype (rtnllink); - const char *driver; - const char *ifname; + if (tun_get_properties_ifname (platform, rtnl_link_get_name (rtnllink), &props)) { + if (!g_strcmp0 (props.mode, "tap")) + return NM_LINK_TYPE_TAP; + if (!g_strcmp0 (props.mode, "tun")) + return NM_LINK_TYPE_TUN; + } + flags = rtnl_link_get_flags (rtnllink); + + nm_log_dbg (LOGD_PLATFORM, "Failed to read tun properties for interface %d (link flags: %X)", + rtnl_link_get_ifindex (rtnllink), flags); + + /* try guessing the type using the link flags instead... */ + if (flags & IFF_POINTOPOINT) + return NM_LINK_TYPE_TUN; + return NM_LINK_TYPE_TAP; + } + } - if (arptype == ARPHRD_LOOPBACK) - return_type (NM_LINK_TYPE_LOOPBACK, "loopback"); - else if (arptype == ARPHRD_INFINIBAND) - return_type (NM_LINK_TYPE_INFINIBAND, "infiniband"); + arptype = rtnl_link_get_arptype (rtnllink); + if (arptype == ARPHRD_LOOPBACK) + return NM_LINK_TYPE_LOOPBACK; + else if (arptype == ARPHRD_INFINIBAND) + return NM_LINK_TYPE_INFINIBAND; - ifname = rtnl_link_get_name (rtnllink); - if (!ifname) - return_type (NM_LINK_TYPE_UNKNOWN, type); + ifname = rtnl_link_get_name (rtnllink); + if (ifname) { + gs_free char *driver = NULL; + gs_free char *sysfs_path = NULL; + gs_free char *anycast_mask = NULL; + gs_free char *devtype = NULL; - driver = ethtool_get_driver (ifname); if (arptype == 256) { /* Some s390 CTC-type devices report 256 for the encapsulation type * for some reason, but we need to call them Ethernet. */ if (!g_strcmp0 (driver, "ctcm")) - return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); + return NM_LINK_TYPE_ETHERNET; } - if (!g_strcmp0 (driver, "openvswitch")) - return_type (NM_LINK_TYPE_OPENVSWITCH, "openvswitch"); - - return link_type_from_udev (platform, - rtnl_link_get_ifindex (rtnllink), - ifname, - arptype, - out_name); - } else if (!strcmp (type, "dummy")) - return_type (NM_LINK_TYPE_DUMMY, "dummy"); - else if (!strcmp (type, "gre")) - return_type (NM_LINK_TYPE_GRE, "gre"); - else if (!strcmp (type, "gretap")) - return_type (NM_LINK_TYPE_GRETAP, "gretap"); - else if (!strcmp (type, "ifb")) - return_type (NM_LINK_TYPE_IFB, "ifb"); - else if (!strcmp (type, "macvlan")) - return_type (NM_LINK_TYPE_MACVLAN, "macvlan"); - else if (!strcmp (type, "macvtap")) - return_type (NM_LINK_TYPE_MACVTAP, "macvtap"); - else if (!strcmp (type, "tun")) { - NMPlatformTunProperties props; - guint flags; - - if (nm_platform_tun_get_properties (rtnl_link_get_ifindex (rtnllink), &props)) { - if (!g_strcmp0 (props.mode, "tap")) - return_type (NM_LINK_TYPE_TAP, "tap"); - if (!g_strcmp0 (props.mode, "tun")) - return_type (NM_LINK_TYPE_TUN, "tun"); + /* Fallback OVS detection for kernel <= 3.16 */ + if (nmp_utils_ethtool_get_driver_info (ifname, &driver, NULL, NULL)) { + if (!g_strcmp0 (driver, "openvswitch")) + return NM_LINK_TYPE_OPENVSWITCH; } - flags = rtnl_link_get_flags (rtnllink); - - nm_log_dbg (LOGD_PLATFORM, "Failed to read tun properties for interface %d (link flags: %X)", - rtnl_link_get_ifindex (rtnllink), flags); - - /* try guessing the type using the link flags instead... */ - if (flags & IFF_POINTOPOINT) - return_type (NM_LINK_TYPE_TUN, "tun"); - return_type (NM_LINK_TYPE_TAP, "tap"); - } else if (!strcmp (type, "veth")) - return_type (NM_LINK_TYPE_VETH, "veth"); - else if (!strcmp (type, "vlan")) - return_type (NM_LINK_TYPE_VLAN, "vlan"); - else if (!strcmp (type, "vxlan")) - return_type (NM_LINK_TYPE_VXLAN, "vxlan"); - else if (!strcmp (type, "bridge")) - return_type (NM_LINK_TYPE_BRIDGE, "bridge"); - else if (!strcmp (type, "bond")) - return_type (NM_LINK_TYPE_BOND, "bond"); - else if (!strcmp (type, "team")) - return_type (NM_LINK_TYPE_TEAM, "team"); - - return_type (NM_LINK_TYPE_UNKNOWN, type); -} -static const char * -udev_get_driver (NMPlatform *platform, GUdevDevice *device, int ifindex) -{ - GUdevDevice *parent = NULL, *grandparent = NULL; - const char *driver, *subsys; - - driver = g_udev_device_get_driver (device); - if (driver) - return driver; - - /* Try the parent */ - parent = g_udev_device_get_parent (device); - if (parent) { - driver = g_udev_device_get_driver (parent); - if (!driver) { - /* Try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = g_udev_device_get_subsystem (parent); - if ( (g_strcmp0 (subsys, "ibmebus") == 0) - || (subsys == NULL)) { - grandparent = g_udev_device_get_parent (parent); - if (grandparent) { - driver = g_udev_device_get_driver (grandparent); + sysfs_path = g_strdup_printf ("/sys/class/net/%s", ifname); + anycast_mask = g_strdup_printf ("%s/anycast_mask", sysfs_path); + if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS)) + return NM_LINK_TYPE_OLPC_MESH; + + devtype = read_devtype (sysfs_path); + for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) { + if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) { + if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) { + /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must + * use arptype to distinguish between them. + */ + if (arptype != ARPHRD_ETHER) + continue; } + return linktypes[i].nm_type; } } - } - /* Intern the string so we don't have to worry about memory - * management in NMPlatformLink. - */ - if (driver) - driver = g_intern_string (driver); + /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ + if (wifi_utils_is_wifi (ifname, sysfs_path)) + return NM_LINK_TYPE_WIFI; - g_clear_object (&parent); - g_clear_object (&grandparent); + /* Standard wired ethernet interfaces don't report an rtnl_link_type, so + * only allow fallback to Ethernet if no type is given. This should + * prevent future virtual network drivers from being treated as Ethernet + * when they should be Generic instead. + */ + if (arptype == ARPHRD_ETHER && !rtnl_type && !devtype) + return NM_LINK_TYPE_ETHERNET; + } - return driver; + 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; + struct nl_addr *nladdr; + const char *kind; + gboolean completed_from_cache_val = FALSE; + gboolean *completed_from_cache = complete_from_cache ? &completed_from_cache_val : NULL; + const NMPObject *link_cached = NULL; - g_return_val_if_fail (rtnllink, FALSE); + nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); - name = rtnl_link_get_name (rtnllink); - memset (info, 0, sizeof (*info)); + 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)); - info->ifindex = rtnl_link_get_ifindex (rtnllink); - if (name) - g_strlcpy (info->name, name, sizeof (info->name)); - else - info->name[0] = '\0'; - info->type = link_extract_type (platform, rtnllink, &info->type_name); - 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 = udev_get_driver (platform, udev_device, info->ifindex); - if (!info->driver) - info->driver = g_intern_string (rtnl_link_get_type (rtnllink)); - if (!info->driver) - info->driver = ethtool_get_driver (info->name); - if (!info->driver) - info->driver = "unknown"; - info->udi = g_udev_device_get_sysfs_path (udev_device); - } + obj->ifindex = rtnl_link_get_ifindex (nlo); - return TRUE; -} + if (id_only) + 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; + name = rtnl_link_get_name (nlo); + if (name) + g_strlcpy (obj->name, name, sizeof (obj->name)); + obj->type = link_extract_type (platform, nlo, completed_from_cache, &link_cached, &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) { + if (!g_strcmp0 (rtnl_link_get_type (nlo), "vlan")) + obj->vlan_id = rtnl_link_vlan_get_id (nlo); + else if (completed_from_cache) { + _lookup_link_cached (platform, obj->ifindex, completed_from_cache, &link_cached); + if (link_cached) + obj->vlan_id = link_cached->link.vlan_id; + } + } - if (!object) - return; - if (strcmp (nl_object_get_type (object), "route/link")) - return; + if ((nladdr = rtnl_link_get_addr (nlo))) { + unsigned int l = 0; - rtnllink = (struct rtnl_link *) object; + 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; + } + } - 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 = _nm_platform_uint8_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: @@ -1112,15 +1107,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 */ @@ -1131,6 +1129,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: * @@ -1148,686 +1149,952 @@ _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); - - /* 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. - */ + guint64 v; - 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); + 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); - memset (address, 0, sizeof (*address)); - - address->source = NM_IP_CONFIG_SOURCE_KERNEL; - address->ifindex = rtnl_addr_get_ifindex (rtnladdr); - address->plen = rtnl_addr_get_prefixlen (rtnladdr); - _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); - address->flags = rtnl_addr_get_flags (rtnladdr); - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { + 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; + struct nl_addr *pref_src; - 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)); + } + 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)); + + pref_src = rtnl_route_get_pref_src (nlo); + if (pref_src) { + if (nl_addr_get_len (pref_src) != sizeof (obj->pref_src)) + g_warn_if_reached (); + else + memcpy (&obj->pref_src, nl_addr_get_binary_addr (pref_src), sizeof (obj->pref_src)); } - 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)); 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; - 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 (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); -static const char * -to_string_ip4_address (struct rtnl_addr *obj) -{ - NMPlatformIP4Address pl_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)); - 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); + 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, klass->obj_type, obj_clone.object.ifindex, &obj_clone.object, (NMPlatformSignalChangeType) cache_op, reason); } -static const char * -to_string_ip6_address (struct rtnl_addr *obj) +/******************************************************************/ + +static DelayedActionType +delayed_action_refresh_from_object_type (NMPObjectType obj_type) { - NMPlatformIP6Address pl_obj; + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: return DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS; + case NMP_OBJECT_TYPE_IP4_ADDRESS: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES; + case NMP_OBJECT_TYPE_IP6_ADDRESS: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES; + case NMP_OBJECT_TYPE_IP4_ROUTE: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES; + case NMP_OBJECT_TYPE_IP6_ROUTE: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; + default: g_return_val_if_reached (DELAYED_ACTION_TYPE_NONE); + } +} - if (init_ip6_address (&pl_obj, obj)) - return nm_platform_ip6_address_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 address %p)", obj); +static NMPObjectType +delayed_action_refresh_to_object_type (DelayedActionType action_type) +{ + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS: return NMP_OBJECT_TYPE_LINK; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES: return NMP_OBJECT_TYPE_IP4_ADDRESS; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES: return NMP_OBJECT_TYPE_IP6_ADDRESS; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES: return NMP_OBJECT_TYPE_IP4_ROUTE; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES: return NMP_OBJECT_TYPE_IP6_ROUTE; + default: g_return_val_if_reached (NMP_OBJECT_TYPE_UNKNOWN); + } } static const char * -to_string_ip4_route (struct rtnl_route *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) { - NMPlatformIP4Route 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_ip4_route (&pl_obj, obj)) - return nm_platform_ip4_route_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 route %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_ip6_route (struct rtnl_route *obj) +static void +delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex) { - NMPlatformIP6Route pl_obj; - - if (init_ip6_route (&pl_obj, obj)) - return nm_platform_ip6_route_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 route %p)", obj); + do_request_link (platform, ifindex, NULL, FALSE); } -static const char * -to_string_object_with_type (NMPlatform *platform, struct nl_object *obj, ObjectType type) +static void +delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags) { - 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); - } + do_request_all (platform, flags, FALSE); } -static const char * -to_string_object (NMPlatform *platform, struct nl_object *obj) +static void +delayed_action_handle_READ_NETLINK (NMPlatform *platform) { - return to_string_object_with_type (platform, obj, object_type_from_nl_object (obj)); + event_handler_read_netlink_all (platform, TRUE); } -#undef SET_AND_RETURN_STRING_BUFFER +static gboolean +delayed_action_handle_one (NMPlatform *platform) +{ + 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; + } -/* Object and cache manipulation */ + /* 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); -static const char *signal_by_type_and_status[__OBJECT_TYPE_LAST] = { - [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, -}; + 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); -static struct nl_cache * -choose_cache_by_type (NMPlatform *platform, ObjectType object_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + _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; + } - 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; + 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 struct nl_cache * -choose_cache (NMPlatform *platform, struct nl_object *object) +static gboolean +delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) { - return choose_cache_by_type (platform, object_type_from_nl_object (object)); + 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 -object_has_ifindex (struct nl_object *object, int ifindex) -{ - switch (object_type_from_nl_object (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; +delayed_action_handle_idle (gpointer user_data) +{ + NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0; + delayed_action_handle_all (user_data, FALSE); + return G_SOURCE_REMOVE; +} + +static void +delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + 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); - if (rtnl_route_get_nnexthops (rtnlroute) != 1) - return FALSE; - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); + priv->delayed_action.flags |= action_type; - return ifindex == rtnl_route_nh_get_ifindex (nexthop); + 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"); } - default: - g_assert_not_reached (); } + + 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 gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason); +/******************************************************************/ static void -check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex) +cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type) { - auto_nl_cache struct nl_cache *cloned_cache = nl_cache_clone (cache); - struct nl_object *object; - GPtrArray *objects_to_refresh = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put); - guint i; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - 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); - } - } + priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, obj_type, FALSE), + priv->prune_candidates); + _LOGT ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name, + priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0); +} + +static void +cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj) +{ + NMLinuxPlatformPrivate *priv; + + if (!obj) + return; + + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - for (i = 0; i < objects_to_refresh->len; i++) - refresh_object (platform, objects_to_refresh->pdata[i], TRUE, NM_PLATFORM_REASON_CACHE_CHECK); + if (!priv->prune_candidates) + priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - g_ptr_array_free (objects_to_refresh, TRUE); + if (_LOGT_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj)) + _LOGT ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj)); } static void -announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatformSignalChangeType change_type, NMPlatformReason reason) +cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj) +{ + NMLinuxPlatformPrivate *priv; + + if (!obj) + return; + + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + if (priv->prune_candidates) { + if (_LOGT_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj)) + _LOGT ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_hash_table_remove (priv->prune_candidates, obj); + } +} + +static void +cache_prune_candidates_prune (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - ObjectType object_type = object_type_from_nl_object (object); - const char *sig = signal_by_type_and_status[object_type]; + GHashTable *prune_candidates; + GHashTableIter iter; + const NMPObject *obj; + gboolean was_visible; + NMPCacheOpsType cache_op; - switch (object_type) { - case OBJECT_TYPE_LINK: - { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; - NMPlatformLink device; + if (!priv->prune_candidates) + return; -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - /* If we ever see a link with valid IPv6 link-local address - * generation modes, the kernel supports it. - */ - if (priv->support_user_ipv6ll == 0) { - uint8_t mode; + prune_candidates = priv->prune_candidates; + priv->prune_candidates = NULL; - if (rtnl_link_inet6_get_addr_gen_mode (rtnl_link, &mode) == 0) - priv->support_user_ipv6ll = 1; - } -#endif + 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; - if (!init_link (platform, &device, rtnl_link)) - return; + _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); + } - /* Skip hardware devices not yet discovered by udev. They will be - * announced by udev_device_added(). This doesn't apply to removed - * devices, as those come either from udev_device_removed(), - * event_notification() or link_delete() which block the announcment - * themselves when appropriate. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - case NM_PLATFORM_SIGNAL_CHANGED: - if (!link_is_software (rtnl_link) && !device.driver) - return; - break; - default: - break; - } + g_hash_table_unref (prune_candidates); +} - /* 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; - } +static void +cache_delayed_deletion_prune (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GPtrArray *prune_list = NULL; + GHashTableIter iter; + guint i; + NMPObject *obj; - g_signal_emit_by_name (platform, sig, device.ifindex, &device, change_type, reason); - } + if (g_hash_table_size (priv->delayed_deletion) == 0) 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; - } + 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)); + } + } - 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); + g_hash_table_remove_all (priv->delayed_deletion); + + if (prune_list) { + for (i = 0; i < prune_list->len; i++) { + obj = prune_list->pdata[i]; + _LOGT ("delayed-deletion: delete %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + cache_remove_netlink (platform, obj, NULL, NULL, NM_PLATFORM_REASON_EXTERNAL); } - return; - case OBJECT_TYPE_IP6_ADDRESS: - { - NMPlatformIP6Address address; + g_ptr_array_unref (prune_list); + } +} - 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; +static void +cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +{ + NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPClass *klass; + char str_buf[sizeof (_nm_platform_to_string_buffer)]; + char str_buf2[sizeof (_nm_platform_to_string_buffer)]; + + nm_assert (old || new); + nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); + nm_assert (ops_type != NMP_CACHE_OPS_ADDED || (old == NULL && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); + nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old))); + nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); + nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old)); + + klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new); + + nm_assert (klass == (new ? NMP_OBJECT_GET_CLASS (new) : NMP_OBJECT_GET_CLASS (old))); + + _LOGT ("update-cache-%s: %s: %s%s%s", + klass->obj_type_name, + (ops_type == NMP_CACHE_OPS_UPDATED + ? "UPDATE" + : (ops_type == NMP_CACHE_OPS_REMOVED + ? "REMOVE" + : (ops_type == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), + (ops_type != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string (old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) + : nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), + (ops_type == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (ops_type == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) + : "")); + + switch (klass->obj_type) { + case NMP_OBJECT_TYPE_LINK: + { + /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ + if ( old + && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, old->link.master, new, old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (old->link.master)); + if ( new + && (!old || old->link.master != new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, new->link.master, new, old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.master)); + } + { + /* check whether we are about to change a master link that needs toggling connected state. */ + if ( new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle (cache, new, new, old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.ifindex)); + } + { + int ifindex = 0; + + /* if we remove a link (from netlink), we must refresh the addresses and routes */ + if ( ops_type == NMP_CACHE_OPS_REMOVED + && old /* <-- nonsensical, make coverity happy */) + ifindex = old->link.ifindex; + else if ( ops_type == NMP_CACHE_OPS_UPDATED + && old && new /* <-- nonsensical, make coverity happy */ + && !new->_link.netlink.is_in_netlink + && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) + ifindex = new->link.ifindex; + + if (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 && new /* <-- nonsensical, make coverity happy */ + && 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 NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + { + /* Address deletion is sometimes accompanied by route deletion. We need to + * check all routes belonging to the same interface. */ + if (ops_type == NMP_CACHE_OPS_REMOVED) { + delayed_action_schedule (platform, + (klass->obj_type == NMP_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 = object_type_from_nl_object (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 : ""); - /* Refresh the master device (even on enslave/release) */ - 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; - struct nl_object *master_object; + if (ifindex > 0) { + NMPObject *obj; - 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); + + 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, NMPObjectType 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)) { + NMPObjectType 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 == NMP_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, NMPObjectType 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); - nle = add_kernel_object (priv->nlh, object); + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + nle = rtnl_link_add (priv->nlh, (struct rtnl_link *) nlo, NLM_F_CREATE); + break; + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + nle = rtnl_addr_add (priv->nlh, (struct rtnl_addr *) nlo, NLM_F_CREATE | NLM_F_REPLACE); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + nle = rtnl_route_add (priv->nlh, (struct rtnl_route *) nlo, NLM_F_CREATE | NLM_F_REPLACE); + break; + default: + g_return_val_if_reached (-NLE_INVAL); + } + + _LOGT ("kernel-add-%s: returned %s (%d)", + nmp_class_from_type (obj_type)->obj_type_name, nl_geterror (nle), -nle); - /* 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 != NMP_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, NMPObjectType 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 = object_type_from_nl_object (object); - g_return_val_if_fail (object_type != OBJECT_TYPE_UNKNOWN, FALSE); switch (object_type) { - case OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_LINK: nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); break; - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0); break; - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0); break; default: @@ -1836,40 +2103,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) { + if (object_type == NMP_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; + if (object_type == NMP_OBJECT_TYPE_IP4_ADDRESS || object_type == NMP_OBJECT_TYPE_IP6_ADDRESS) { + _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return -NLE_SUCCESS; } - 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; +} - if (do_refresh_object) - refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL); +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; - result = TRUE; + ifindex = rtnl_link_get_ifindex (nlo); -out: - nl_object_put (object); - return result; + g_return_val_if_fail (ifindex > 0, FALSE); + + /* Previously, we were using rtnl_link_change(), which builds a request based + * on the diff with an original link instance. + * + * The diff only reused ifi_family, ifi_index, ifi_flags, and name from + * the original link (see rtnl_link_build_change_request()). + * + * We don't do that anymore as we don't have an "orig" netlink instance that + * we can use. Instead the caller must ensure to properly initialize @nlo, + * especially it must set family, ifindex (or ifname) and flags. + * ifname should be set *only* if the caller wishes to change the name. + * + * @complete_from_cache is a convenience to copy the link flags over the link inside + * the platform cache. */ + + if (*complete_from_cache) { + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) { + _LOGT ("kernel-change-link: failure changing link %d: cannot complete link", ifindex); + *complete_from_cache = FALSE; + return -NLE_INVAL; + } + + rtnl_link_set_flags (nlo, obj_cache->link.flags); + + /* If the caller wants to rename the link, he should explicitly set + * rtnl_link_set_name(). In all other cases, it should leave the name + * unset. Unfortunately, there is not public API in libnl to modify the + * attribute mask and clear (link->ce_mask = ~LINK_ATTR_IFNAME), so we + * require the caller to do the right thing -- i.e. don't set the name. + */ + } + + /* We don't use rtnl_link_change() because we have no original rtnl_link object + * at hand. We also don't use rtnl_link_add() because that doesn't have the + * hack to retry with RTM_SETLINK. Reimplement a mix of both. */ + + nle = rtnl_link_build_add_request (nlo, nlflags, &msg); + if (nle < 0) { + _LOGT ("kernel-change-link: failure changing link %d: cannot construct message (%s, %d)", + ifindex, nl_geterror (nle), -nle); + return nle; + } + +retry: + nle = nl_send_auto_complete (priv->nlh, msg); + if (nle < 0) + goto errout; + + nle = nl_wait_for_ack(priv->nlh); + if (nle == -NLE_OPNOTSUPP && nlmsg_hdr (msg)->nlmsg_type == RTM_NEWLINK) { + nlmsg_hdr (msg)->nlmsg_type = RTM_SETLINK; + goto retry; + } + +errout: + nlmsg_free(msg); + + /* NLE_EXIST is considered equivalent to success to avoid race conditions. You + * never know when something sends an identical object just before + * NetworkManager. + * + * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find + * firmware for the device, especially on nm_platform_link_set_up (). + * This is basically the same check as in the original code and could + * potentially be improved. + */ + switch (nle) { + case -NLE_SUCCESS: + _LOGT ("kernel-change-link: success changing link %d", ifindex); + break; + case -NLE_EXIST: + _LOGT ("kernel-change-link: success changing link %d: %s (%d)", + ifindex, nl_geterror (nle), -nle); + break; + case -NLE_OBJ_NOTFOUND: + _LOGT ("kernel-change-link: failure changing link %d: firmware not found (%s, %d)", + ifindex, nl_geterror (nle), -nle); + break; + default: + _LOGT ("kernel-change-link: failure changing link %d: netlink error (%s, %d)", + ifindex, nl_geterror (nle), -nle); + break; + } + + return nle; } static void @@ -1881,47 +2242,40 @@ 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 (platform); + 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 (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; + 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); - /* 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; - } + priv->nlh_seq_expect = 0; + } else + _LOGT ("event_seq_check(): seq %u received (wait for %u)", hdr->nlmsg_seq, priv->nlh_seq_last); - return FALSE; + return NL_OK; +} + +static int +event_err (struct sockaddr_nl *nla, struct nlmsgerr *nlerr, gpointer platform) +{ + _LOGT ("event_err(): error from kernel: %s (%d) for request %d", + strerror (nlerr ? -nlerr->error : 0), + nlerr ? -nlerr->error : 0, + NM_LINUX_PLATFORM_GET_PRIVATE (platform)->nlh_seq_last); + return NL_OK; } /* This function does all the magic to avoid race conditions caused @@ -1934,123 +2288,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 (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) { - /* if kernel support for extended ifa flags is still undecided, use the opportunity - * now and use @msg to decide it. This saves a blocking net link request. - **/ - _check_support_kernel_extended_ifa_flags_init (priv, msg); - } + 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); - g_return_val_if_fail (object, NL_OK); - - type = object_type_from_nl_object (object); + nl_msg_parse (msg, ref_object, &nlo); + if (!nlo) + return NL_OK; - 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); - /* Don't announce removed interfaces that are not recognized by - * udev. They were either not yet discovered or they have been - * already removed and announced. - */ - if (event == RTM_DELLINK) { - if (!link_is_announceable (platform, (struct rtnl_link *) cached_object)) - return NL_OK; - } - announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); + case RTM_NEWLINK: + if ( NMP_OBJECT_GET_TYPE (obj) == NMP_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) == NMP_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; } /******************************************************************/ @@ -2219,40 +2521,81 @@ 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)) { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; + return nmp_cache_lookup_multi_to_array (priv->cache, + NMP_OBJECT_TYPE_LINK, + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, NMP_OBJECT_TYPE_LINK, TRUE)); +} - if (link_is_announceable (platform, rtnl_link)) { - if (init_link (platform, &device, rtnl_link)) - g_array_append_val (links, device); - } - } +static const NMPlatformLink * +_nm_platform_link_get (NMPlatform *platform, int ifindex) +{ + const NMPObject *obj; - return links; + obj = cache_lookup_link (platform, ifindex); + return obj ? &obj->link : NULL; } -static gboolean -_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l) +static const NMPlatformLink * +_nm_platform_link_get_by_ifname (NMPlatform *platform, + const char *ifname) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; + const NMPObject *obj = NULL; - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) { - if (link_is_announceable (platform, rtnllink)) { - if (init_link (platform, l, rtnllink)) - return TRUE; - } + if (ifname && *ifname) { + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); } - return FALSE; + return obj ? &obj->link : NULL; +} + +struct _nm_platform_link_get_by_address_data { + gconstpointer address; + guint8 length; +}; + +static gboolean +_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) +{ + return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); +} + +static const NMPlatformLink * +_nm_platform_link_get_by_address (NMPlatform *platform, + gconstpointer address, + size_t length) +{ + const NMPObject *obj; + struct _nm_platform_link_get_by_address_data d = { + .address = address, + .length = length, + }; + + if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) + return NULL; + if (!address) + return NULL; + + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, NULL, TRUE, NM_LINK_TYPE_NONE, + (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); + return obj ? &obj->link : NULL; } static struct nl_object * @@ -2261,331 +2604,392 @@ 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, type_to_string (type)); + nle = rtnl_link_set_type (rtnllink, nm_link_type_to_rtnl_type_string (type)); g_assert (!nle); } 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_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) +do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo) { - int r; - struct nl_object *l; + NMPObject obj_needle; + int nle; - 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)) - /* Ignore return value to shut up the compiler */ - r = system ("modprobe bonding max_bonds=0"); - } + event_handler_read_netlink_all (platform, FALSE); - debug ("link: add link '%s' of type '%s' (%d)", - name, type_to_string (type), (int) type); + nle = kernel_add_object (platform, NMP_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); - l = build_rtnl_link (0, name, type); + nmp_object_stackinit_id_link (&obj_needle, 0); + g_strlcpy (obj_needle.link.name, name, sizeof (obj_needle.link.name)); - 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); + delayed_action_handle_all (platform, TRUE); - rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); + /* 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 add_object (platform, l); + + /* 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 struct rtnl_link * -link_get (NMPlatform *platform, int ifindex) +static gboolean +do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, NMLinkType expected_link_type, NMPlatformLink *out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex); + const NMPObject *obj; - if (!rtnllink) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return NULL; + do_add_link (platform, name, nlo); + + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, name, FALSE, expected_link_type, NULL, NULL); + if (out_link && obj) + *out_link = obj->link; + return !!obj; +} + +static gboolean +do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +{ + int nle; + + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), + NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + + event_handler_read_netlink_all (platform, FALSE); + + nle = kernel_add_object (platform, NMP_OBJECT_GET_CLASS (obj_id)->obj_type, (const struct nl_object *) nlo); + if (nle < 0) { + _LOGW ("do-add-%s: failure adding %s '%s': %s (%d)", + 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)); - /* physical interfaces must be found by udev before they can be used */ - if (!link_is_announceable (platform, rtnllink)) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - rtnl_link_put (rtnllink); - return NULL; + delayed_action_handle_all (platform, TRUE); + + /* FIXME: instead of re-requesting the added object, add it via nlh_event + * so that the events are in sync. */ + if (!nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_id)) { + _LOGT ("do-add-%s: reload: the added object is not yet ready. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); } - 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 TRUE; } + static gboolean -link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change) +do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + auto_nl_object struct nl_object *nlo_free = NULL; int nle; - if (!rtnllink) - return FALSE; - g_return_val_if_fail (rtnl_link_get_ifindex (change) > 0, FALSE); + event_handler_read_netlink_all (platform, FALSE); - nle = rtnl_link_change (priv->nlh, rtnllink, change, 0); + 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) == NMP_OBJECT_TYPE_LINK) { + const NMPObject *obj; + + obj = nmp_cache_lookup_link_full (priv->cache, obj_id->link.ifindex, obj_id->link.ifindex <= 0 && obj_id->link.name[0] ? obj_id->link.name : NULL, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); + if (obj && obj->_link.netlink.is_in_netlink) { + _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_link (platform, obj_id->link.ifindex, obj_id->link.name, TRUE); + } + } else { + if (nmp_cache_lookup_obj (priv->cache, obj_id)) { + _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); + } + } + + /* The return value doesn't say, whether the object is in the platform cache after adding + * it. + * Instead the return value says, whether kernel_add_object() succeeded. */ + return nle >= 0; +} + +static NMPlatformError +do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache) +{ + int nle; + int ifindex; + gboolean complete_from_cache2 = complete_from_cache; + + ifindex = rtnl_link_get_ifindex (nlo); + if (ifindex <= 0) + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + + 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_delete (NMPlatform *platform, int ifindex) +link_add (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { - 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 *l = NULL; - if (!rtnllink) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return FALSE; + 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); } - return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE), TRUE); -} + debug ("link: add link '%s' of type '%s' (%d)", + name, nm_link_type_to_string (type), (int) type); -static int -link_get_ifindex (NMPlatform *platform, const char *ifname) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + l = build_rtnl_link (0, name, type); - return rtnl_link_name2i (priv->link_cache, ifname); -} + g_assert ( (address != NULL) ^ (address_len == 0) ); + if (address) { + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, address_len); -static const char * -link_get_name (NMPlatform *platform, int ifindex) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); + } - return rtnllink ? rtnl_link_get_name (rtnllink) : NULL; + return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link); } -static NMLinkType -link_get_type (NMPlatform *platform, int ifindex) +static gboolean +link_delete (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPObject obj_needle; + const NMPObject *obj; + + obj = nmp_cache_lookup_link (priv->cache, ifindex); + if (!obj || !obj->_link.netlink.is_in_netlink) + return FALSE; - return link_extract_type (platform, rtnllink, NULL); + nmp_object_stackinit_id_link (&obj_needle, ifindex); + return do_delete_object (platform, &obj_needle, NULL); } static const char * link_get_type_name (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - const char *type; - - link_extract_type (platform, rtnllink, &type); - return type; -} + const NMPObject *obj = cache_lookup_link (platform, ifindex); -static guint32 -link_get_flags (NMPlatform *platform, int ifindex) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - - if (!rtnllink) - return IFF_NOARP; + if (!obj) + return NULL; - return rtnl_link_get_flags (rtnllink); + if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { + /* We could detect the @link_type. In this case the function returns + * our internel module names, which differs from rtnl_link_get_type(): + * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") + * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). + * Note that this functions is only used by NMDeviceGeneric to + * set type_description. */ + return nm_link_type_to_string (obj->link.type); + } + /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ + return str_if_set (obj->link.kind, "unknown"); } static gboolean -link_refresh (NMPlatform *platform, int ifindex) +link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) { - auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (ifindex, NULL); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *link; + GUdevDevice *udev_device = NULL; - return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL); -} + link = nmp_cache_lookup_link (priv->cache, ifindex); + if (link) + udev_device = link->_link.udev.device; -static gboolean -link_is_up (NMPlatform *platform, int ifindex) -{ - return !!(link_get_flags (platform, ifindex) & IFF_UP); -} + if (udev_device && g_udev_device_get_property (udev_device, "NM_UNMANAGED")) { + *unmanaged = g_udev_device_get_property_as_boolean (udev_device, "NM_UNMANAGED"); + return TRUE; + } -static gboolean -link_is_connected (NMPlatform *platform, int ifindex) -{ - return !!(link_get_flags (platform, ifindex) & IFF_LOWER_UP); + return FALSE; } static gboolean -link_uses_arp (NMPlatform *platform, int ifindex) +link_refresh (NMPlatform *platform, int ifindex) { - return !(link_get_flags (platform, ifindex) & IFF_NOARP); + do_request_link (platform, ifindex, NULL, TRUE); + return !!cache_lookup_link (platform, ifindex); } -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]; - - rtnl_link_flags2str (flags, buf, sizeof (buf)); - debug ("link: change %d: flags %s '%s' (%d)", ifindex, value ? "set" : "unset", buf, flags); - } + _LOGD ("link: change %d: flags %s '%s' (%d)", ifindex, + value ? "set" : "unset", + rtnl_link_flags2str (flags, buf, sizeof (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_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) +static const char * +link_get_udi (NMPlatform *platform, int ifindex) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj = cache_lookup_link (platform, ifindex); - if (priv->support_user_ipv6ll > 0) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - uint8_t mode = 0; + 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); +} - if (rtnllink) { - if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) != 0) { - /* Default to "disabled" on error */ - return FALSE; - } - return mode == IN6_ADDR_GEN_MODE_NONE; - } - } -#endif - return FALSE; +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_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { #if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (check_support_user_ipv6ll (platform)) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + if (_support_user_ipv6ll_get ()) { + 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; } static gboolean -supports_ethtool_carrier_detect (const char *ifname) -{ - struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; - - /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we - * assume the device supports carrier-detect, otherwise we assume it - * doesn't. - */ - return ethtool_get (ifname, &edata); -} - -static gboolean -supports_mii_carrier_detect (const char *ifname) -{ - int fd; - struct ifreq ifr; - struct mii_ioctl_data *mii; - gboolean supports_mii = FALSE; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_PLATFORM, "couldn't open control socket."); - return FALSE; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - strncpy (ifr.ifr_name, ifname, IFNAMSIZ); - - errno = 0; - if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIPHY failed: %d", errno); - goto out; - } - - /* If we can read the BMSR register, we assume that the card supports MII link detection */ - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - mii->reg_num = MII_BMSR; - - if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG result 0x%X", mii->val_out); - supports_mii = TRUE; - } else { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG failed: %d", errno); - } - - out: - close (fd); - nm_log_dbg (LOGD_PLATFORM, "MII %s supported", supports_mii ? "is" : "not"); - return supports_mii; -} - -static gboolean link_supports_carrier_detect (NMPlatform *platform, int ifindex) { - const char *name = nm_platform_link_get_name (ifindex); + const char *name = nm_platform_link_get_name (platform, ifindex); if (!name) return FALSE; @@ -2594,103 +2998,56 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) * us whether the device actually supports carrier detection in the first * place. We assume any device that does implements one of these two APIs. */ - return supports_ethtool_carrier_detect (name) || supports_mii_carrier_detect (name); + return nmp_utils_ethtool_supports_carrier_detect (name) || nmp_utils_mii_supports_carrier_detect (name); } static gboolean link_supports_vlans (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - const char *name = nm_platform_link_get_name (ifindex); - gs_free struct ethtool_gfeatures *features = NULL; - int idx, block, bit, size; + const NMPObject *obj; - /* Only ARPHRD_ETHER links can possibly support VLANs. */ - if (!rtnllink || rtnl_link_get_arptype (rtnllink) != ARPHRD_ETHER) - return FALSE; + obj = cache_lookup_link (platform, ifindex); - if (!name) - return FALSE; - - idx = ethtool_get_stringset_index (name, ETH_SS_FEATURES, "vlan-challenged"); - if (idx == -1) { - debug ("vlan-challenged ethtool feature does not exist?"); - return FALSE; - } - - block = idx / 32; - bit = idx % 32; - size = block + 1; - - features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); - features->cmd = ETHTOOL_GFEATURES; - features->size = size; - - if (!ethtool_get (name, features)) + /* Only ARPHRD_ETHER links can possibly support VLANs. */ + if (!obj || obj->link.arptype != ARPHRD_ETHER) return FALSE; - return !(features->features[block].active & (1 << bit)); + 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) +static gboolean +link_get_permanent_address (NMPlatform *platform, + int ifindex, + guint8 *buf, + size_t *length) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - struct nl_addr *nladdr; - size_t l = 0; - gconstpointer a = NULL; - - if (rtnllink && - (nladdr = rtnl_link_get_addr (rtnllink))) { - l = nl_addr_get_len (nladdr); - if (l > 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); - } - - if (length) - *length = l; - return a; + return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length); } 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); -} - -static guint32 -link_get_mtu (NMPlatform *platform, int ifindex) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - - return rtnllink ? rtnl_link_get_mtu (rtnllink) : 0; + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static char * @@ -2699,7 +3056,7 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex) const char *ifname; char *path, *id; - ifname = nm_platform_link_get_name (ifindex); + ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return NULL; @@ -2719,7 +3076,7 @@ link_get_dev_id (NMPlatform *platform, int ifindex) gs_free char *path = NULL, *id = NULL; gint64 int_val; - ifname = nm_platform_link_get_name (ifindex); + ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return 0; @@ -2731,16 +3088,20 @@ link_get_dev_id (NMPlatform *platform, int ifindex) return 0; /* Value is reported as hex */ - int_val = nm_utils_ascii_str_to_int64 (id, 16, 0, G_MAXUINT16, 0); + int_val = _nm_utils_ascii_str_to_int64 (id, 16, 0, G_MAXUINT16, 0); return errno ? 0 : (int) int_val; } static int -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags) -{ - struct nl_object *object = build_rtnl_link (0, name, NM_LINK_TYPE_VLAN); - struct rtnl_link *rtnllink = (struct rtnl_link *) object; +vlan_add (NMPlatform *platform, + const char *name, + int parent, + int vlan_id, + guint32 vlan_flags, + NMPlatformLink *out_link) +{ + 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; @@ -2758,63 +3119,61 @@ vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint debug ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)", name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags); - return add_object (platform, object); + 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,19 +3182,11 @@ link_release (NMPlatform *platform, int master, int slave) return link_enslave (platform, 0, slave); } -static int -link_get_master (NMPlatform *platform, int slave) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, slave); - - return rtnllink ? rtnl_link_get_master (rtnllink) : 0; -} - static char * -link_option_path (int master, const char *category, const char *option) +link_option_path (NMPlatform *platform, int master, const char *category, const char *option) { - const char *name = nm_platform_link_get_name (master); - + const char *name = nm_platform_link_get_name (platform, master); + if (!name || !category || !option) return NULL; @@ -2846,31 +3197,30 @@ link_option_path (int master, const char *category, const char *option) } static gboolean -link_set_option (int master, const char *category, const char *option, const char *value) +link_set_option (NMPlatform *platform, int master, const char *category, const char *option, const char *value) { - gs_free char *path = link_option_path (master, category, option); + gs_free char *path = link_option_path (platform, master, category, option); - return path && nm_platform_sysctl_set (path, value); + return path && nm_platform_sysctl_set (platform, path, value); } static char * -link_get_option (int master, const char *category, const char *option) +link_get_option (NMPlatform *platform, int master, const char *category, const char *option) { - gs_free char *path = link_option_path (master, category, option); + gs_free char *path = link_option_path (platform, master, category, option); - return path ? nm_platform_sysctl_get (path) : NULL; + return path ? nm_platform_sysctl_get (platform, path) : NULL; } static const char * master_category (NMPlatform *platform, int master) { - switch (link_get_type (platform, master)) { + switch (nm_platform_link_get_type (platform, master)) { case NM_LINK_TYPE_BRIDGE: return "bridge"; case NM_LINK_TYPE_BOND: return "bonding"; default: - g_return_val_if_reached (NULL); return NULL; } } @@ -2878,18 +3228,15 @@ master_category (NMPlatform *platform, int master) static const char * slave_category (NMPlatform *platform, int slave) { - int master = link_get_master (platform, slave); + int master = nm_platform_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)) { + switch (nm_platform_link_get_type (platform, master)) { case NM_LINK_TYPE_BRIDGE: return "brport"; default: - g_return_val_if_reached (NULL); return NULL; } } @@ -2897,84 +3244,187 @@ slave_category (NMPlatform *platform, int slave) static gboolean master_set_option (NMPlatform *platform, int master, const char *option, const char *value) { - return link_set_option (master, master_category (platform, master), option, value); + return link_set_option (platform, master, master_category (platform, master), option, value); } static char * master_get_option (NMPlatform *platform, int master, const char *option) { - return link_get_option (master, master_category (platform, master), option); + return link_get_option (platform, master, master_category (platform, master), option); } static gboolean slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value) { - return link_set_option (slave, slave_category (platform, slave), option, value); + return link_set_option (platform, slave, slave_category (platform, slave), option, value); } static char * slave_get_option (NMPlatform *platform, int slave, const char *option) { - return link_get_option (slave, slave_category (platform, slave), option); + return link_get_option (platform, slave, slave_category (platform, slave), option); } static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key) +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 (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 (path, id); - g_free (id); - g_free (path); + if (!nm_platform_sysctl_set (platform, path, id)) + return FALSE; - if (success) { - gs_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key); - auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (0, ifname); + do_request_link (platform, 0, ifname, TRUE); - success = refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL); + 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 { + int p_key; + const char *mode; +} IpoibInfo; + +/* IFLA_IPOIB_* were introduced in the 3.7 kernel, but the kernel headers + * we're building against might not have those properties even though the + * running kernel might. + */ +#define IFLA_IPOIB_UNSPEC 0 +#define IFLA_IPOIB_PKEY 1 +#define IFLA_IPOIB_MODE 2 +#define IFLA_IPOIB_UMCAST 3 +#undef IFLA_IPOIB_MAX +#define IFLA_IPOIB_MAX IFLA_IPOIB_UMCAST + +#define IPOIB_MODE_DATAGRAM 0 /* using unreliable datagram QPs */ +#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ + +static const struct nla_policy infiniband_info_policy[IFLA_IPOIB_MAX + 1] = { + [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, + [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, + [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, +}; + +static int +infiniband_info_data_parser (struct nlattr *info_data, gpointer parser_data) +{ + IpoibInfo *info = parser_data; + struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; + int err; + + err = nla_parse_nested (tb, IFLA_IPOIB_MAX, info_data, + (struct nla_policy *) infiniband_info_policy); + if (err < 0) + return err; + if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) + return -EINVAL; + + info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); + + switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) { + case IPOIB_MODE_DATAGRAM: + info->mode = "datagram"; + break; + case IPOIB_MODE_CONNECTED: + info->mode = "connected"; + break; + default: + return -NLE_PARSE_ERR; } - return success; + return 0; +} + +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); + const NMPObject *obj; + IpoibInfo info = { -1, NULL }; + + obj = cache_lookup_link (platform, ifindex); + if (!obj) + return FALSE; + + if (parent) + *parent = obj->link.parent; + + 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 */ + path = g_strdup_printf ("/sys/class/net/%s/mode", ASSERT_VALID_PATH_COMPONENT (iface)); + contents = nm_platform_sysctl_get (platform, path); + g_free (path); + if (!contents) + return FALSE; + + if (strstr (contents, "datagram")) + info.mode = "datagram"; + else if (strstr (contents, "connected")) + info.mode = "connected"; + g_free (contents); + + path = g_strdup_printf ("/sys/class/net/%s/pkey", ASSERT_VALID_PATH_COMPONENT (iface)); + contents = nm_platform_sysctl_get (platform, path); + g_free (path); + if (!contents) + return FALSE; + + info.p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1); + g_free (contents); + + if (info.p_key < 0) + return FALSE; + } + + if (p_key) + *p_key = info.p_key; + if (mode) + *mode = info.mode; + return TRUE; } static gboolean veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) { const char *ifname; - gs_free struct ethtool_stats *stats = NULL; - int peer_ifindex_stat; + int peer_ifindex; - ifname = nm_platform_link_get_name (ifindex); + ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return FALSE; - peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat == -1) { - debug ("%s: peer_ifindex ethtool stat does not exist?", ifname); + peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (ifname); + if (peer_ifindex <= 0) return FALSE; - } - stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = peer_ifindex_stat + 1; - if (!ethtool_get (ifname, stats)) - return FALSE; - - props->peer = stats->data[peer_ifindex_stat]; + props->peer = peer_ifindex; return TRUE; } static gboolean -tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) +tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props) { - const char *ifname; char *path, *val; gboolean success = TRUE; @@ -2984,16 +3434,15 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * props->owner = -1; props->group = -1; - ifname = nm_platform_link_get_name (ifindex); if (!ifname || !nm_utils_iface_valid_name (ifname)) return FALSE; ifname = ASSERT_VALID_PATH_COMPONENT (ifname); path = g_strdup_printf ("/sys/class/net/%s/owner", ifname); - val = nm_platform_sysctl_get (path); + val = nm_platform_sysctl_get (platform, path); g_free (path); if (val) { - props->owner = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); if (errno) success = FALSE; g_free (val); @@ -3001,10 +3450,10 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * success = FALSE; path = g_strdup_printf ("/sys/class/net/%s/group", ifname); - val = nm_platform_sysctl_get (path); + val = nm_platform_sysctl_get (platform, path); g_free (path); if (val) { - props->group = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); if (errno) success = FALSE; g_free (val); @@ -3012,12 +3461,12 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * success = FALSE; path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname); - val = nm_platform_sysctl_get (path); + val = nm_platform_sysctl_get (platform, path); g_free (path); if (val) { gint64 flags; - flags = nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); + flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); if (!errno) { #ifndef IFF_MULTI_QUEUE const int IFF_MULTI_QUEUE = 0x0100; @@ -3035,6 +3484,12 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * return success; } +static gboolean +tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) +{ + return tun_get_properties_ifname (platform, nm_platform_link_get_name (platform, ifindex), props); +} + static const struct nla_policy macvlan_info_policy[IFLA_MACVLAN_MAX + 1] = { [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, #ifdef MACVLAN_FLAG_NOPROMISC @@ -3084,20 +3539,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); } @@ -3224,11 +3679,11 @@ 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)); + warning ("(%s) could not read vxlan properties: %s", + nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); } return (err == 0); } @@ -3278,11 +3733,11 @@ 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)); + warning ("(%s) could not read gre properties: %s", + nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); } return (err == 0); } @@ -3295,26 +3750,25 @@ wifi_get_wifi_data (NMPlatform *platform, int ifindex) wifi_data = g_hash_table_lookup (priv->wifi_data, GINT_TO_POINTER (ifindex)); if (!wifi_data) { - NMLinkType type; - const char *ifname; - - type = link_get_type (platform, ifindex); - ifname = link_get_name (platform, ifindex); - - if (type == NM_LINK_TYPE_WIFI) - wifi_data = wifi_utils_init (ifname, ifindex, TRUE); - else if (type == NM_LINK_TYPE_OLPC_MESH) { - /* The kernel driver now uses nl80211, but we force use of WEXT because - * the cfg80211 interactions are not quite ready to support access to - * mesh control through nl80211 just yet. - */ + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get (platform, ifindex); + if (pllink) { + if (pllink->type == NM_LINK_TYPE_WIFI) + wifi_data = wifi_utils_init (pllink->name, ifindex, TRUE); + else if (pllink->type == NM_LINK_TYPE_OLPC_MESH) { + /* The kernel driver now uses nl80211, but we force use of WEXT because + * the cfg80211 interactions are not quite ready to support access to + * mesh control through nl80211 just yet. + */ #if HAVE_WEXT - wifi_data = wifi_wext_init (ifname, ifindex, FALSE); + wifi_data = wifi_wext_init (pllink->name, ifindex, FALSE); #endif - } + } - if (wifi_data) - g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data); + if (wifi_data) + g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data); + } } return wifi_data; @@ -3460,18 +3914,11 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) static gboolean link_get_wake_on_lan (NMPlatform *platform, int ifindex) { - NMLinkType type = link_get_type (platform, ifindex); - - if (type == NM_LINK_TYPE_ETHERNET) { - struct ethtool_wolinfo wol; + NMLinkType type = nm_platform_link_get_type (platform, ifindex); - memset (&wol, 0, sizeof (wol)); - wol.cmd = ETHTOOL_GWOL; - if (!ethtool_get (link_get_name (platform, ifindex), &wol)) - return FALSE; - - return wol.wolopts != 0; - } else if (type == NM_LINK_TYPE_WIFI) { + if (type == NM_LINK_TYPE_ETHERNET) + return nmp_utils_ethtool_get_wake_on_lan (nm_platform_link_get_name (platform, ifindex)); + else if (type == NM_LINK_TYPE_WIFI) { WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); if (!wifi_data) @@ -3482,55 +3929,45 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex) return FALSE; } -/******************************************************************/ - static gboolean -_address_match (struct rtnl_addr *addr, int family, int ifindex) +link_get_driver_info (NMPlatform *platform, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) { - g_return_val_if_fail (addr, FALSE); - - return rtnl_addr_get_family (addr) == family && - (ifindex == 0 || rtnl_addr_get_ifindex (addr) == ifindex); + return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex), + out_driver_name, + out_driver_version, + out_fw_version); } +/******************************************************************/ + static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) +ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *addresses; - NMPlatformIP4Address address; - struct nl_object *object; - addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address)); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - 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 nmp_cache_lookup_multi_to_array (priv->cache, + obj_type, + nmp_cache_id_init_addrroute_visible_by_ifindex (NMP_CACHE_ID_STATIC, + obj_type, + ifindex)); +} - return addresses; +static GArray * +ip4_address_get_all (NMPlatform *platform, int ifindex) +{ + return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); } static GArray * ip6_address_get_all (NMPlatform *platform, int ifindex) { - 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, NMP_OBJECT_TYPE_IP6_ADDRESS); } #define IPV4LL_NETWORK (htonl (0xA9FE0000L)) @@ -3543,7 +3980,8 @@ ip4_is_link_local (const struct in_addr *src) } static struct nl_object * -build_rtnl_addr (int family, +build_rtnl_addr (NMPlatform *platform, + int family, int ifindex, gconstpointer addr, gconstpointer peer_addr, @@ -3553,10 +3991,10 @@ build_rtnl_addr (int family, 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 */ @@ -3568,7 +4006,7 @@ build_rtnl_addr (int family, /* 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) { @@ -3576,14 +4014,14 @@ build_rtnl_addr (int family, 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) { @@ -3593,19 +4031,15 @@ build_rtnl_addr (int family, } } - 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 - * timestamps (see _rtnl_addr_hack_lifetimes_rel_to_abs()). - * - * This is correct, because we only use build_rtnl_addr() for - * add_object(), delete_object() and cache search (ip_address_exists). */ + _nl_rtnl_addr_set_prefixlen (rtnladdr, plen); + if ( (lifetime != 0 && lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + || (preferred != 0 && preferred != NM_PLATFORM_LIFETIME_PERMANENT)) { + /* note that here we set the relative timestamps (ticking from *now*). */ rtnl_addr_set_valid_lifetime (rtnladdr, lifetime); rtnl_addr_set_preferred_lifetime (rtnladdr, preferred); } if (flags) { - if ((flags & ~0xFF) && !check_support_kernel_extended_ifa_flags (nm_platform_get ())) { + if ((flags & ~0xFF) && !_support_kernel_extended_ifa_flags_get ()) { /* Older kernels don't accept unknown netlink attributes. * * With commit libnl commit 5206c050504f8676a24854519b9c351470fb7cc6, libnl will only set @@ -3625,6 +4059,48 @@ build_rtnl_addr (int family, 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; + guint32 lifetime, preferred; + + nmp_utils_lifetime_get (obj->timestamp, obj->lifetime, obj->preferred, + 0, 0, &lifetime, &preferred); + + return build_rtnl_addr (platform, + AF_INET, + obj->ifindex, + &obj->address, + obj->peer_address ? &obj->peer_address : NULL, + obj->plen, + lifetime, + 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; + guint32 lifetime, preferred; + + nmp_utils_lifetime_get (obj->timestamp, obj->lifetime, obj->preferred, + 0, 0, &lifetime, &preferred); + + return build_rtnl_addr (platform, + AF_INET6, + obj->ifindex, + &obj->address, + !IN6_IS_ADDR_UNSPECIFIED (&obj->peer_address) ? &obj->peer_address : NULL, + obj->plen, + lifetime, + preferred, + 0, + NULL); +} + static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -3635,10 +4111,16 @@ ip4_address_add (NMPlatform *platform, guint32 preferred, const char *label) { - return add_object (platform, build_rtnl_addr (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 @@ -3651,169 +4133,114 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint flags) { - return add_object (platform, build_rtnl_addr (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 (AF_INET, ifindex, &addr, peer_address ? &peer_address : NULL, plen, 0, 0, 0, NULL), TRUE); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + obj_needle.ip4_address.peer_address = peer_address; + return do_delete_object (platform, &obj_needle, NULL); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); + return do_delete_object (platform, &obj_needle, NULL); } -static gboolean -ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen) +static const NMPlatformIP4Address * +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { - auto_nl_object struct nl_object *object = build_rtnl_addr (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; + const NMPObject *obj; - return !!cached_object; + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip4_address; + return NULL; } -static gboolean -ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +static const NMPlatformIP6Address * +ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return ip_address_exists (platform, AF_INET, ifindex, &addr, plen); -} + NMPObject obj_needle; + const NMPObject *obj; -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); + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip6_address; + return NULL; } -static gboolean -ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) +/******************************************************************/ + +static GArray * +ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPlatformIP4Address addr_candidate; - NMPlatformIP4Route route_candidate; - struct nl_object *object; - guint32 device_network; - - 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; - } - } - } + NMPCacheId cache_id; + const NMPlatformIPRoute *const* routes; + GArray *array; + const NMPClass *klass; + gboolean with_rtprot_kernel; + guint i, len; - 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; - } - } - } - } + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - return TRUE; -} + if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) + flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; -/******************************************************************/ + klass = nmp_class_from_type (obj_type); -static gboolean -_route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel) -{ - struct rtnl_nexthop *nexthop; + nmp_cache_id_init_routes_visible (&cache_id, + obj_type, + NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT), + NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT), + ifindex); - g_return_val_if_fail (rtnlroute, FALSE); + routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - 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; + array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - if (ifindex == 0) - return TRUE; + with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); + for (i = 0; i < len; i++) { + nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass); - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - return rtnl_route_nh_get_ifindex (nexthop) == ifindex; + if ( with_rtprot_kernel + || routes[i]->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + g_array_append_vals (array, routes[i], 1); + } + return array; } static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { - 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, NMP_OBJECT_TYPE_IP4_ROUTE, flags); } static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { - 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, NMP_OBJECT_TYPE_IP6_ROUTE, flags); } static void @@ -3846,26 +4273,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); @@ -3879,111 +4306,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, + obj->pref_src ? &obj->pref_src : 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); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; - for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { - struct nl_addr *dst; - struct rtnl_route *rtnlroute = (struct rtnl_route *) object; - - if (!_route_match (rtnlroute, family, ifindex, FALSE)) - continue; - - if (metric != rtnl_route_get_priority (rtnlroute)) - continue; - - dst = rtnl_route_get_dst (rtnlroute); - if ( !dst - || nl_addr_get_family (dst) != family - || nl_addr_get_prefixlen (dst) != plen) - continue; - - /* plen = 0 means all host bits, so all bits should be cleared. - * Likewise if the binary address is not present or all zeros. - */ - if (plen == 0 || nl_addr_iszero (dst)) - memset (dst_clean, 0, sizeof (dst_clean)); - else - clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean); - - if (memcmp (dst_clean, network_clean, - family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0) - continue; - - rtnl_route_get (rtnlroute); - return rtnlroute; - } - return NULL; + 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, NMP_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 @@ -4000,8 +4429,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. @@ -4010,7 +4439,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. */ @@ -4022,179 +4451,51 @@ 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); -} - -static gboolean -ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, guint32 metric) -{ - auto_nl_object struct nl_object *object = build_rtnl_route (family, ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - network, plen, NULL, NULL, metric, 0); - struct nl_cache *cache = choose_cache (platform, object); - auto_nl_object struct nl_object *cached_object = nl_cache_search (cache, object); - - if (!cached_object) - cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric); - return !!cached_object; -} - -static gboolean -ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) -{ - return ip_route_exists (platform, AF_INET, ifindex, &network, plen, metric); -} - -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); -} - -/******************************************************************/ + nlo = build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0); -/* 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; + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); - 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); + return do_delete_object (platform, &obj_needle, nlo); } -/* 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) +static const NMPlatformIP4Route * +ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { - struct nl_object *object; - - if (!old) - return; + NMPObject obj_needle; + const NMPObject *obj; - 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 = object_type_from_nl_object (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); + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip4_route; + return NULL; } -/* 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) +static const NMPlatformIP6Route * +ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { - GPtrArray *objects_to_remove = NULL; - struct nl_object *object; - - for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { - if (object_type_from_nl_object (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)); + NMPObject obj_needle; + const NMPObject *obj; - g_ptr_array_free (objects_to_remove, TRUE); - } -} - -/* Creates and populates the netlink object caches. Called upon platform init and - * when we run out of sync (out of buffer space, netlink congestion control). In case - * the caches already exist, it finds changed, added and removed objects, announces - * them and destroys the old caches. */ -static void -cache_repopulate_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_cache *old_link_cache = priv->link_cache; - struct nl_cache *old_address_cache = priv->address_cache; - struct nl_cache *old_route_cache = priv->route_cache; - struct nl_object *object; - - debug ("platform: %spopulate platform cache", old_link_cache ? "re" : ""); - - /* Allocate new netlink caches */ - init_link_cache (platform); - rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache); - rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache); - g_assert (priv->link_cache && priv->address_cache && priv->route_cache); - - /* Remove all unknown objects from the caches */ - cache_remove_unknown (priv->link_cache); - cache_remove_unknown (priv->address_cache); - cache_remove_unknown (priv->route_cache); - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); - } + 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); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip6_route; + return NULL; } /******************************************************************/ @@ -4208,10 +4509,9 @@ verify_source (struct nl_msg *msg, gpointer user_data) { struct ucred *creds = nlmsg_get_creds (msg); - if (!creds || creds->pid || creds->uid || creds->gid) { + if (!creds || creds->pid) { if (creds) - warning ("netlink: received non-kernel message (pid %d uid %d gid %d)", - creds->pid, creds->uid, creds->gid); + warning ("netlink: received non-kernel message (pid %d)", creds->pid); else warning ("netlink: received message without credentials"); return NL_STOP; @@ -4225,35 +4525,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); @@ -4262,6 +4569,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) { @@ -4278,7 +4650,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); @@ -4298,14 +4671,23 @@ 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; - gboolean was_announceable = FALSE; ifname = g_udev_device_get_name (udev_device); if (!ifname) { @@ -4329,59 +4711,37 @@ udev_device_added (NMPlatform *platform, return; } - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) - was_announceable = link_is_announceable (platform, rtnllink); - - g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex), - g_object_ref (udev_device)); + cache_update_link_udev (platform, ifindex, udev_device); +} - /* Announce devices only if they also have been discovered via Netlink. */ - if (rtnllink && link_is_announceable (platform, rtnllink)) - announce_object (platform, (struct nl_object *) rtnllink, was_announceable ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, 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); - auto_nl_object struct rtnl_link *rtnllink = NULL; int ifindex = 0; - gboolean was_announceable = FALSE; 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; - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) - was_announceable = link_is_announceable (platform, rtnllink); - - g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); - - /* Announce device removal if it is no longer announceable. */ - if (was_announceable && !link_is_announceable (platform, rtnllink)) - announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); + cache_update_link_udev (platform, ifindex, NULL); } static void @@ -4416,28 +4776,40 @@ 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 gboolean -setup (NMPlatform *platform) +static void +constructed (GObject *_object) { + NMPlatform *platform = NM_PLATFORM (_object); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const char *udev_subsys[] = { "net", NULL }; - GUdevEnumerator *enumerator; - GList *devices, *iter; int channel_flags; gboolean status; int nle; -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - struct nl_object *object; -#endif + 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); @@ -4454,7 +4826,7 @@ setup (NMPlatform *platform) 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); @@ -4465,37 +4837,31 @@ setup (NMPlatform *platform) 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 - /* 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) { - uint8_t mode; - - if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) object, &mode) == 0) - priv->support_user_ipv6ll = 1; - else - priv->support_user_ipv6ll = -1; - } -#else - priv->support_user_ipv6ll = -1; -#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); + + /* complete construction of the GObject instance before populating the cache. */ + G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); + + _LOGD ("populate platform cache"); + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); + + delayed_action_handle_all (platform, FALSE); /* And read initial device list */ enumerator = g_udev_enumerator_new (priv->udev_client); g_udev_enumerator_add_match_subsystem (enumerator, "net"); + g_udev_enumerator_add_match_is_initialized (enumerator); devices = g_udev_enumerator_execute (enumerator); @@ -4505,16 +4871,26 @@ setup (NMPlatform *platform) } g_list_free (devices); g_object_unref (enumerator); +} - /* 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)); +static void +dispose (GObject *object) +{ + NMPlatform *platform = NM_PLATFORM (object); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); + _LOGD ("dispose"); - return TRUE; + 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 @@ -4522,17 +4898,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); @@ -4549,21 +4926,21 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) g_type_class_add_private (klass, sizeof (NMLinuxPlatformPrivate)); /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; object_class->finalize = nm_linux_platform_finalize; - platform_class->setup = setup; - platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; platform_class->link_get = _nm_platform_link_get; + platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; + platform_class->link_get_by_address = _nm_platform_link_get_by_address; platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_ifindex = link_get_ifindex; - platform_class->link_get_name = link_get_name; - platform_class->link_get_type = link_get_type; platform_class->link_get_type_name = link_get_type_name; + platform_class->link_get_unmanaged = link_get_unmanaged; platform_class->link_refresh = link_refresh; @@ -4571,28 +4948,26 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_down = link_set_down; platform_class->link_set_arp = link_set_arp; platform_class->link_set_noarp = link_set_noarp; - platform_class->link_is_up = link_is_up; - platform_class->link_is_connected = link_is_connected; - platform_class->link_uses_arp = link_uses_arp; - platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled; + platform_class->link_get_udi = link_get_udi; + platform_class->link_get_udev_device = link_get_udev_device; + platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; - platform_class->link_get_address = link_get_address; platform_class->link_set_address = link_set_address; - platform_class->link_get_mtu = link_get_mtu; + platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; platform_class->link_get_wake_on_lan = link_get_wake_on_lan; + platform_class->link_get_driver_info = link_get_driver_info; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; - platform_class->link_get_master = link_get_master; platform_class->master_set_option = master_set_option; platform_class->master_get_option = master_get_option; platform_class->slave_set_option = slave_set_option; @@ -4604,6 +4979,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->vlan_set_egress_map = vlan_set_egress_map; platform_class->infiniband_partition_add = infiniband_partition_add; + platform_class->infiniband_get_info = infiniband_get_info; platform_class->veth_get_properties = veth_get_properties; platform_class->tun_get_properties = tun_get_properties; @@ -4626,26 +5002,27 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; + platform_class->ip4_address_get = ip4_address_get; + platform_class->ip6_address_get = ip6_address_get; platform_class->ip4_address_get_all = ip4_address_get_all; platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_address_exists = ip4_address_exists; - platform_class->ip6_address_exists = ip6_address_exists; - - platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route; + platform_class->ip4_route_get = ip4_route_get; + platform_class->ip6_route_get = ip6_route_get; platform_class->ip4_route_get_all = ip4_route_get_all; platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; platform_class->ip6_route_add = ip6_route_add; platform_class->ip4_route_delete = ip4_route_delete; platform_class->ip6_route_delete = ip6_route_delete; - platform_class->ip4_route_exists = ip4_route_exists; - platform_class->ip6_route_exists = ip6_route_exists; platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; + + platform_class->process_events = process_events; } + diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index 6f2c1993..a9e2cd82 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-utils.c b/src/platform/nm-platform-utils.c new file mode 100644 index 00000000..d3c62bdd --- /dev/null +++ b/src/platform/nm-platform-utils.c @@ -0,0 +1,436 @@ +/* -*- 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 "nm-platform-utils.h" + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> +#include <linux/mii.h> +#include <linux/version.h> + +#include "gsystem-local-alloc.h" +#include "nm-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + + +/****************************************************************** + * ethtool + ******************************************************************/ + +static gboolean +ethtool_get (const char *name, gpointer edata) +{ + struct ifreq ifr; + int fd; + + if (!name || !*name) + return FALSE; + + memset (&ifr, 0, sizeof (ifr)); + strncpy (ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_data = edata; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_PLATFORM, "ethtool: Could not open socket."); + return FALSE; + } + + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: Request failed: %s", strerror (errno)); + close (fd); + return FALSE; + } + + close (fd); + return TRUE; +} + +static int +ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) +{ + gs_free struct ethtool_sset_info *info = NULL; + gs_free struct ethtool_gstrings *strings = NULL; + guint32 len, i; + + info = g_malloc0 (sizeof (*info) + sizeof (guint32)); + info->cmd = ETHTOOL_GSSET_INFO; + info->reserved = 0; + info->sset_mask = 1ULL << stringset_id; + + if (!ethtool_get (ifname, info)) + return -1; + if (!info->sset_mask) + return -1; + + len = info->data[0]; + + strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = stringset_id; + strings->len = len; + if (!ethtool_get (ifname, strings)) + return -1; + + for (i = 0; i < len; i++) { + if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) + return i; + } + + return -1; +} + +gboolean +nmp_utils_ethtool_get_driver_info (const char *ifname, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) +{ + struct ethtool_drvinfo drvinfo = { 0 }; + + if (!ifname) + return FALSE; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + if (!ethtool_get (ifname, &drvinfo)) + return FALSE; + + if (out_driver_name) + *out_driver_name = g_strdup (drvinfo.driver); + if (out_driver_version) + *out_driver_version = g_strdup (drvinfo.version); + if (out_fw_version) + *out_fw_version = g_strdup (drvinfo.fw_version); + + return TRUE; +} + +gboolean +nmp_utils_ethtool_get_permanent_address (const char *ifname, + guint8 *buf, + size_t *length) +{ + gs_free struct ethtool_perm_addr *epaddr = NULL; + + if (!ifname) + return FALSE; + + epaddr = g_malloc0 (sizeof (*epaddr) + NM_UTILS_HWADDR_LEN_MAX); + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = NM_UTILS_HWADDR_LEN_MAX; + + if (!ethtool_get (ifname, epaddr)) + return FALSE; + + g_assert (epaddr->size <= NM_UTILS_HWADDR_LEN_MAX); + memcpy (buf, epaddr->data, epaddr->size); + *length = epaddr->size; + return TRUE; +} + +gboolean +nmp_utils_ethtool_supports_carrier_detect (const char *ifname) +{ + struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; + + /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we + * assume the device supports carrier-detect, otherwise we assume it + * doesn't. + */ + return ethtool_get (ifname, &edata); +} + +gboolean +nmp_utils_ethtool_supports_vlans (const char *ifname) +{ + gs_free struct ethtool_gfeatures *features = NULL; + int idx, block, bit, size; + + if (!ifname) + return FALSE; + + idx = ethtool_get_stringset_index (ifname, ETH_SS_FEATURES, "vlan-challenged"); + if (idx == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %s?", ifname); + return FALSE; + } + + block = idx / 32; + bit = idx % 32; + size = block + 1; + + features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); + features->cmd = ETHTOOL_GFEATURES; + features->size = size; + + if (!ethtool_get (ifname, features)) + return FALSE; + + return !(features->features[block].active & (1 << bit)); +} + +int +nmp_utils_ethtool_get_peer_ifindex (const char *ifname) +{ + gs_free struct ethtool_stats *stats = NULL; + int peer_ifindex_stat; + + if (!ifname) + return 0; + + peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); + if (peer_ifindex_stat == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: peer_ifindex stat for %s does not exist?", ifname); + return FALSE; + } + + stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = peer_ifindex_stat + 1; + if (!ethtool_get (ifname, stats)) + return 0; + + return stats->data[peer_ifindex_stat]; +} + +gboolean +nmp_utils_ethtool_get_wake_on_lan (const char *ifname) +{ + struct ethtool_wolinfo wol; + + if (!ifname) + return FALSE; + + memset (&wol, 0, sizeof (wol)); + wol.cmd = ETHTOOL_GWOL; + if (!ethtool_get (ifname, &wol)) + return FALSE; + + return wol.wolopts != 0; +} + +gboolean +nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed) +{ + struct ethtool_cmd edata = { + .cmd = ETHTOOL_GSET, + }; + guint32 speed; + + if (!ethtool_get (ifname, &edata)) + return FALSE; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + speed = edata.speed; +#else + speed = ethtool_cmd_speed (&edata); +#endif + if (speed == G_MAXUINT16 || speed == G_MAXUINT32) + speed = 0; + + if (out_speed) + *out_speed = speed; + return TRUE; +} + +/****************************************************************** + * mii + ******************************************************************/ + +gboolean +nmp_utils_mii_supports_carrier_detect (const char *ifname) +{ + int fd, errsv; + struct ifreq ifr; + struct mii_ioctl_data *mii; + gboolean supports_mii = FALSE; + + if (!ifname) + return FALSE; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname); + return FALSE; + } + + memset (&ifr, 0, sizeof (struct ifreq)); + strncpy (ifr.ifr_name, ifname, IFNAMSIZ); + + errno = 0; + if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { + errsv = errno; + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIPHY failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); + goto out; + } + + /* If we can read the BMSR register, we assume that the card supports MII link detection */ + mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; + mii->reg_num = MII_BMSR; + + if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) { + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG result 0x%X (%s)", mii->val_out, ifname); + supports_mii = TRUE; + } else { + errsv = errno; + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); + } + +out: + close (fd); + nm_log_dbg (LOGD_PLATFORM, "mii: MII %s supported (%s)", supports_mii ? "is" : "not", ifname); + return supports_mii; +} + +/****************************************************************** + * udev + ******************************************************************/ + +const char * +nmp_utils_udev_get_driver (GUdevDevice *device) +{ + GUdevDevice *parent = NULL, *grandparent = NULL; + const char *driver, *subsys; + + driver = g_udev_device_get_driver (device); + if (driver) + goto out; + + /* Try the parent */ + parent = g_udev_device_get_parent (device); + if (parent) { + driver = g_udev_device_get_driver (parent); + if (!driver) { + /* Try the grandparent if it's an ibmebus device or if the + * subsys is NULL which usually indicates some sort of + * platform device like a 'gadget' net interface. + */ + subsys = g_udev_device_get_subsystem (parent); + if ( (g_strcmp0 (subsys, "ibmebus") == 0) + || (subsys == NULL)) { + grandparent = g_udev_device_get_parent (parent); + if (grandparent) + driver = g_udev_device_get_driver (grandparent); + } + } + } + g_clear_object (&parent); + g_clear_object (&grandparent); + +out: + /* Intern the string so we don't have to worry about memory + * management in NMPlatformLink. */ + return g_intern_string (driver); +} + +/****************************************************************** + * utils + ******************************************************************/ + +/** + * Takes a pair @timestamp and @duration, and returns the remaining duration based + * on the new timestamp @now. + */ +guint32 +nmp_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, + guint32 duration, + guint32 now, + guint32 padding) +{ + gint64 t; + + if (duration == NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT; + + if (timestamp == 0) { + /* if the @timestamp is zero, assume it was just left unset and that the relative + * @duration starts counting from @now. This is convenient to construct an address + * and print it in nm_platform_ip4_address_to_string(). + * + * In general it does not make sense to set the @duration without anchoring at + * @timestamp because you don't know the absolute expiration time when looking + * at the address at a later moment. */ + timestamp = now; + } + + /* For timestamp > now, just accept it and calculate the expected(?) result. */ + t = (gint64) timestamp + (gint64) duration - (gint64) now; + + /* Optional padding to avoid potential races. */ + t += (gint64) padding; + + if (t <= 0) + return 0; + if (t >= NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT - 1; + return t; +} + +gboolean +nmp_utils_lifetime_get (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 now, + guint32 padding, + guint32 *out_lifetime, + guint32 *out_preferred) +{ + guint32 t_lifetime, t_preferred; + + if (lifetime == 0) { + *out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + *out_preferred = NM_PLATFORM_LIFETIME_PERMANENT; + + /* We treat lifetime==0 as permanent addresses to allow easy creation of such addresses + * (without requiring to set the lifetime fields to NM_PLATFORM_LIFETIME_PERMANENT). + * In that case we also expect that the other fields (timestamp and preferred) are left unset. */ + g_return_val_if_fail (timestamp == 0 && preferred == 0, TRUE); + } else { + if (!now) + now = nm_utils_get_monotonic_timestamp_s (); + t_lifetime = nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now, padding); + if (!t_lifetime) { + *out_lifetime = 0; + *out_preferred = 0; + return FALSE; + } + t_preferred = nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now, padding); + + *out_lifetime = t_lifetime; + *out_preferred = MIN (t_preferred, t_lifetime); + + /* Assert that non-permanent addresses have a (positive) @timestamp. nmp_utils_lifetime_rebase_relative_time_on_now() + * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always + * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. + */ + g_return_val_if_fail ( timestamp != 0 + || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); + g_return_val_if_fail (t_preferred <= t_lifetime, TRUE); + } + return TRUE; +} + + diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h new file mode 100644 index 00000000..557de844 --- /dev/null +++ b/src/platform/nm-platform-utils.h @@ -0,0 +1,66 @@ +/* -*- 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 __NM_PLATFORM_UTILS_H__ +#define __NM_PLATFORM_UTILS_H__ + +#include "config.h" + +#include <gudev/gudev.h> + +#include "nm-platform.h" + + +const char *nmp_utils_ethtool_get_driver (const char *ifname); +gboolean nmp_utils_ethtool_supports_carrier_detect (const char *ifname); +gboolean nmp_utils_ethtool_supports_vlans (const char *ifname); +int nmp_utils_ethtool_get_peer_ifindex (const char *ifname); +gboolean nmp_utils_ethtool_get_wake_on_lan (const char *ifname); +gboolean nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed); + +gboolean nmp_utils_ethtool_get_driver_info (const char *ifname, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version); + +gboolean nmp_utils_ethtool_get_permanent_address (const char *ifname, + guint8 *buf, + size_t *length); + + +gboolean nmp_utils_mii_supports_carrier_detect (const char *ifname); + + +const char *nmp_utils_udev_get_driver (GUdevDevice *device); + +guint32 nmp_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, + guint32 duration, + guint32 now, + guint32 padding); + +gboolean nmp_utils_lifetime_get (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 now, + guint32 padding, + guint32 *out_lifetime, + guint32 *out_preferred); + +#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index ba7708d6..b6d87ff9 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -27,16 +27,59 @@ #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" #include "nm-utils.h" #include "nm-platform.h" +#include "nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" #include "nm-enum-types.h" +#include "nm-core-internal.h" + +#define ADDRESS_LIFETIME_PADDING 5 + +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX); + +#define _LOG_DOMAIN LOGD_PLATFORM +#define _LOG_PREFIX_NAME "platform" + +#define _LOG(level, domain, self, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + char __prefix[32]; \ + const char *__p_prefix = _LOG_PREFIX_NAME; \ + const void *const __self = (self); \ + \ + if (__self && __self != nm_platform_try_get ()) { \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log (__level, __domain, 0, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __p_prefix _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(...) _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__) +#else +#define _LOGT_ENABLED() FALSE +#define _LOGT(...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__); } } G_STMT_END +#endif -#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) +#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, self, __VA_ARGS__) +#define _LOGI(...) _LOG (LOGL_INFO , _LOG_DOMAIN, self, __VA_ARGS__) +#define _LOGW(...) _LOG (LOGL_WARN , _LOG_DOMAIN, self, __VA_ARGS__) +#define _LOGE(...) _LOG (LOGL_ERR , _LOG_DOMAIN, self, __VA_ARGS__) #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate)) @@ -54,19 +97,46 @@ 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 */ -static NMPlatform *platform = NULL; -static NMPlatformClass *klass = NULL; +NM_DEFINE_SINGLETON_INSTANCE (NMPlatform); + +NM_DEFINE_SINGLETON_WEAK_REF (NMPlatform); + +/* Just always initialize a @klass instance. NM_PLATFORM_GET_CLASS() + * is only a plain read on the self instance, which the compiler + * like can optimize out. + */ +#define _CHECK_SELF_VOID(self, klass) \ + NMPlatformClass *klass; \ + do { \ + g_return_if_fail (NM_IS_PLATFORM (self)); \ + klass = NM_PLATFORM_GET_CLASS (self); \ + (void) klass; \ + } while (0) + +#define _CHECK_SELF(self, klass, err_val) \ + NMPlatformClass *klass; \ + do { \ + g_return_val_if_fail (NM_IS_PLATFORM (self), err_val); \ + klass = NM_PLATFORM_GET_CLASS (self); \ + (void) klass; \ + } while (0) /** * nm_platform_setup: - * @type: The #GType for a subclass of #NMPlatform - * - * Do not use this function directly, it is intended to be called by - * NMPlatform subclasses. For the linux platform initialization use - * nm_linux_platform_setup() instead. + * @instance: the #NMPlatform instance * * Failing to set up #NMPlatform singleton results in a fatal error, * as well as trying to initialize it multiple times without freeing @@ -74,122 +144,82 @@ static NMPlatformClass *klass = NULL; * * NetworkManager will typically use only one platform object during * its run. Test programs might want to switch platform implementations, - * though. This is done with a combination of nm_platform_free() and - * nm_*_platform_setup(). + * though. */ void -nm_platform_setup (GType type) +nm_platform_setup (NMPlatform *instance) { - gboolean status; + g_return_if_fail (NM_IS_PLATFORM (instance)); + g_return_if_fail (!singleton_instance); - g_assert (platform == NULL); + singleton_instance = instance; - platform = g_object_new (type, NULL); - g_assert (NM_IS_PLATFORM (platform)); + nm_singleton_instance_weak_ref_register (); - klass = NM_PLATFORM_GET_CLASS (platform); - g_assert (klass->setup); - - status = klass->setup (platform); - g_assert (status); -} - -/** - * nm_platform_free: - * - * Free #NMPlatform singleton created by nm_*_platform_setup(). - */ -void -nm_platform_free (void) -{ - g_assert (platform); - - g_object_unref (platform); - platform = NULL; + nm_log_dbg (LOGD_CORE, "setup NMPlatform singleton (%p, %s)", instance, G_OBJECT_TYPE_NAME (instance)); } /** * nm_platform_get: + * @self: platform instance * * Retrieve #NMPlatform singleton. Use this whenever you want to connect to - * #NMPlatform signals. It is an error to call it before nm_*_platform_setup() - * or after nm_platform_free(). + * #NMPlatform signals. It is an error to call it before nm_platform_setup(). * * Returns: (transfer none): The #NMPlatform singleton reference. */ NMPlatform * -nm_platform_get (void) +nm_platform_get () { - g_assert (platform); + g_assert (singleton_instance); - return platform; + return singleton_instance; } -/******************************************************************/ - -/** - * nm_platform_set_error: - * @error: The error code - * - * Convenience function to falsify platform->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 (NMPlatformError error) +NMPlatform * +nm_platform_try_get (void) { - platform->error = error; + return singleton_instance; } -/** - * nm_platform_get_error: - * - * Convenience function to quickly retrieve the error code of the last - * operation. - * - * Returns: Integer error code. - */ -NMPlatformError -nm_platform_get_error (void) -{ - g_assert (platform); - - return platform->error; -} +/******************************************************************/ /** - * nm_platform_get_error_message: + * 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 (void) -{ - g_assert (platform); - - switch (platform->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 (void) -{ - g_assert (platform); - platform->error = NM_PLATFORM_ERROR_NONE; -} +/******************************************************************/ #define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" #define IFA_F_NOPREFIXROUTE_STR "noprefixroute" @@ -208,35 +238,53 @@ nm_platform_check_support_libnl_extended_ifa_flags () } gboolean -nm_platform_check_support_kernel_extended_ifa_flags () +nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) { - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + _CHECK_SELF (self, klass, FALSE); if (!klass->check_support_kernel_extended_ifa_flags) return FALSE; - return klass->check_support_kernel_extended_ifa_flags (platform); + return klass->check_support_kernel_extended_ifa_flags (self); } gboolean -nm_platform_check_support_user_ipv6ll (void) +nm_platform_check_support_user_ipv6ll (NMPlatform *self) { static int supported = -1; - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + _CHECK_SELF (self, klass, FALSE); if (!klass->check_support_user_ipv6ll) return FALSE; if (supported < 0) - supported = klass->check_support_user_ipv6ll (platform) ? 1 : 0; + supported = klass->check_support_user_ipv6ll (self) ? 1 : 0; return !!supported; } +/** + * nm_platform_process_events: + * @self: platform instance + * + * Process pending events or handle pending delayed-actions. + * Effectively, this reads the netlink socket and processes + * new netlink messages. Possibly it will raise change signals. + */ +void +nm_platform_process_events (NMPlatform *self) +{ + _CHECK_SELF_VOID (self, klass); + + if (klass->process_events) + klass->process_events (self); +} + /******************************************************************/ /** * nm_platform_sysctl_set: + * @self: platform instance * @path: Absolute option path * @value: Value to write * @@ -247,23 +295,25 @@ nm_platform_check_support_user_ipv6ll (void) * Returns: %TRUE on success. */ gboolean -nm_platform_sysctl_set (const char *path, const char *value) +nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (path, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->sysctl_set, FALSE); - return klass->sysctl_set (platform, path, value); + return klass->sysctl_set (self, path, value); } gboolean -nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) +nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value) { const char *path; gint64 cur; + _CHECK_SELF (self, klass, FALSE); + /* the hop-limit provided via RA is uint8. */ if (value > 0xFF) return FALSE; @@ -273,7 +323,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) return FALSE; path = nm_utils_ip6_property_path (iface, "hop_limit"); - cur = nm_platform_sysctl_get_int_checked (path, 10, 1, G_MAXINT32, -1); + cur = nm_platform_sysctl_get_int_checked (self, path, 10, 1, G_MAXINT32, -1); /* only allow increasing the hop-limit to avoid DOS by an attacker * setting a low hop-limit (CVE-2015-2924, rh#1209902) */ @@ -284,7 +334,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) char svalue[20]; sprintf (svalue, "%d", value); - nm_platform_sysctl_set (path, svalue); + nm_platform_sysctl_set (self, path, svalue); } return TRUE; @@ -292,23 +342,25 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) /** * nm_platform_sysctl_get: + * @self: platform instance * @path: Absolute path to sysctl * * Returns: (transfer full): Contents of the virtual sysctl file. */ char * -nm_platform_sysctl_get (const char *path) +nm_platform_sysctl_get (NMPlatform *self, const char *path) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (path, NULL); g_return_val_if_fail (klass->sysctl_get, NULL); - return klass->sysctl_get (platform, path); + return klass->sysctl_get (self, path); } /** * nm_platform_sysctl_get_int32: + * @self: platform instance * @path: Absolute path to sysctl * @fallback: default value, if the content of path could not be read * as decimal integer. @@ -318,13 +370,14 @@ nm_platform_sysctl_get (const char *path) * value, on success %errno will be set to zero. */ gint32 -nm_platform_sysctl_get_int32 (const char *path, gint32 fallback) +nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback) { - return nm_platform_sysctl_get_int_checked (path, 10, G_MININT32, G_MAXINT32, fallback); + return nm_platform_sysctl_get_int_checked (self, path, 10, G_MININT32, G_MAXINT32, fallback); } /** * nm_platform_sysctl_get_int_checked: + * @self: platform instance * @path: Absolute path to sysctl * @base: base of numeric conversion * @min: minimal value that is still valid @@ -339,22 +392,24 @@ nm_platform_sysctl_get_int32 (const char *path, gint32 fallback) * (inclusive) or @fallback. */ gint64 -nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback) +nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint base, gint64 min, gint64 max, gint64 fallback) { char *value = NULL; gint32 ret; + _CHECK_SELF (self, klass, fallback); + g_return_val_if_fail (path, fallback); if (path) - value = nm_platform_sysctl_get (path); + value = nm_platform_sysctl_get (self, path); if (!value) { errno = EINVAL; return fallback; } - ret = nm_utils_ascii_str_to_int64 (value, base, min, max, fallback); + ret = _nm_utils_ascii_str_to_int64 (value, base, min, max, fallback); g_free (value); return ret; } @@ -362,47 +417,25 @@ nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gi /******************************************************************/ /** - * nm_platform_query_devices: - * - * Emit #NMPlatform:link-changed ADDED signals for all currently-known links. - * Should only be called at startup. - */ -void -nm_platform_query_devices (void) -{ - GArray *links_array; - NMPlatformLink *links; - int i; - - links_array = nm_platform_link_get_all (); - links = (NMPlatformLink *) links_array->data; - for (i = 0; i < links_array->len; i++) { - g_signal_emit (platform, signals[SIGNAL_LINK_CHANGED], 0, - links[i].ifindex, &links[i], NM_PLATFORM_SIGNAL_ADDED, - NM_PLATFORM_REASON_INTERNAL); - } - g_array_unref (links_array); -} - -/** * nm_platform_link_get_all: + * self: platform instance * * Retrieve a snapshot of configuration for all links at once. The result is * owned by the caller and should be freed with g_array_unref(). */ GArray * -nm_platform_link_get_all (void) +nm_platform_link_get_all (NMPlatform *self) { GArray *links, *result; guint i, j, nresult; GHashTable *unseen; NMPlatformLink *item; - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (klass->link_get_all, NULL); - links = klass->link_get_all (platform); + links = klass->link_get_all (self); if (!links || links->len == 0) return links; @@ -411,11 +444,9 @@ nm_platform_link_get_all (void) for (i = 0; i < links->len; i++) { item = &g_array_index (links, NMPlatformLink, i); - if (item->ifindex <= 0 || g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))) { - g_warn_if_reached (); - item->ifindex = 0; - continue; - } + _LOGT ("link-get: %3d: %s", i, nm_platform_link_to_string (item)); + + nm_assert (item->ifindex > 0 && !g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL); } @@ -466,6 +497,8 @@ nm_platform_link_get_all (void) if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) continue; + _LOGT ("link-get: add %3d -> %3d: %s", i, j, nm_platform_link_to_string (item)); + g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; item->ifindex = 0; @@ -473,10 +506,12 @@ nm_platform_link_get_all (void) } if (!found_something) { - /* there is a circle, pop the first (remaining) element from the list */ - g_warn_if_reached (); + /* There is a loop, pop the first (remaining) element from the list. + * This can happen for veth pairs where each peer is parent of the other end. */ item = &g_array_index (links, NMPlatformLink, first_idx); + _LOGT ("link-get: add (loop) %3d -> %3d: %s", first_idx, j, nm_platform_link_to_string (item)); + g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; item->ifindex = 0; @@ -491,182 +526,229 @@ nm_platform_link_get_all (void) /** * nm_platform_link_get: + * @self: platform instance * @ifindex: ifindex of the link - * @link: (out): output NMPlatformLink structure. * - * If a link with given @ifindex exists, fill the given NMPlatformLink - * structure. + * Lookup the internal NMPlatformLink object. * - * Returns: %TRUE, if such a link exists, %FALSE otherwise. - * If the link does not exist, the content of @link is undefined. + * Returns: %NULL, if such a link exists or the internal + * platform link object. Do not modify the returned value. + * Also, be aware that any subsequent platform call might + * invalidated/modify the returned instance. **/ -gboolean -nm_platform_link_get (int ifindex, NMPlatformLink *link) +const NMPlatformLink * +nm_platform_link_get (NMPlatform *self, int ifindex) { - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (link, FALSE); + _CHECK_SELF (self, klass, NULL); + + if (ifindex > 0) + return klass->link_get (self, ifindex); + return NULL; +} + +/** + * nm_platform_link_get_by_ifname: + * @self: platform instance + * @ifname: the ifname + * + * Returns: the first #NMPlatformLink instance with the given name. + **/ +const NMPlatformLink * +nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) +{ + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (klass->link_get, FALSE); - return !!klass->link_get (platform, ifindex, link); + if (ifname && *ifname) + return klass->link_get_by_ifname (self, ifname); + return NULL; +} + +/** + * nm_platform_link_get_by_address: + * @self: platform instance + * @address: a pointer to the binary hardware address + * @length: the size of @address in bytes + * + * Returns: the first #NMPlatformLink object with a matching + * address. + **/ +const NMPlatformLink * +nm_platform_link_get_by_address (NMPlatform *self, + gconstpointer address, + size_t length) +{ + _CHECK_SELF (self, klass, NULL); + + g_return_val_if_fail (length == 0 || address, NULL); + if (length > 0) { + if (length > NM_UTILS_HWADDR_LEN_MAX) + g_return_val_if_reached (NULL); + return klass->link_get_by_address (self, address, length); + } + return NULL; +} + +static NMPlatformError +_link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, NMPlatformLink *out_link) +{ + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get_by_ifname (self, name); + if (pllink) { + gboolean wrong_type; + + wrong_type = type != NM_LINK_TYPE_NONE && pllink->type != type; + _LOGD ("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; + } + return NM_PLATFORM_ERROR_SUCCESS; } /** * nm_platform_link_add: + * @self: platform instance * @name: Interface name * @type: Interface type * @address: (allow-none): set the mac address of the link * @address_len: the length of the @address + * @out_link: on success, the link object * - * Add a software interface. Sets platform->error to NM_PLATFORM_ERROR_EXISTS - * if interface is already already exists. Any link-changed ADDED signal will be - * emitted directly, before this function finishes. + * Add a software interface. If the interface already exists and is of type + * @type, return NM_PLATFORM_ERROR_EXISTS and returns the link + * in @out_link. If the interface already exists and is not of type @type, + * 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 -nm_platform_link_add (const char *name, NMLinkType type, const void *address, size_t address_len) +static NMPlatformError +nm_platform_link_add (NMPlatform *self, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { - reset_error (); + NMPlatformError plerr; - 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); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - if (nm_platform_link_exists (name)) { - debug ("link: already exists"); - platform->error = NM_PLATFORM_ERROR_EXISTS; - return 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); + + plerr = _link_add_check_existing (self, name, type, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; - return klass->link_add (platform, name, type, address, address_len); + _LOGD ("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; } /** * nm_platform_dummy_add: + * @self: platform instance * @name: New interface name + * @out_link: on success, the link object * * Create a software ethernet-like interface */ -gboolean -nm_platform_dummy_add (const char *name) -{ - g_return_val_if_fail (name, FALSE); - - debug ("link: adding dummy '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY, NULL, 0); -} - -/** - * nm_platform_link_exists: - * @name: Interface name - * - * Returns: %TRUE if an interface of this name exists, %FALSE otherwise. - */ -gboolean -nm_platform_link_exists (const char *name) +NMPlatformError +nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - int ifindex = nm_platform_link_get_ifindex (name); - - reset_error(); - return ifindex > 0; + return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, 0, out_link); } /** * nm_platform_link_delete: + * @self: platform instance * @ifindex: Interface index - * - * Delete a software interface. Sets platform->error to - * NM_PLATFORM_ERROR_NOT_FOUND if ifindex not available. */ gboolean -nm_platform_link_delete (int ifindex) +nm_platform_link_delete (NMPlatform *self, int ifindex) { - const char *name; + const NMPlatformLink *pllink; - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (klass->link_delete, FALSE); - - name = nm_platform_link_get_name (ifindex); - - if (!name) + pllink = nm_platform_link_get (self, ifindex); + if (!pllink) return FALSE; - debug ("link: deleting '%s' (%d)", name, ifindex); - return klass->link_delete (platform, ifindex); + _LOGD ("link: deleting '%s' (%d)", pllink->name, ifindex); + return klass->link_delete (self, ifindex); } /** * nm_platform_link_get_index: + * @self: platform instance * @name: Interface name * * Returns: The interface index corresponding to the given interface name * or 0. Inteface name is owned by #NMPlatform, don't free it. */ int -nm_platform_link_get_ifindex (const char *name) +nm_platform_link_get_ifindex (NMPlatform *self, const char *name) { - int ifindex; - - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (name, 0); - g_return_val_if_fail (klass->link_get_ifindex, 0); - - ifindex = klass->link_get_ifindex (platform, name); - - if (!ifindex) { - debug ("link not found: %s", name); - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - } - - return ifindex; + pllink = nm_platform_link_get_by_ifname (self, name); + return pllink ? pllink->ifindex : 0; } /** * nm_platform_link_get_name: + * @self: platform instance * @name: Interface name * * Returns: The interface name corresponding to the given interface index * or %NULL. */ const char * -nm_platform_link_get_name (int ifindex) +nm_platform_link_get_name (NMPlatform *self, int ifindex) { - const char *name; - - reset_error (); - - g_return_val_if_fail (klass->link_get_name, NULL); + const NMPlatformLink *pllink; - name = klass->link_get_name (platform, ifindex); - - if (!name) { - debug ("link not found: %d", ifindex); - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return FALSE; - } + _CHECK_SELF (self, klass, NULL); - return name; + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->name : NULL; } /** * nm_platform_link_get_type: + * @self: platform instance * @ifindex: Interface index. * * Returns: Link type constant as defined in nm-platform.h. On error, * NM_LINK_TYPE_NONE is returned. */ NMLinkType -nm_platform_link_get_type (int ifindex) +nm_platform_link_get_type (NMPlatform *self, int ifindex) { - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (klass->link_get_type, NM_LINK_TYPE_NONE); + _CHECK_SELF (self, klass, NM_LINK_TYPE_NONE); - return klass->link_get_type (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->type : NM_LINK_TYPE_NONE; } /** * nm_platform_link_get_type_name: + * @self: platform instance * @ifindex: Interface index. * * Returns: A string describing the type of link. In some cases this @@ -674,113 +756,200 @@ nm_platform_link_get_type (int ifindex) * other cases it may not. On error, %NULL is returned. */ const char * -nm_platform_link_get_type_name (int ifindex) +nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (klass->link_get_type_name, NULL); - return klass->link_get_type_name (platform, ifindex); + return klass->link_get_type_name (self, ifindex); +} + +/** + * nm_platform_link_get_unmanaged: + * @self: platform instance + * @ifindex: interface index + * @unmanaged: management status (in case %TRUE is returned) + * + * Returns: %TRUE if platform overrides NM default-unmanaged status, + * %FALSE otherwise (with @unmanaged unmodified). + */ +gboolean +nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged) +{ + _CHECK_SELF (self, klass, FALSE); + + if (klass->link_get_unmanaged) + return klass->link_get_unmanaged (self, ifindex, unmanaged); + return FALSE; } /** * nm_platform_link_is_software: + * @self: platform instance * @ifindex: Interface index. * * Returns: %TRUE if ifindex belongs to a software interface, not backed by * a physical device. */ gboolean -nm_platform_link_is_software (int ifindex) +nm_platform_link_is_software (NMPlatform *self, int ifindex) { - return (nm_platform_link_get_type (ifindex) & 0x10000); + return (nm_platform_link_get_type (self, ifindex) & 0x10000); } /** * nm_platform_link_supports_slaves: + * @self: platform instance * @ifindex: Interface index. * * Returns: %TRUE if ifindex belongs to an interface capable of enslaving * other interfaces. */ gboolean -nm_platform_link_supports_slaves (int ifindex) +nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) { - return (nm_platform_link_get_type (ifindex) & 0x20000); + return (nm_platform_link_get_type (self, ifindex) & 0x20000); } /** * nm_platform_link_refresh: + * @self: platform instance * @ifindex: Interface index * * Reload the cache for ifindex synchronously. */ gboolean -nm_platform_link_refresh (int ifindex) +nm_platform_link_refresh (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); if (klass->link_refresh) - return klass->link_refresh (platform, ifindex); + return klass->link_refresh (self, ifindex); return TRUE; } +static guint32 +_link_get_flags (NMPlatform *self, int ifindex) +{ + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->flags : IFF_NOARP; +} + /** * nm_platform_link_is_up: + * @self: platform instance * @ifindex: Interface index * * Check if the interface is up. */ gboolean -nm_platform_link_is_up (int ifindex) +nm_platform_link_is_up (NMPlatform *self, int ifindex) { - reset_error (); - - g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_is_up, FALSE); + _CHECK_SELF (self, klass, FALSE); - return klass->link_is_up (platform, ifindex); + return NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_UP); } /** * nm_platform_link_is_connected: + * @self: platform instance * @ifindex: Interface index * * Check if the interface is connected. */ gboolean -nm_platform_link_is_connected (int ifindex) +nm_platform_link_is_connected (NMPlatform *self, int ifindex) { - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_is_connected, FALSE); + _CHECK_SELF (self, klass, FALSE); - return klass->link_is_connected (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->connected : FALSE; } /** * nm_platform_link_uses_arp: + * @self: platform instance * @ifindex: Interface index * * Check if the interface is configured to use ARP. */ gboolean -nm_platform_link_uses_arp (int ifindex) +nm_platform_link_uses_arp (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); + + return !NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_NOARP); +} + +/** + * nm_platform_link_get_ipv6_token: + * @self: platform instance + * @ifindex: Interface index + * @iid: Tokenized interface identifier + * + * Returns IPv6 tokenized interface identifier. If the platform or OS doesn't + * support IPv6 tokenized interface identifiers, or the token is not set + * this call will fail and return %FALSE. + * + * Returns: %TRUE a tokenized identifier was available + */ +gboolean +nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid) +{ + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_uses_arp, FALSE); + g_return_val_if_fail (iid, FALSE); + +#if HAVE_LIBNL_INET6_TOKEN + { + const NMPlatformLink *pllink; - return klass->link_uses_arp (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->inet6_token.is_valid) { + *iid = pllink->inet6_token.iid; + return TRUE; + } + } +#endif + 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: + * @self: platform instance * @ifindex: Interface index * * Check whether NM handles IPv6LL address creation for the link. If the @@ -790,20 +959,28 @@ nm_platform_link_uses_arp (int ifindex) * Returns: %TRUE if NM handles the IPv6LL address for @ifindex */ gboolean -nm_platform_link_get_user_ipv6ll_enabled (int ifindex) +nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); - if (klass->link_get_user_ipv6ll_enabled) - return klass->link_get_user_ipv6ll_enabled (platform, ifindex); +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + { + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->inet6_addr_gen_mode_inv) + return _nm_platform_uint8_inv (pllink->inet6_addr_gen_mode_inv) == IN6_ADDR_GEN_MODE_NONE; + } +#endif return FALSE; } + /** * nm_platform_link_set_user_ip6vll_enabled: + * @self: platform instance * @ifindex: Interface index * * Set whether NM handles IPv6LL address creation for the link. If the @@ -813,189 +990,248 @@ nm_platform_link_get_user_ipv6ll_enabled (int ifindex) * Returns: %TRUE if the operation was successful, %FALSE if it failed. */ gboolean -nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled) +nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); if (klass->link_set_user_ipv6ll_enabled) - return klass->link_set_user_ipv6ll_enabled (platform, ifindex, enabled); + return klass->link_set_user_ipv6ll_enabled (self, ifindex, enabled); return FALSE; } /** * nm_platform_link_set_address: + * @self: platform instance * @ifindex: Interface index * @address: The new MAC address * * Set interface MAC address. */ gboolean -nm_platform_link_set_address (int ifindex, gconstpointer address, size_t length) +nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (address, FALSE); g_return_val_if_fail (length > 0, FALSE); g_return_val_if_fail (klass->link_set_address, FALSE); - debug ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_address (platform, ifindex, address, length); + _LOGD ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_address (self, ifindex, address, length); } /** * nm_platform_link_get_address: + * @self: platform instance * @ifindex: Interface index * @length: Pointer to a variable to store address length * - * Saves interface hardware address to @address. + * Returns: the interface hardware address as an array of bytes of + * length @length. */ gconstpointer -nm_platform_link_get_address (int ifindex, size_t *length) +nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) { - reset_error (); + const NMPlatformLink *pllink; + gconstpointer a = NULL; + guint8 l = 0; + + _CHECK_SELF (self, klass, NULL); if (length) *length = 0; g_return_val_if_fail (ifindex > 0, NULL); - g_return_val_if_fail (klass->link_get_address, NULL); - return klass->link_get_address (platform, ifindex, length); + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->addr.len > 0) { + if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) { + if (length) + *length = 0; + g_return_val_if_reached (NULL); + } + a = pllink->addr.data; + l = pllink->addr.len; + } + + if (length) + *length = l; + return a; +} + +/** + * nm_platform_link_get_permanent_address: + * @self: platform instance + * @ifindex: Interface index + * @buf: buffer of at least %NM_UTILS_HWADDR_LEN_MAX bytes, on success + * the permanent hardware address + * @length: Pointer to a variable to store address length + * + * Returns: %TRUE on success, %FALSE on failure to read the permanent hardware + * address. + */ +gboolean +nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length) +{ + _CHECK_SELF (self, klass, FALSE); + + if (length) + *length = 0; + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (buf, FALSE); + g_return_val_if_fail (length, FALSE); + + if (klass->link_get_permanent_address) + return klass->link_get_permanent_address (self, ifindex, buf, length); + return FALSE; } gboolean -nm_platform_link_supports_carrier_detect (int ifindex) +nm_platform_link_supports_carrier_detect (NMPlatform *self, int ifindex) { + _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_supports_carrier_detect, FALSE); - return klass->link_supports_carrier_detect (platform, ifindex); + return klass->link_supports_carrier_detect (self, ifindex); } gboolean -nm_platform_link_supports_vlans (int ifindex) +nm_platform_link_supports_vlans (NMPlatform *self, int ifindex) { + _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_supports_vlans, FALSE); - return klass->link_supports_vlans (platform, ifindex); + return klass->link_supports_vlans (self, 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 (int ifindex) +nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); 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 (ifindex), ifindex); - return klass->link_set_up (platform, ifindex); + _LOGD ("link: setting up '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_up (self, ifindex, out_no_firmware); } /** * nm_platform_link_set_down: + * @self: platform instance * @ifindex: Interface index * * Take the interface down. */ gboolean -nm_platform_link_set_down (int ifindex) +nm_platform_link_set_down (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (klass->link_set_down, FALSE); - debug ("link: setting down '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_down (platform, ifindex); + _LOGD ("link: setting down '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_down (self, ifindex); } /** * nm_platform_link_set_arp: + * @self: platform instance * @ifindex: Interface index * * Enable ARP on the interface. */ gboolean -nm_platform_link_set_arp (int ifindex) +nm_platform_link_set_arp (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_set_arp, FALSE); - debug ("link: setting arp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_arp (platform, ifindex); + _LOGD ("link: setting arp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_arp (self, ifindex); } /** * nm_platform_link_set_noarp: + * @self: platform instance * @ifindex: Interface index * * Disable ARP on the interface. */ gboolean -nm_platform_link_set_noarp (int ifindex) +nm_platform_link_set_noarp (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_set_noarp, FALSE); - debug ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_noarp (platform, ifindex); + _LOGD ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_noarp (self, ifindex); } /** * nm_platform_link_set_mtu: + * @self: platform instance * @ifindex: Interface index * @mtu: The new MTU value * * Set interface MTU. */ gboolean -nm_platform_link_set_mtu (int ifindex, guint32 mtu) +nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (mtu > 0, FALSE); g_return_val_if_fail (klass->link_set_mtu, FALSE); - debug ("link: setting '%s' (%d) mtu %"G_GUINT32_FORMAT, nm_platform_link_get_name (ifindex), ifindex, mtu); - return klass->link_set_mtu (platform, ifindex, mtu); + _LOGD ("link: setting '%s' (%d) mtu %"G_GUINT32_FORMAT, nm_platform_link_get_name (self, ifindex), ifindex, mtu); + return klass->link_set_mtu (self, ifindex, mtu); } /** * nm_platform_link_get_mtu: + * @self: platform instance * @ifindex: Interface index * * Returns: MTU value for the interface or 0 on error. */ guint32 -nm_platform_link_get_mtu (int ifindex) +nm_platform_link_get_mtu (NMPlatform *self, int ifindex) { - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (ifindex >= 0, 0); - g_return_val_if_fail (klass->link_get_mtu, 0); + _CHECK_SELF (self, klass, 0); - return klass->link_get_mtu (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->mtu : 0; } /** * nm_platform_link_get_physical_port_id: + * @self: platform instance * @ifindex: Interface index * * The physical port ID, if present, indicates some unique identifier of @@ -1008,18 +1244,20 @@ nm_platform_link_get_mtu (int ifindex) * or if the interface has no physical port ID. */ char * -nm_platform_link_get_physical_port_id (int ifindex) +nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex >= 0, NULL); - g_return_val_if_fail (klass->link_get_physical_port_id, NULL); - return klass->link_get_physical_port_id (platform, ifindex); + if (klass->link_get_physical_port_id) + return klass->link_get_physical_port_id (self, ifindex); + return NULL; } /** * nm_platform_link_get_dev_id: + * @self: platform instance * @ifindex: Interface index * * In contrast to the physical device ID (which indicates which parent a @@ -1030,233 +1268,277 @@ nm_platform_link_get_physical_port_id (int ifindex) * interface has no device ID. */ guint -nm_platform_link_get_dev_id (int ifindex) +nm_platform_link_get_dev_id (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex >= 0, 0); - g_return_val_if_fail (klass->link_get_dev_id, 0); - return klass->link_get_dev_id (platform, ifindex); + if (klass->link_get_dev_id) + return klass->link_get_dev_id (self, ifindex); + return 0; } /** * nm_platform_link_get_wake_onlan: + * @self: platform instance * @ifindex: Interface index * * Returns: the "Wake-on-LAN" status for @ifindex. */ gboolean -nm_platform_link_get_wake_on_lan (int ifindex) +nm_platform_link_get_wake_on_lan (NMPlatform *self, int ifindex) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex >= 0, FALSE); + + if (klass->link_get_wake_on_lan) + return klass->link_get_wake_on_lan (self, ifindex); + return FALSE; +} + +/** + * nm_platform_link_get_driver_info: + * @self: platform instance + * @ifindex: Interface index + * @out_driver_name: (transfer full): on success, the driver name if available + * @out_driver_version: (transfer full): on success, the driver version if available + * @out_fw_version: (transfer full): on success, the firmware version if available + * + * Returns: %TRUE on success (though @out_driver_name, @out_driver_version and + * @out_fw_version can be %NULL if no information was available), %FALSE on + * failure. + */ +gboolean +nm_platform_link_get_driver_info (NMPlatform *self, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_get_wake_on_lan, FALSE); + g_return_val_if_fail (klass->link_get_driver_info, FALSE); - return klass->link_get_wake_on_lan (platform, ifindex); + return klass->link_get_driver_info (self, + ifindex, + out_driver_name, + out_driver_version, + out_fw_version); } /** * nm_platform_link_enslave: + * @self: platform instance * @master: Interface index of the master * @slave: Interface index of the slave * * Enslave @slave to @master. */ gboolean -nm_platform_link_enslave (int master, int slave) +nm_platform_link_enslave (NMPlatform *self, int master, int slave) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (master > 0, FALSE); g_return_val_if_fail (slave> 0, FALSE); g_return_val_if_fail (klass->link_enslave, FALSE); - debug ("link: enslaving '%s' (%d) to master '%s' (%d)", - nm_platform_link_get_name (slave), slave, - nm_platform_link_get_name (master), master); - return klass->link_enslave (platform, master, slave); + _LOGD ("link: enslaving '%s' (%d) to master '%s' (%d)", + nm_platform_link_get_name (self, slave), slave, + nm_platform_link_get_name (self, master), master); + return klass->link_enslave (self, master, slave); } /** * nm_platform_link_release: + * @self: platform instance * @master: Interface index of the master * @slave: Interface index of the slave * * Release @slave from @master. */ gboolean -nm_platform_link_release (int master, int slave) +nm_platform_link_release (NMPlatform *self, int master, int slave) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); 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 (slave) != master) { - platform->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 (slave), slave, - nm_platform_link_get_name (master), master); - return klass->link_release (platform, master, slave); + _LOGD ("link: releasing '%s' (%d) from master '%s' (%d)", + nm_platform_link_get_name (self, slave), slave, + nm_platform_link_get_name (self, master), master); + return klass->link_release (self, master, slave); } /** * nm_platform_link_get_master: + * @self: platform instance * @slave: Interface index of the slave. * * Returns: Interfase index of the slave's master. */ int -nm_platform_link_get_master (int slave) +nm_platform_link_get_master (NMPlatform *self, int slave) { - reset_error (); + const NMPlatformLink *pllink; + + _CHECK_SELF (self, klass, 0); - g_assert (platform); g_return_val_if_fail (slave >= 0, FALSE); - g_return_val_if_fail (klass->link_get_master, FALSE); - if (!nm_platform_link_get_name (slave)) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return 0; - } - return klass->link_get_master (platform, slave); + pllink = nm_platform_link_get (self, slave); + return pllink ? pllink->master : 0; } /** * nm_platform_bridge_add: + * @self: platform instance * @name: New interface name * @address: (allow-none): set the mac address of the new bridge * @address_len: the length of the @address + * @out_link: on success, the link object * * Create a software bridge. */ -gboolean -nm_platform_bridge_add (const char *name, const void *address, size_t address_len) +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 (name, NM_LINK_TYPE_BRIDGE, address, address_len); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, address, address_len, out_link); } /** * nm_platform_bond_add: + * @self: platform instance * @name: New interface name + * @out_link: on success, the link object * * Create a software bonding device. */ -gboolean -nm_platform_bond_add (const char *name) +NMPlatformError +nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - debug ("link: adding bond '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_BOND, NULL, 0); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, 0, out_link); } /** * nm_platform_team_add: + * @self: platform instance * @name: New interface name + * @out_link: on success, the link object * * Create a software teaming device. */ -gboolean -nm_platform_team_add (const char *name) +NMPlatformError +nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - debug ("link: adding team '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_TEAM, NULL, 0); + return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, 0, out_link); } /** * nm_platform_vlan_add: + * @self: platform instance * @name: New interface name * @vlanid: VLAN identifier * @vlanflags: VLAN flags from libnm + * @out_link: on success, the link object * * Create a software VLAN device. */ -gboolean -nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags) +NMPlatformError +nm_platform_vlan_add (NMPlatform *self, + const char *name, + int parent, + int vlanid, + guint32 vlanflags, + NMPlatformLink *out_link) { - reset_error (); + NMPlatformError plerr; - g_assert (platform); - 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 (name)) { - debug ("link already exists: %s", name); - platform->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 (platform, name, parent, vlanid, vlanflags); + _LOGD ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x", + 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 (int ifindex, const char *option, const char *value) +nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->master_set_option, FALSE); - return klass->master_set_option (platform, ifindex, option, value); + return klass->master_set_option (self, ifindex, option, value); } char * -nm_platform_master_get_option (int ifindex, const char *option) +nm_platform_master_get_option (NMPlatform *self, int ifindex, const char *option) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (klass->master_set_option, FALSE); - return klass->master_get_option (platform, ifindex, option); + return klass->master_get_option (self, ifindex, option); } gboolean -nm_platform_slave_set_option (int ifindex, const char *option, const char *value) +nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->slave_set_option, FALSE); - return klass->slave_set_option (platform, ifindex, option, value); + return klass->slave_set_option (self, ifindex, option, value); } char * -nm_platform_slave_get_option (int ifindex, const char *option) +nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (klass->slave_set_option, FALSE); - return klass->slave_get_option (platform, ifindex, option); + return klass->slave_get_option (self, ifindex, option); } gboolean -nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid) +nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (klass->vlan_get_info, FALSE); if (parent) @@ -1264,261 +1546,274 @@ nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid) if (vlanid) *vlanid = 0; - if (nm_platform_link_get_type (ifindex) != NM_LINK_TYPE_VLAN) + if (nm_platform_link_get_type (self, ifindex) != NM_LINK_TYPE_VLAN) return FALSE; - return klass->vlan_get_info (platform, ifindex, parent, vlanid); + return klass->vlan_get_info (self, ifindex, parent, vlanid); } gboolean -nm_platform_vlan_set_ingress_map (int ifindex, int from, int to) +nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE); - debug ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to); - return klass->vlan_set_ingress_map (platform, ifindex, from, to); + _LOGD ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to); + return klass->vlan_set_ingress_map (self, ifindex, from, to); } gboolean -nm_platform_vlan_set_egress_map (int ifindex, int from, int to) +nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (klass->vlan_set_egress_map, FALSE); - debug ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to); - return klass->vlan_set_egress_map (platform, ifindex, from, to); + _LOGD ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to); + return klass->vlan_set_egress_map (self, ifindex, from, to); } -gboolean -nm_platform_infiniband_partition_add (int parent, int p_key) +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; - reset_error (); + _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 (parent) != NM_LINK_TYPE_INFINIBAND) { - platform->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 (parent); name = g_strdup_printf ("%s.%04x", parent_name, p_key); - if (nm_platform_link_exists (name)) { - debug ("infiniband: already exists"); - platform->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 (platform, parent, p_key); + _LOGD ("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 -nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *props) +nm_platform_infiniband_get_info (NMPlatform *self, + int ifindex, + int *parent, + int *p_key, + const char **mode) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (klass->infiniband_get_info, FALSE); + + return klass->infiniband_get_info (self, ifindex, parent, p_key, mode); +} + +gboolean +nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *props) +{ + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->veth_get_properties (platform, ifindex, props); + return klass->veth_get_properties (self, ifindex, props); } gboolean -nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *props) +nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->tun_get_properties (platform, ifindex, props); + return klass->tun_get_properties (self, ifindex, props); } gboolean -nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props) +nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->macvlan_get_properties (platform, ifindex, props); + return klass->macvlan_get_properties (self, ifindex, props); } gboolean -nm_platform_vxlan_get_properties (int ifindex, NMPlatformVxlanProperties *props) +nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->vxlan_get_properties (platform, ifindex, props); + return klass->vxlan_get_properties (self, ifindex, props); } gboolean -nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props) +nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->gre_get_properties (platform, ifindex, props); + return klass->gre_get_properties (self, ifindex, props); } gboolean -nm_platform_wifi_get_capabilities (int ifindex, NMDeviceWifiCapabilities *caps) +nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - return klass->wifi_get_capabilities (platform, ifindex, caps); + return klass->wifi_get_capabilities (self, ifindex, caps); } gboolean -nm_platform_wifi_get_bssid (int ifindex, guint8 *bssid) +nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - return klass->wifi_get_bssid (platform, ifindex, bssid); + return klass->wifi_get_bssid (self, ifindex, bssid); } GByteArray * -nm_platform_wifi_get_ssid (int ifindex) +nm_platform_wifi_get_ssid (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); - return klass->wifi_get_ssid (platform, ifindex); + return klass->wifi_get_ssid (self, ifindex); } guint32 -nm_platform_wifi_get_frequency (int ifindex) +nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->wifi_get_frequency (platform, ifindex); + return klass->wifi_get_frequency (self, ifindex); } int -nm_platform_wifi_get_quality (int ifindex) +nm_platform_wifi_get_quality (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->wifi_get_quality (platform, ifindex); + return klass->wifi_get_quality (self, ifindex); } guint32 -nm_platform_wifi_get_rate (int ifindex) +nm_platform_wifi_get_rate (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->wifi_get_rate (platform, ifindex); + return klass->wifi_get_rate (self, ifindex); } NM80211Mode -nm_platform_wifi_get_mode (int ifindex) +nm_platform_wifi_get_mode (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NM_802_11_MODE_UNKNOWN); g_return_val_if_fail (ifindex > 0, NM_802_11_MODE_UNKNOWN); - return klass->wifi_get_mode (platform, ifindex); + return klass->wifi_get_mode (self, ifindex); } void -nm_platform_wifi_set_mode (int ifindex, NM80211Mode mode) +nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode) { - reset_error (); + _CHECK_SELF_VOID (self, klass); g_return_if_fail (ifindex > 0); - klass->wifi_set_mode (platform, ifindex, mode); + klass->wifi_set_mode (self, ifindex, mode); } guint32 -nm_platform_wifi_find_frequency (int ifindex, const guint32 *freqs) +nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); g_return_val_if_fail (freqs != NULL, 0); - return klass->wifi_find_frequency (platform, ifindex, freqs); + return klass->wifi_find_frequency (self, ifindex, freqs); } void -nm_platform_wifi_indicate_addressing_running (int ifindex, gboolean running) +nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running) { - reset_error (); + _CHECK_SELF_VOID (self, klass); g_return_if_fail (ifindex > 0); - klass->wifi_indicate_addressing_running (platform, ifindex, running); + klass->wifi_indicate_addressing_running (self, ifindex, running); } guint32 -nm_platform_mesh_get_channel (int ifindex) +nm_platform_mesh_get_channel (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->mesh_get_channel (platform, ifindex); + return klass->mesh_get_channel (self, ifindex); } gboolean -nm_platform_mesh_set_channel (int ifindex, guint32 channel) +nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - return klass->mesh_set_channel (platform, ifindex, channel); + return klass->mesh_set_channel (self, ifindex, channel); } gboolean -nm_platform_mesh_set_ssid (int ifindex, const guint8 *ssid, gsize len) +nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (ssid != NULL, FALSE); - return klass->mesh_set_ssid (platform, ifindex, ssid, len); + return klass->mesh_set_ssid (self, ifindex, ssid, len); } #define TO_STRING_DEV_BUF_SIZE (5+15+1) static const char * -_to_string_dev (int ifindex, char *buf, size_t size) +_to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) { g_assert (buf && size >= TO_STRING_DEV_BUF_SIZE); if (ifindex) { - const char *name = ifindex > 0 ? nm_platform_link_get_name (ifindex) : NULL; + const char *name = ifindex > 0 && self ? nm_platform_link_get_name (self, ifindex) : NULL; char *buf2; strcpy (buf, " dev "); @@ -1538,29 +1833,30 @@ _to_string_dev (int ifindex, char *buf, size_t size) /******************************************************************/ GArray * -nm_platform_ip4_address_get_all (int ifindex) +nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (klass->ip4_address_get_all, NULL); - return klass->ip4_address_get_all (platform, ifindex); + return klass->ip4_address_get_all (self, ifindex); } GArray * -nm_platform_ip6_address_get_all (int ifindex) +nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (klass->ip6_address_get_all, NULL); - return klass->ip6_address_get_all (platform, ifindex); + return klass->ip6_address_get_all (self, ifindex); } gboolean -nm_platform_ip4_address_add (int ifindex, +nm_platform_ip4_address_add (NMPlatform *self, + int ifindex, in_addr_t address, in_addr_t peer_address, int plen, @@ -1568,7 +1864,7 @@ nm_platform_ip4_address_add (int ifindex, guint32 preferred, const char *label) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -1590,13 +1886,14 @@ nm_platform_ip4_address_add (int ifindex, if (label) g_strlcpy (addr.label, label, sizeof (addr.label)); - debug ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr)); + _LOGD ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr)); } - return klass->ip4_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, label); + return klass->ip4_address_add (self, ifindex, address, peer_address, plen, lifetime, preferred, label); } gboolean -nm_platform_ip6_address_add (int ifindex, +nm_platform_ip6_address_add (NMPlatform *self, + int ifindex, struct in6_addr address, struct in6_addr peer_address, int plen, @@ -1604,7 +1901,7 @@ nm_platform_ip6_address_add (int ifindex, guint32 preferred, guint flags) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -1624,74 +1921,72 @@ nm_platform_ip6_address_add (int ifindex, addr.preferred = preferred; addr.flags = flags; - debug ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr)); + _LOGD ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr)); } - return klass->ip6_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, flags); + return klass->ip6_address_add (self, ifindex, address, peer_address, plen, lifetime, preferred, flags); } gboolean -nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen, in_addr_t peer_address) +nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address) { char str_dev[TO_STRING_DEV_BUF_SIZE]; char str_peer[NM_UTILS_INET_ADDRSTRLEN]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip4_address_delete, FALSE); - debug ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s", + _LOGD ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s", nm_utils_inet4_ntop (address, NULL), plen, peer_address ? "peer " : "", peer_address ? nm_utils_inet4_ntop (peer_address, str_peer) : "", peer_address ? ", " : "", ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip4_address_delete (platform, ifindex, address, plen, peer_address); + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip4_address_delete (self, ifindex, address, plen, peer_address); } gboolean -nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen) +nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, int plen) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip6_address_delete, FALSE); - debug ("address: deleting IPv6 address %s/%d, ifindex %d%s", + _LOGD ("address: deleting IPv6 address %s/%d, ifindex %d%s", nm_utils_inet6_ntop (&address, NULL), plen, ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip6_address_delete (platform, ifindex, address, plen); + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip6_address_delete (self, ifindex, address, plen); } -gboolean -nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen) +const NMPlatformIP4Address * +nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (plen > 0, FALSE); - g_return_val_if_fail (klass->ip4_address_exists, FALSE); - return klass->ip4_address_exists (platform, ifindex, address, plen); + return klass->ip4_address_get (self, ifindex, address, plen); } -gboolean -nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen) +const NMPlatformIP6Address * +nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (plen > 0, FALSE); - g_return_val_if_fail (klass->ip6_address_exists, FALSE); - return klass->ip6_address_exists (platform, ifindex, address, plen); + return klass->ip6_address_get (self, ifindex, address, plen); } static gboolean -array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address) +array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address, gint64 now, guint32 padding) { guint len = addresses ? addresses->len : 0; guint i; @@ -1699,15 +1994,20 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address for (i = 0; i < len; i++) { NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i); - if (candidate->address == address->address && candidate->plen == address->plen) - return TRUE; + if (candidate->address == address->address && candidate->plen == address->plen) { + guint32 lifetime, preferred; + + if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, + now, padding, &lifetime, &preferred)) + return TRUE; + } } return FALSE; } static gboolean -array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address) +array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address, gint64 now, guint32 padding) { guint len = addresses ? addresses->len : 0; guint i; @@ -1715,108 +2015,27 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address for (i = 0; i < len; i++) { NMPlatformIP6Address *candidate = &g_array_index (addresses, NMPlatformIP6Address, i); - if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) - return TRUE; - } - - return FALSE; -} - -/** - * Takes a pair @timestamp and @duration, and returns the remaining duration based - * on the new timestamp @now. - */ -static guint32 -_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, guint32 now, guint32 padding) -{ - gint64 t; - - if (duration == NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT; - - if (timestamp == 0) { - /* if the @timestamp is zero, assume it was just left unset and that the relative - * @duration starts counting from @now. This is convenient to construct an address - * and print it in nm_platform_ip4_address_to_string(). - * - * In general it does not make sense to set the @duration without anchoring at - * @timestamp because you don't know the absolute expiration time when looking - * at the address at a later moment. */ - timestamp = now; - } + if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) { + guint32 lifetime, preferred; - /* For timestamp > now, just accept it and calculate the expected(?) result. */ - t = (gint64) timestamp + (gint64) duration - (gint64) now; - - /* Optional padding to avoid potential races. */ - t += (gint64) padding; - - if (t <= 0) - return 0; - if (t >= NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT - 1; - return t; -} - -static gboolean -_address_get_lifetime (const NMPlatformIPAddress *address, guint32 now, guint32 padding, guint32 *out_lifetime, guint32 *out_preferred) -{ - guint32 lifetime, preferred; - - if (address->lifetime == 0) { - *out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - *out_preferred = NM_PLATFORM_LIFETIME_PERMANENT; - - /* We treat lifetime==0 as permanent addresses to allow easy creation of such addresses - * (without requiring to set the lifetime fields to NM_PLATFORM_LIFETIME_PERMANENT). - * In that case we also expect that the other fields (timestamp and preferred) are left unset. */ - g_return_val_if_fail (address->timestamp == 0 && address->preferred == 0, TRUE); - } else { - lifetime = _rebase_relative_time_on_now (address->timestamp, address->lifetime, now, padding); - if (!lifetime) - return FALSE; - preferred = _rebase_relative_time_on_now (address->timestamp, address->preferred, now, padding); - - *out_lifetime = lifetime; - *out_preferred = MIN (preferred, lifetime); - - /* Assert that non-permanent addresses have a (positive) @timestamp. _rebase_relative_time_on_now() - * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always - * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. - */ - g_return_val_if_fail ( address->timestamp != 0 - || ( address->lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && address->preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); - g_return_val_if_fail (preferred <= lifetime, TRUE); - } - return TRUE; -} - -gboolean -nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - g_return_val_if_fail (address, FALSE); - - if ( ifindex <= 0 - || address->plen <= 0 - || address->plen >= 32) - return FALSE; - - if (device_route_metric == NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { - /* The automatically added route would be already our desired priority. - * Nothing to do. */ - return FALSE; + if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, + now, padding, &lifetime, &preferred)) + return TRUE; + } } - return klass->ip4_check_reinstall_device_route (platform, ifindex, address, device_route_metric); + return FALSE; } /** * nm_platform_ip4_address_sync: + * @self: platform instance * @ifindex: Interface index * @known_addresses: List of addresses - * @device_route_metric: the route metric for adding subnet routes (replaces - * the kernel added routes). + * @out_added_addresses: (out): (allow-none): if not %NULL, return a #GPtrArray + * with the addresses added. The pointers point into @known_addresses. + * It possibly does not contain all addresses from @known_address because + * some addresses might be expired. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -1825,23 +2044,28 @@ nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Ad * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint32 device_route_metric) +nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses) { GArray *addresses; NMPlatformIP4Address *address; guint32 now = nm_utils_get_monotonic_timestamp_s (); int i; + _CHECK_SELF (self, klass, FALSE); + /* Delete unknown addresses */ - addresses = nm_platform_ip4_address_get_all (ifindex); + addresses = nm_platform_ip4_address_get_all (self, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); - if (!array_contains_ip4_address (known_addresses, address)) - nm_platform_ip4_address_delete (ifindex, address->address, address->plen, address->peer_address); + if (!array_contains_ip4_address (known_addresses, address, now, ADDRESS_LIFETIME_PADDING)) + nm_platform_ip4_address_delete (self, ifindex, address->address, address->plen, address->peer_address); } g_array_free (addresses, TRUE); + if (out_added_addresses) + *out_added_addresses = NULL; + if (!known_addresses) return TRUE; @@ -1849,33 +2073,18 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 for (i = 0; i < known_addresses->len; i++) { const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i); guint32 lifetime, preferred; - guint32 network; - gboolean reinstall_device_route = FALSE; - /* add a padding of 5 seconds to avoid potential races. */ - if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, 5, &lifetime, &preferred)) + if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred)) continue; - if (nm_platform_ip4_check_reinstall_device_route (ifindex, known_address, device_route_metric)) - reinstall_device_route = TRUE; - - if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) + if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) return FALSE; - if (reinstall_device_route) { - /* Kernel automatically adds a device route for us with metric 0. That is not what we want. - * Remove it, and re-add it. - * - * In face of having the same subnets on two different interfaces with the same metric, - * this is a problem. Surprisingly, kernel is able to add two routes for the same subnet/prefix,metric - * to different interfaces. We cannot. Adding one, would replace the other. This is avoided - * by the above nm_platform_ip4_check_reinstall_device_route() check. - */ - network = nm_utils_ip4_address_clear_host_address (known_address->address, known_address->plen); - (void) nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_KERNEL, network, known_address->plen, - 0, known_address->address, device_route_metric, 0); - (void) nm_platform_ip4_route_delete (ifindex, network, known_address->plen, - NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE); + if (out_added_addresses) { + if (!*out_added_addresses) + *out_added_addresses = g_ptr_array_new (); + g_ptr_array_add (*out_added_addresses, (gpointer) known_address); } } @@ -1884,6 +2093,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 /** * nm_platform_ip6_address_sync: + * @self: platform instance * @ifindex: Interface index * @known_addresses: List of addresses * @keep_link_local: Don't remove link-local address @@ -1895,7 +2105,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 * Returns: %TRUE on success. */ gboolean -nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboolean keep_link_local) +nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local) { GArray *addresses; NMPlatformIP6Address *address; @@ -1903,7 +2113,7 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole int i; /* Delete unknown addresses */ - addresses = nm_platform_ip6_address_get_all (ifindex); + addresses = nm_platform_ip6_address_get_all (self, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP6Address, i); @@ -1911,8 +2121,8 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) continue; - if (!array_contains_ip6_address (known_addresses, address)) - nm_platform_ip6_address_delete (ifindex, address->address, address->plen); + if (!array_contains_ip6_address (known_addresses, address, now, ADDRESS_LIFETIME_PADDING)) + nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); } g_array_free (addresses, TRUE); @@ -1924,11 +2134,11 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i); guint32 lifetime, preferred; - /* add a padding of 5 seconds to avoid potential races. */ - if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, 5, &lifetime, &preferred)) + if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred)) continue; - if (!nm_platform_ip6_address_add (ifindex, known_address->address, + if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->flags)) return FALSE; @@ -1938,51 +2148,50 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole } gboolean -nm_platform_address_flush (int ifindex) +nm_platform_address_flush (NMPlatform *self, int ifindex) { - return nm_platform_ip4_address_sync (ifindex, NULL, 0) - && nm_platform_ip6_address_sync (ifindex, NULL, FALSE); + _CHECK_SELF (self, klass, FALSE); + + return nm_platform_ip4_address_sync (self, ifindex, NULL, NULL) + && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); } /******************************************************************/ GArray * -nm_platform_ip4_route_get_all (int ifindex, NMPlatformGetRouteMode mode) +nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex >= 0, NULL); - g_return_val_if_fail (klass->ip4_route_get_all, NULL); - return klass->ip4_route_get_all (platform, ifindex, mode); + return klass->ip4_route_get_all (self, ifindex, flags); } GArray * -nm_platform_ip6_route_get_all (int ifindex, NMPlatformGetRouteMode mode) +nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex >= 0, NULL); - g_return_val_if_fail (klass->ip6_route_get_all, NULL); - return klass->ip6_route_get_all (platform, ifindex, mode); + return klass->ip6_route_get_all (self, ifindex, flags); } gboolean -nm_platform_ip4_route_add (int ifindex, NMIPConfigSource source, +nm_platform_ip4_route_add (NMPlatform *self, + int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (platform, FALSE); g_return_val_if_fail (0 <= plen && plen <= 32, FALSE); g_return_val_if_fail (klass->ip4_route_add, FALSE); if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { NMPlatformIP4Route route = { 0 }; - char pref_src_buf[NM_UTILS_INET_ADDRSTRLEN]; route.ifindex = ifindex; route.source = source; @@ -1991,21 +2200,21 @@ nm_platform_ip4_route_add (int ifindex, NMIPConfigSource source, route.gateway = gateway; route.metric = metric; route.mss = mss; + route.pref_src = pref_src; - debug ("route: adding or updating IPv4 route: %s%s%s%s", nm_platform_ip4_route_to_string (&route), - pref_src ? " (src: " : "", - pref_src ? nm_utils_inet4_ntop (pref_src, pref_src_buf) : "", - pref_src ? ")" : ""); + _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route)); } - return klass->ip4_route_add (platform, ifindex, source, network, plen, gateway, pref_src, metric, mss); + return klass->ip4_route_add (self, ifindex, source, network, plen, gateway, pref_src, metric, mss); } gboolean -nm_platform_ip6_route_add (int ifindex, NMIPConfigSource source, +nm_platform_ip6_route_add (NMPlatform *self, + int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss) { - g_return_val_if_fail (platform, FALSE); + _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (0 <= plen && plen <= 128, FALSE); g_return_val_if_fail (klass->ip6_route_add, FALSE); @@ -2020,256 +2229,55 @@ nm_platform_ip6_route_add (int ifindex, NMIPConfigSource source, route.metric = metric; route.mss = mss; - debug ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route)); + _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route)); } - return klass->ip6_route_add (platform, ifindex, source, network, plen, gateway, metric, mss); + return klass->ip6_route_add (self, ifindex, source, network, plen, gateway, metric, mss); } gboolean -nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, guint32 metric) +nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (platform, FALSE); g_return_val_if_fail (klass->ip4_route_delete, FALSE); - debug ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", + _LOGD ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", nm_utils_inet4_ntop (network, NULL), plen, metric, ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip4_route_delete (platform, ifindex, network, plen, metric); + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip4_route_delete (self, ifindex, network, plen, metric); } gboolean -nm_platform_ip6_route_delete (int ifindex, struct in6_addr network, int plen, guint32 metric) +nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (platform, FALSE); g_return_val_if_fail (klass->ip6_route_delete, FALSE); - debug ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", + _LOGD ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", nm_utils_inet6_ntop (&network, NULL), plen, metric, ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip6_route_delete (platform, ifindex, network, plen, metric); -} - -gboolean -nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, guint32 metric) -{ - reset_error (); - - g_return_val_if_fail (platform, FALSE); - g_return_val_if_fail (klass->ip4_route_exists, FALSE); - - return klass->ip4_route_exists (platform,ifindex, network, plen, metric); -} - -gboolean -nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - reset_error (); - - g_return_val_if_fail (platform, FALSE); - g_return_val_if_fail (klass->ip6_route_exists, FALSE); - - return klass->ip6_route_exists (platform, ifindex, network, plen, metric); -} - -static gboolean -array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route) -{ - guint len = routes ? routes->len : 0; - guint i; - - for (i = 0; i < len; i++) { - NMPlatformIP4Route *c = &g_array_index (routes, NMPlatformIP4Route, i); - - if (route->network == c->network && - route->plen == c->plen && - route->gateway == c->gateway && - route->metric == c->metric) - return TRUE; - } - - return FALSE; + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip6_route_delete (self, ifindex, network, plen, metric); } -static gboolean -array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route) +const NMPlatformIP4Route * +nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric) { - guint len = routes ? routes->len : 0; - guint i; + _CHECK_SELF (self, klass, FALSE); - for (i = 0; i < len; i++) { - NMPlatformIP6Route *c = &g_array_index (routes, NMPlatformIP6Route, i); - - if (IN6_ARE_ADDR_EQUAL (&route->network, &c->network) && - route->plen == c->plen && - IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway) && - route->metric == c->metric) - return TRUE; - } - - return FALSE; + return klass->ip4_route_get (self ,ifindex, network, plen, metric); } -/** - * nm_platform_ip4_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes) +const NMPlatformIP6Route * +nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric) { - GArray *routes; - NMPlatformIP4Route *route; - const NMPlatformIP4Route *known_route; - gboolean success; - int i, i_type; - - /* Delete unknown routes */ - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); - for (i = 0; i < routes->len; i++) { - route = &g_array_index (routes, NMPlatformIP4Route, i); - - if (!array_contains_ip4_route (known_routes, route)) - (void) nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric); - } - - if (!known_routes) { - g_array_free (routes, TRUE); - return TRUE; - } - - /* Add missing routes */ - for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) { - for (i = 0; i < known_routes->len && success; i++) { - known_route = &g_array_index (known_routes, NMPlatformIP4Route, i); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) - continue; + _CHECK_SELF (self, klass, FALSE); - if ( (i_type == 0 && known_route->gateway != 0) - || (i_type == 1 && known_route->gateway == 0)) { - /* Make two runs over the list of routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - /* Ignore routes that already exist */ - if (!array_contains_ip4_route (routes, known_route)) { - success = nm_platform_ip4_route_add (ifindex, - known_route->source, - known_route->network, - known_route->plen, - known_route->gateway, - 0, - known_route->metric, - known_route->mss); - if (!success && known_route->source < NM_IP_CONFIG_SOURCE_USER) { - nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv4 route to kernel: %s", - nm_platform_ip4_route_to_string (known_route)); - success = TRUE; - } - } - } - } - - g_array_free (routes, TRUE); - return success; -} - -/** - * nm_platform_ip6_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes) -{ - GArray *routes; - NMPlatformIP6Route *route; - const NMPlatformIP6Route *known_route; - gboolean success; - int i, i_type; - - /* Delete unknown routes */ - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); - for (i = 0; i < routes->len; i++) { - route = &g_array_index (routes, NMPlatformIP6Route, i); - route->ifindex = 0; - - if (!array_contains_ip6_route (known_routes, route)) - nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric); - } - - if (!known_routes) { - g_array_free (routes, TRUE); - return TRUE; - } - - /* Add missing routes */ - for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) { - for (i = 0; i < known_routes->len && success; i++) { - known_route = &g_array_index (known_routes, NMPlatformIP6Route, i); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) - continue; - - if ( (i_type == 0 && !IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway)) - || (i_type == 1 && IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway))) { - /* Make two runs over the list of routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - /* Ignore routes that already exist */ - if (!array_contains_ip6_route (routes, known_route)) { - success = nm_platform_ip6_route_add (ifindex, - known_route->source, - known_route->network, - known_route->plen, - known_route->gateway, - known_route->metric, - known_route->mss); - if (!success && known_route->source < NM_IP_CONFIG_SOURCE_USER) { - nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv6 route to kernel: %s", - nm_platform_ip6_route_to_string (known_route)); - success = TRUE; - } - } - } - } - - g_array_free (routes, TRUE); - return success; -} - -gboolean -nm_platform_route_flush (int ifindex) -{ - return nm_platform_ip4_route_sync (ifindex, NULL) - && nm_platform_ip6_route_sync (ifindex, NULL); + return klass->ip6_route_get (self, ifindex, network, plen, metric); } /******************************************************************/ @@ -2278,6 +2286,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: @@ -2309,7 +2321,7 @@ _lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, return "forever"; g_snprintf (buf, buf_size, "%usec", - _rebase_relative_time_on_now (timestamp, lifetime, now, 0)); + nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now, 0)); return buf; } @@ -2322,28 +2334,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, *type; - 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); @@ -2355,19 +2377,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; - type = link->type_name ? NULL : g_strdup_printf ("(%d)", link->type); - - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%d: %s%s <%s> mtu %d%s %s%s%s", - link->ifindex, link->name, parent, str->str, - link->mtu, master, link->type_name ? link->type_name : type, - driver ? driver : "", udi ? udi : ""); - g_string_free (str, TRUE); - g_free (driver); - g_free (udi); - g_free (type); - return to_string_buffer; + 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 (_nm_platform_uint8_inv (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", _nm_platform_uint8_inv (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" /* 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, + 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; } /** @@ -2403,7 +2475,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) str_peer = g_strconcat (" ptp ", s_peer, NULL); } - _to_string_dev (address->ifindex, str_dev, sizeof (str_dev)); + _to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev)); if (*address->label) g_snprintf (str_label, sizeof (str_label), " label %s", address->label); @@ -2420,14 +2492,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; } /** @@ -2488,7 +2560,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) str_peer = g_strconcat (" ptp ", s_peer, NULL); } - _to_string_dev (address->ifindex, str_dev, sizeof (str_dev)); + _to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev)); nm_platform_addr_flags2str (address->flags, s_flags, sizeof (s_flags)); @@ -2504,7 +2576,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, @@ -2512,7 +2584,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; } /** @@ -2531,21 +2603,38 @@ const char * nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) { char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; + char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_scope[30]; g_return_val_if_fail (route, "(unknown)"); inet_ntop (AF_INET, &route->network, s_network, sizeof(s_network)); inet_ntop (AF_INET, &route->gateway, s_gateway, sizeof(s_gateway)); - _to_string_dev (route->ifindex, str_dev, sizeof (str_dev)); - - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", - s_network, route->plen, s_gateway, + _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); + + 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" /* source */ + "%s%s" /* scope */ + "%s%s" /* pref-src */ + "", + s_network, route->plen, + s_gateway, str_dev, - route->metric, route->mss, - source_to_string (route->source)); - return to_string_buffer; + route->metric, + route->mss, + source_to_string (route->source), + route->scope_inv ? " scope " : "", + route->scope_inv ? (rtnl_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "", + route->pref_src ? " pref-src " : "", + route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : ""); + return _nm_platform_to_string_buffer; } /** @@ -2571,14 +2660,23 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network)); inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway)); - _to_string_dev (route->ifindex, str_dev, sizeof (str_dev)); - - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", - s_network, route->plen, s_gateway, + _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); + + 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" /* source */ + "", + s_network, route->plen, + s_gateway, str_dev, - route->metric, route->mss, + route->metric, + route->mss, source_to_string (route->source)); - return to_string_buffer; + return _nm_platform_to_string_buffer; } #define _CMP_POINTER(a, b) \ @@ -2597,6 +2695,12 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return (((a)->field) < ((b)->field)) ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_BOOL(a, b, field) \ + G_STMT_START { \ + if ((!((a)->field)) != (!((b)->field))) \ + return ((!((a)->field)) < (!((b)->field))) ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD_STR(a, b, field) \ G_STMT_START { \ int c = strcmp ((a)->field, (b)->field); \ @@ -2604,6 +2708,16 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return c < 0 ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_STR_INTERNED(a, b, field) \ + G_STMT_START { \ + if (((a)->field) != ((b)->field)) { \ + /* just to be sure, also do a strcmp() if the pointers don't match */ \ + int c = g_strcmp0 ((a)->field, (b)->field); \ + if (c != 0) \ + return c < 0 ? -1 : 1; \ + } \ + } G_STMT_END + #define _CMP_FIELD_STR0(a, b, field) \ G_STMT_START { \ int c = g_strcmp0 ((a)->field, (b)->field); \ @@ -2611,6 +2725,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), \ @@ -2623,17 +2745,26 @@ int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) { _CMP_POINTER (a, b); + _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, type); _CMP_FIELD_STR (a, b, name); _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, 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_STR0 (a, b, type_name); - _CMP_FIELD_STR0 (a, b, udi); - _CMP_FIELD_STR0 (a, b, driver); + _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_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; } @@ -2680,6 +2811,8 @@ 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); + _CMP_FIELD (a, b, pref_src); return 0; } @@ -2753,8 +2886,8 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor #undef _CMP_POINTER -static const char * -_change_type_to_string (NMPlatformSignalChangeType change_type) +const char * +nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type) { switch (change_type) { case NM_PLATFORM_SIGNAL_ADDED: @@ -2769,76 +2902,80 @@ _change_type_to_string (NMPlatformSignalChangeType change_type) } static void -log_link (NMPlatform *p, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) +log_link (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: link %7s: %s", _change_type_to_string (change_type), nm_platform_link_to_string (device)); + _LOGD ("signal: link %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_link_to_string (device)); } static void -log_ip4_address (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip4_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: address 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_address_to_string (address)); + _LOGD ("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_address_to_string (address)); } static void -log_ip6_address (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip6_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: address 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_address_to_string (address)); + _LOGD ("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_address_to_string (address)); } static void -log_ip4_route (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip4_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: route 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_route_to_string (route)); + _LOGD ("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_route_to_string (route)); } static void -log_ip6_route (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: route 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_route_to_string (route)); + _LOGD ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route)); } /******************************************************************/ static gboolean -_vtr_v4_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +_vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) { - return nm_platform_ip4_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip4_route_add (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->rx.source, route->r4.network, route->rx.plen, route->r4.gateway, - v4_pref_src, - route->rx.metric, + route->r4.pref_src, + metric >= 0 ? (guint32) metric : route->rx.metric, route->rx.mss); } static gboolean -_vtr_v6_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +_vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) { - return nm_platform_ip6_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip6_route_add (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->rx.source, route->r6.network, route->rx.plen, route->r6.gateway, - route->rx.metric, + metric >= 0 ? (guint32) metric : route->rx.metric, route->rx.mss); } static gboolean -_vtr_v4_route_delete (int ifindex, const NMPlatformIPXRoute *route) +_vtr_v4_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) { - return nm_platform_ip4_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip4_route_delete (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric); } static gboolean -_vtr_v6_route_delete (int ifindex, const NMPlatformIPXRoute *route) +_vtr_v6_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) { - return nm_platform_ip6_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip6_route_delete (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->r6.network, route->rx.plen, route->rx.metric); @@ -2851,15 +2988,15 @@ _vtr_v4_metric_normalize (guint32 metric) } static gboolean -_vtr_v4_route_delete_default (int ifindex, guint32 metric) +_vtr_v4_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) { - return nm_platform_ip4_route_delete (ifindex, 0, 0, metric); + return nm_platform_ip4_route_delete (self, ifindex, 0, 0, metric); } static gboolean -_vtr_v6_route_delete_default (int ifindex, guint32 metric) +_vtr_v6_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) { - return nm_platform_ip6_route_delete (ifindex, in6addr_any, 0, metric); + return nm_platform_ip6_route_delete (self, ifindex, in6addr_any, 0, metric); } /******************************************************************/ @@ -2893,6 +3030,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) { } @@ -2903,13 +3069,26 @@ nm_platform_init (NMPlatform *object) G_SIGNAL_RUN_FIRST, \ G_CALLBACK (method), \ NULL, NULL, NULL, \ - G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE, NM_TYPE_PLATFORM_REASON); + G_TYPE_NONE, 5, NM_TYPE_POBJECT_TYPE, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE, NM_TYPE_PLATFORM_REASON); static void 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; + + 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 a49e0f11..4d254195 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,12 @@ /******************************************************************/ +#define NM_PLATFORM_REGISTER_SINGLETON "register-singleton" + +/******************************************************************/ + +typedef struct _NMPlatform NMPlatform; + /* workaround for older libnl version, that does not define these flags. */ #ifndef IFA_F_MANAGETEMPADDR #define IFA_F_MANAGETEMPADDR 0x100 @@ -47,19 +54,24 @@ #define IFA_F_NOPREFIXROUTE 0x200 #endif -typedef enum { - /* no error specified, sometimes this means the arguments were wrong */ - NM_PLATFORM_ERROR_NONE, - /* object was not found */ +typedef enum { /*< skip >*/ + + /* 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 { @@ -83,18 +95,54 @@ struct _NMPlatformLink { __NMPlatformObject_COMMON; char name[IFNAMSIZ]; NMLinkType type; - const char *type_name; - const char *udi; + + /* rtnl_link_get_type(), IFLA_INFO_KIND. */ + /* NMPlatform initializes this field with a static string. */ + const char *kind; + + /* 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; }; typedef enum { + NM_PLATFORM_SIGNAL_NONE, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, @@ -102,11 +150,17 @@ typedef enum { #define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32 -typedef enum { - NM_PLATFORM_GET_ROUTE_MODE_ALL, - NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, - NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT, -} NMPlatformGetRouteMode; +typedef enum { /*< skip >*/ + NM_PLATFORM_GET_ROUTE_FLAGS_NONE = 0, + + /* Whether to include default-routes/non-default-routes. Omitting + * both WITH_DEFAULT and WITH_NON_DEFAULT, is equal to specifying + * both of them. */ + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT = (1LL << 0), + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT = (1LL << 1), + + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL = (1LL << 2), +} NMPlatformGetRouteFlags; typedef struct { __NMPlatformObject_COMMON; @@ -224,6 +278,14 @@ struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; in_addr_t network; in_addr_t gateway; + + /* The bitwise inverse of the route scope. It is inverted so that the + * default value (RT_SCOPE_NOWHERE) is nul. */ + guint8 scope_inv; + + /* RTA_PREFSRC/rtnl_route_get_pref_src(). A value of zero means that + * no pref-src is set. */ + guint32 pref_src; }; G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); @@ -252,16 +314,17 @@ typedef struct { gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b); const char *(*route_to_string) (const NMPlatformIPXRoute *route); - GArray *(*route_get_all) (int ifindex, NMPlatformGetRouteMode mode); - gboolean (*route_add) (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src); - gboolean (*route_delete) (int ifindex, const NMPlatformIPXRoute *route); - gboolean (*route_delete_default) (int ifindex, guint32 metric); + GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); + gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); + gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); + gboolean (*route_delete_default) (NMPlatform *self, int ifindex, guint32 metric); guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; 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; @@ -346,68 +409,81 @@ typedef struct { * network configuration daemons stopped. Look at the code first. */ -typedef struct { +struct _NMPlatform { GObject parent; - - NMPlatformError error; -} NMPlatform; +}; typedef struct { GObjectClass parent; - gboolean (*setup) (NMPlatform *); - gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *path); - gboolean (*link_get) (NMPlatform *platform, int ifindex, NMPlatformLink *link); + const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); + const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); + const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); + GArray *(*link_get_all) (NMPlatform *); - gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, const void *address, size_t address_len); + gboolean (*link_add) (NMPlatform *, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link); gboolean (*link_delete) (NMPlatform *, int ifindex); - int (*link_get_ifindex) (NMPlatform *, const char *name); - const char *(*link_get_name) (NMPlatform *, int ifindex); - NMLinkType (*link_get_type) (NMPlatform *, int ifindex); const char *(*link_get_type_name) (NMPlatform *, int ifindex); + gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged); gboolean (*link_refresh) (NMPlatform *, int ifindex); + void (*process_events) (NMPlatform *self); - 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); - gboolean (*link_is_up) (NMPlatform *, int ifindex); - gboolean (*link_is_connected) (NMPlatform *, int ifindex); - gboolean (*link_uses_arp) (NMPlatform *, int ifindex); - gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex); + const char *(*link_get_udi) (NMPlatform *self, int ifindex); + GObject *(*link_get_udev_device) (NMPlatform *self, int ifindex); + gboolean (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); - gconstpointer (*link_get_address) (NMPlatform *, int ifindex, size_t *length); + gboolean (*link_get_permanent_address) (NMPlatform *, + int ifindex, + guint8 *buf, + size_t *length); gboolean (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); - guint32 (*link_get_mtu) (NMPlatform *, int ifindex); gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); guint (*link_get_dev_id) (NMPlatform *, int ifindex); gboolean (*link_get_wake_on_lan) (NMPlatform *, int ifindex); + gboolean (*link_get_driver_info) (NMPlatform *, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version); gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex); gboolean (*link_supports_vlans) (NMPlatform *, int ifindex); gboolean (*link_enslave) (NMPlatform *, int master, int slave); gboolean (*link_release) (NMPlatform *, int master, int slave); - gboolean (*link_get_master) (NMPlatform *, int slave); gboolean (*master_set_option) (NMPlatform *, int ifindex, const char *option, const char *value); char * (*master_get_option) (NMPlatform *, int ifindex, const char *option); gboolean (*slave_set_option) (NMPlatform *, int ifindex, const char *option, const char *value); char * (*slave_get_option) (NMPlatform *, int ifindex, const char *option); - gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags); + gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); gboolean (*vlan_get_info) (NMPlatform *, int ifindex, int *parent, int *vlan_id); gboolean (*vlan_set_ingress_map) (NMPlatform *, int ifindex, int from, int to); gboolean (*vlan_set_egress_map) (NMPlatform *, int ifindex, int from, int to); - gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key); + gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, NMPlatformLink *out_link); + gboolean (*infiniband_get_info) (NMPlatform *, + int ifindex, + int *parent, + int *p_key, + const char **mode); gboolean (*veth_get_properties) (NMPlatform *, int ifindex, NMPlatformVethProperties *properties); gboolean (*tun_get_properties) (NMPlatform *, int ifindex, NMPlatformTunProperties *properties); @@ -441,13 +517,11 @@ typedef struct { guint32 lifetime, guint32 preferred_lft, guint flags); gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, int plen); - gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen); - gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen); + const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, int plen); + const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address, int plen); - gboolean (*ip4_check_reinstall_device_route) (NMPlatform *, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); - - GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteMode mode); - GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteMode mode); + GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); + GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss); @@ -456,8 +530,8 @@ typedef struct { guint32 metric, guint32 mss); gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, int plen, guint32 metric); gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, int plen, guint32 metric); - gboolean (*ip4_route_exists) (NMPlatform *, int ifindex, in_addr_t network, int plen, guint32 metric); - gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, struct in6_addr network, int plen, guint32 metric); + const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, int plen, guint32 metric); + const NMPlatformIP6Route *(*ip6_route_get) (NMPlatform *, int ifindex, struct in6_addr network, int plen, guint32 metric); gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); gboolean (*check_support_user_ipv6ll) (NMPlatform *); @@ -480,139 +554,172 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" +const char *nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type); + /******************************************************************/ GType nm_platform_get_type (void); -void nm_platform_setup (GType type); +void nm_platform_setup (NMPlatform *instance); NMPlatform *nm_platform_get (void); -void nm_platform_free (void); +NMPlatform *nm_platform_try_get (void); + +#define NM_PLATFORM_GET (nm_platform_get ()) /******************************************************************/ -void nm_platform_set_error (NMPlatformError error); -NMPlatformError nm_platform_get_error (void); -const char *nm_platform_get_error_msg (void); - -void nm_platform_query_devices (void); - -gboolean nm_platform_sysctl_set (const char *path, const char *value); -char *nm_platform_sysctl_get (const char *path); -gint32 nm_platform_sysctl_get_int32 (const char *path, gint32 fallback); -gint64 nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback); - -gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value); - -gboolean nm_platform_link_get (int ifindex, NMPlatformLink *link); -GArray *nm_platform_link_get_all (void); -gboolean nm_platform_dummy_add (const char *name); -gboolean nm_platform_bridge_add (const char *name, const void *address, size_t address_len); -gboolean nm_platform_bond_add (const char *name); -gboolean nm_platform_team_add (const char *name); -gboolean nm_platform_link_exists (const char *name); -gboolean nm_platform_link_delete (int ifindex); -int nm_platform_link_get_ifindex (const char *name); -const char *nm_platform_link_get_name (int ifindex); -NMLinkType nm_platform_link_get_type (int ifindex); -const char *nm_platform_link_get_type_name (int ifindex); -gboolean nm_platform_link_is_software (int ifindex); -gboolean nm_platform_link_supports_slaves (int ifindex); - -gboolean nm_platform_link_refresh (int ifindex); - -gboolean nm_platform_link_set_up (int ifindex); -gboolean nm_platform_link_set_down (int ifindex); -gboolean nm_platform_link_set_arp (int ifindex); -gboolean nm_platform_link_set_noarp (int ifindex); -gboolean nm_platform_link_is_up (int ifindex); -gboolean nm_platform_link_is_connected (int ifindex); -gboolean nm_platform_link_uses_arp (int ifindex); - -gboolean nm_platform_link_get_user_ipv6ll_enabled (int ifindex); -gboolean nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled); - -gconstpointer nm_platform_link_get_address (int ifindex, size_t *length); -gboolean nm_platform_link_set_address (int ifindex, const void *address, size_t length); -guint32 nm_platform_link_get_mtu (int ifindex); -gboolean nm_platform_link_set_mtu (int ifindex, guint32 mtu); - -char *nm_platform_link_get_physical_port_id (int ifindex); -guint nm_platform_link_get_dev_id (int ifindex); -gboolean nm_platform_link_get_wake_on_lan (int ifindex); - -gboolean nm_platform_link_supports_carrier_detect (int ifindex); -gboolean nm_platform_link_supports_vlans (int ifindex); - -gboolean nm_platform_link_enslave (int master, int slave); -gboolean nm_platform_link_release (int master, int slave); -int nm_platform_link_get_master (int slave); -gboolean nm_platform_master_set_option (int ifindex, const char *option, const char *value); -char *nm_platform_master_get_option (int ifindex, const char *option); -gboolean nm_platform_slave_set_option (int ifindex, const char *option, const char *value); -char *nm_platform_slave_get_option (int ifindex, const char *option); - -gboolean nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags); -gboolean nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid); -gboolean nm_platform_vlan_set_ingress_map (int ifindex, int from, int to); -gboolean nm_platform_vlan_set_egress_map (int ifindex, int from, int to); - -gboolean nm_platform_infiniband_partition_add (int parent, int p_key); - -gboolean nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *properties); -gboolean nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *properties); -gboolean nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props); -gboolean nm_platform_vxlan_get_properties (int ifindex, NMPlatformVxlanProperties *props); -gboolean nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props); - -gboolean nm_platform_wifi_get_capabilities (int ifindex, NMDeviceWifiCapabilities *caps); -gboolean nm_platform_wifi_get_bssid (int ifindex, guint8 *bssid); -GByteArray *nm_platform_wifi_get_ssid (int ifindex); -guint32 nm_platform_wifi_get_frequency (int ifindex); -int nm_platform_wifi_get_quality (int ifindex); -guint32 nm_platform_wifi_get_rate (int ifindex); -NM80211Mode nm_platform_wifi_get_mode (int ifindex); -void nm_platform_wifi_set_mode (int ifindex, NM80211Mode mode); -guint32 nm_platform_wifi_find_frequency (int ifindex, const guint32 *freqs); -void nm_platform_wifi_indicate_addressing_running (int ifindex, gboolean running); - -guint32 nm_platform_mesh_get_channel (int ifindex); -gboolean nm_platform_mesh_set_channel (int ifindex, guint32 channel); -gboolean nm_platform_mesh_set_ssid (int ifindex, const guint8 *ssid, gsize len); - -GArray *nm_platform_ip4_address_get_all (int ifindex); -GArray *nm_platform_ip6_address_get_all (int ifindex); -gboolean nm_platform_ip4_address_add (int ifindex, +/** + * 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. + * */ +#define nm_platform_route_scope_inv _nm_platform_uint8_inv +static inline guint8 +_nm_platform_uint8_inv (guint8 scope) +{ + return (guint8) ~scope; +} + +const char *nm_link_type_to_string (NMLinkType link_type); + +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); +gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback); +gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint base, gint64 min, gint64 max, gint64 fallback); + +gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value); + +const NMPlatformLink *nm_platform_link_get (NMPlatform *self, int ifindex); +const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname); +const NMPlatformLink *nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length); + +GArray *nm_platform_link_get_all (NMPlatform *self); +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_delete (NMPlatform *self, int ifindex); + +/* convienience methods to lookup the link and access fields of NMPlatformLink. */ +int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); +const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); +NMLinkType nm_platform_link_get_type (NMPlatform *self, int ifindex); +gboolean nm_platform_link_is_software (NMPlatform *self, int ifindex); +gboolean nm_platform_link_is_up (NMPlatform *self, int ifindex); +gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex); +gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex); +guint32 nm_platform_link_get_mtu (NMPlatform *self, int ifindex); +gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid); +gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex); +gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length); +int nm_platform_link_get_master (NMPlatform *self, int slave); + +gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged); +gboolean nm_platform_link_supports_slaves (NMPlatform *self, int ifindex); +const char *nm_platform_link_get_type_name (NMPlatform *self, int ifindex); + +gboolean nm_platform_link_refresh (NMPlatform *self, int ifindex); +void nm_platform_process_events (NMPlatform *self); + +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); + +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_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled); + +gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); +gboolean nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); +gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); + +char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); +guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); +gboolean nm_platform_link_get_wake_on_lan (NMPlatform *self, int ifindex); +gboolean nm_platform_link_get_driver_info (NMPlatform *self, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version); + +gboolean nm_platform_link_supports_carrier_detect (NMPlatform *self, int ifindex); +gboolean nm_platform_link_supports_vlans (NMPlatform *self, int ifindex); + +gboolean nm_platform_link_enslave (NMPlatform *self, int master, int slave); +gboolean nm_platform_link_release (NMPlatform *self, int master, int slave); +gboolean nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); +char *nm_platform_master_get_option (NMPlatform *self, int ifindex, const char *option); +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); + +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); + +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); +gboolean nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *properties); +gboolean nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props); +gboolean nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props); +gboolean nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props); + +gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); +gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid); +GByteArray *nm_platform_wifi_get_ssid (NMPlatform *self, int ifindex); +guint32 nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex); +int nm_platform_wifi_get_quality (NMPlatform *self, int ifindex); +guint32 nm_platform_wifi_get_rate (NMPlatform *self, int ifindex); +NM80211Mode nm_platform_wifi_get_mode (NMPlatform *self, int ifindex); +void nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode); +guint32 nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs); +void nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running); + +guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex); +gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel); +gboolean nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); + +const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen); +const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen); +GArray *nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex); +GArray *nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex); +gboolean nm_platform_ip4_address_add (NMPlatform *self, int ifindex, in_addr_t address, in_addr_t peer_address, int plen, guint32 lifetime, guint32 preferred_lft, const char *label); -gboolean nm_platform_ip6_address_add (int ifindex, +gboolean nm_platform_ip6_address_add (NMPlatform *self, int ifindex, struct in6_addr address, struct in6_addr peer_address, int plen, guint32 lifetime, guint32 preferred_lft, guint flags); -gboolean nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen, in_addr_t peer_address); -gboolean nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen); -gboolean nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen); -gboolean nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen); -gboolean nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint32 device_route_metric); -gboolean nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboolean keep_link_local); -gboolean nm_platform_address_flush (int ifindex); - -gboolean nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); - -GArray *nm_platform_ip4_route_get_all (int ifindex, NMPlatformGetRouteMode mode); -GArray *nm_platform_ip6_route_get_all (int ifindex, NMPlatformGetRouteMode mode); -gboolean nm_platform_ip4_route_add (int ifindex, NMIPConfigSource source, +gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); +gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, int plen); +gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses); +gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local); +gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); + +const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric); +const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric); +GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); +GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); +gboolean nm_platform_ip4_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss); -gboolean nm_platform_ip6_route_add (int ifindex, NMIPConfigSource source, +gboolean nm_platform_ip6_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss); -gboolean nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, guint32 metric); -gboolean nm_platform_ip6_route_delete (int ifindex, struct in6_addr network, int plen, guint32 metric); -gboolean nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, guint32 metric); -gboolean nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, guint32 metric); -gboolean nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes); -gboolean nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes); -gboolean nm_platform_route_flush (int ifindex); +gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric); +gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric); const char *nm_platform_link_to_string (const NMPlatformLink *link); const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address); @@ -627,8 +734,8 @@ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4R int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b); gboolean nm_platform_check_support_libnl_extended_ifa_flags (void); -gboolean nm_platform_check_support_kernel_extended_ifa_flags (void); -gboolean nm_platform_check_support_user_ipv6ll (void); +gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self); +gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self); void nm_platform_addr_flags2str (int flags, char *buf, size_t size); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c new file mode 100644 index 00000000..51684b93 --- /dev/null +++ b/src/platform/nmp-object.c @@ -0,0 +1,1924 @@ +/* -*- 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) == NMP_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) == NMP_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 (NMPObjectType obj_type) +{ + g_return_val_if_fail (obj_type > NMP_OBJECT_TYPE_UNKNOWN && obj_type <= NMP_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 (NMPObjectType 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 (NMP_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, NMPObjectType 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, NMP_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, NMP_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, NMP_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, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + obj->ip4_route.ifindex = ifindex; + obj->ip4_route.network = network; + obj->ip4_route.plen = plen; + obj->ip4_route.metric = metric; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_ip4_route (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_ip4_route (obj, src->ip_route.ifindex, src->ip4_route.network, src->ip_route.plen, src->ip_route.metric); +} + +const NMPObject * +nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, int plen, guint32 metric) +{ + nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip6_route.ifindex = ifindex; + if (network) + obj->ip6_route.network = *network; + obj->ip6_route.plen = plen; + obj->ip6_route.metric = metric; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_ip6_route (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_ip6_route (obj, src->ip_route.ifindex, &src->ip6_route.network, src->ip_route.plen, src->ip_route.metric); +} + +/******************************************************************/ + +const char * +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) == NMP_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) 3982791431u; + 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) 3591309853u; + 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) 2907861637u; + 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) 2569857221u; + 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) 3999787007u; + 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 + && obj->link.name[0]; +} + +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_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) +{ + NMPObjectType obj_type = _nlo_get_object_type (nlo); + NMPObject *obj; + + if (obj_type == NMP_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; + +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, NMPObjectType obj_type, gboolean visible_only) +{ + _nmp_cache_id_init (id, visible_only + ? NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY + : NMP_CACHE_ID_TYPE_OBJECT_TYPE); + id->object_type.obj_type = obj_type; + return id; +} + +NMPCacheId * +nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, + NMPObjectType obj_type, + int ifindex) +{ + g_return_val_if_fail (NM_IN_SET (obj_type, + NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); + + if (ifindex <= 0) + return nmp_cache_id_init_object_type (id, obj_type, TRUE); + + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX); + id->object_type_by_ifindex.obj_type = obj_type; + id->object_type_by_ifindex.ifindex = ifindex; + return id; +} + +NMPCacheId * +nmp_cache_id_init_routes_visible (NMPCacheId *id, + NMPObjectType obj_type, + gboolean with_default, + gboolean with_non_default, + int ifindex) +{ + g_return_val_if_fail (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); + + if (with_default && with_non_default) { + if (ifindex <= 0) + return nmp_cache_id_init_object_type (id, obj_type, TRUE); + return nmp_cache_id_init_addrroute_visible_by_ifindex (id, obj_type, ifindex); + } + + if (with_default) + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT); + else if (with_non_default) + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT); + else + g_return_val_if_reached (NULL); + + id->object_type_by_ifindex.obj_type = obj_type; + id->object_type_by_ifindex.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); + + switch (id_type) { + case NMP_CACHE_ID_TYPE_OBJECT_TYPE: + *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, FALSE); + return TRUE; + case NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY: + if (nmp_object_is_visible (obj)) + *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, TRUE); + else + *out_id = NULL; + return TRUE; + default: + return klass->cmd_obj_init_cache_id (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) +{ + return FALSE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_address (obj)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_route (obj)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex); + return TRUE; + } + break; + 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) != NMP_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) != NMP_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_object_type (NMP_CACHE_ID_STATIC, NMP_OBJECT_TYPE_LINK, 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)) == NMP_OBJECT_TYPE_LINK); + + if ( (!potential_slave || potential_slave->link.ifindex != link->ifindex) + && ignore_slave != obj + && link->ifindex > 0 + && link->master == master->link.ifindex + && nmp_object_is_visible (obj) + && link->connected) { + 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, NMPObjectType 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, NMP_OBJECT_TYPE_LINK, visible_only), &len); + for (i = 0; i < len; i++) { + obj = NMP_OBJECT_UP_CAST (list[i]); + + 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 GLIB_CHECK_VERSION(2, 40, 0) + if (!g_hash_table_add (cache->idx_main, obj)) + g_assert_not_reached (); +#else + g_hash_table_add (cache->idx_main, obj); +#endif + 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) == NMP_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) != NMP_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) == NMP_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) == NMP_OBJECT_TYPE_LINK) { + if (!obj->_link.netlink.is_in_netlink) { + if (!old->_link.netlink.is_in_netlink) { + nm_assert (old->_link.udev.device); + return NMP_CACHE_OPS_UNCHANGED; + } + if (old->_link.udev.device) { + /* @obj is not in netlink. + * + * This is similar to nmp_cache_remove_netlink(), but there we preserve the + * preexisting netlink properties. The use case of that is when kernel_get_object() + * cannot load an object (based on the id of a needle). + * + * Here we keep the data provided from @obj. The usecase is when receiving + * a valid @obj instance from netlink with RTM_DELROUTE. + */ + is_alive = TRUE; + } + } else + is_alive = TRUE; + + if (is_alive) { + _nmp_object_fixup_link_master_connected (obj, cache); + + /* 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 (NMP_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[NMP_OBJECT_TYPE_MAX] = { + [NMP_OBJECT_TYPE_LINK - 1] = { + .obj_type = NMP_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, + }, + [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { + .obj_type = NMP_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_ipx_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, + }, + [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = { + .obj_type = NMP_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_ipx_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 + }, + [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { + .obj_type = NMP_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_ipx_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, + }, + [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { + .obj_type = NMP_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_ipx_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 00000000..5b1887b8 --- /dev/null +++ b/src/platform/nmp-object.h @@ -0,0 +1,361 @@ +/* -*- 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 >*/ + 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 can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT. + * + * Of one index type, there can be multiple indexes or not. + * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX there + * are multiple instances (for different route/addresses, v4/v6, per-ifindex). + * + * But one object, can only be indexed by one particular index of a + * type. For example, a certain address instance is only indexed by + * the index NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX with + * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible. + * */ +typedef enum { /*< skip >*/ + /* all the objects of a certain type */ + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + + /* all the visible objects of a certain type */ + NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, + + /* indeces for the visible routes, ignoring ifindex. */ + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, + + /* all the visible addresses/routes (by object-type) for an ifindex. */ + NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, + + /* three indeces for the visible routes, per ifindex. */ + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, + + __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 */ + /* NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */ + guint8 _id_type; + guint8 obj_type; /* NMPObjectType as guint8 */ + } object_type; + struct { + /* NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT */ + guint8 _id_type; + guint8 obj_type; /* NMPObjectType as guint8 */ + int ifindex; + } object_type_by_ifindex; + }; +} NMPCacheId; + +extern NMPCacheId _nmp_cache_id_static; +#define NMP_CACHE_ID_STATIC (&_nmp_cache_id_static) + +typedef struct { + NMPObjectType 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[NMP_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 NMPObjectType +NMP_OBJECT_GET_TYPE (const NMPObject *obj) +{ + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + + return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; +} + + + +const NMPClass *nmp_class_from_type (NMPObjectType obj_type); + +NMPObject *nmp_object_ref (NMPObject *object); +void nmp_object_unref (NMPObject *object); +NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); +NMPObject *nmp_object_new_link (int ifindex); + +const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj); +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_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only); +NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex); +NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex); + +const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len); +GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id); +const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj); +const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex); + +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. */ +NMPObjectType _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/Makefile.am b/src/platform/tests/Makefile.am index c10600eb..54844ec1 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -37,6 +37,8 @@ noinst_PROGRAMS = \ test-link-linux \ test-address-fake \ test-address-linux \ + test-general \ + test-nmp-object \ test-route-fake \ test-route-linux \ test-cleanup-fake \ @@ -109,7 +111,27 @@ test_cleanup_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_cleanup_linux_LDADD = $(PLATFORM_LDADD) -@VALGRIND_RULES@ -TESTS = test-link-fake test-address-fake test-route-fake test-cleanup-fake test-address-linux test-route-linux test-cleanup-linux +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 = \ + $(top_builddir)/src/libNetworkManager.la + +@VALGRIND_RULES@ +TESTS = \ + test-address-fake \ + test-address-linux \ + test-cleanup-fake \ + test-cleanup-linux \ + test-general \ + test-link-fake \ + test-link-linux \ + test-nmp-object \ + test-route-fake \ + test-route-linux diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in index 16a48430..a9333539 100644 --- a/src/platform/tests/Makefile.in +++ b/src/platform/tests/Makefile.in @@ -92,12 +92,14 @@ host_triplet = @host@ noinst_PROGRAMS = dump$(EXEEXT) monitor$(EXEEXT) platform$(EXEEXT) \ test-link-fake$(EXEEXT) test-link-linux$(EXEEXT) \ test-address-fake$(EXEEXT) test-address-linux$(EXEEXT) \ + test-general$(EXEEXT) test-nmp-object$(EXEEXT) \ test-route-fake$(EXEEXT) test-route-linux$(EXEEXT) \ test-cleanup-fake$(EXEEXT) test-cleanup-linux$(EXEEXT) -TESTS = test-link-fake$(EXEEXT) test-address-fake$(EXEEXT) \ - test-route-fake$(EXEEXT) test-cleanup-fake$(EXEEXT) \ - test-address-linux$(EXEEXT) test-route-linux$(EXEEXT) \ - test-cleanup-linux$(EXEEXT) +TESTS = test-address-fake$(EXEEXT) test-address-linux$(EXEEXT) \ + test-cleanup-fake$(EXEEXT) test-cleanup-linux$(EXEEXT) \ + test-general$(EXEEXT) test-link-fake$(EXEEXT) \ + test-link-linux$(EXEEXT) test-nmp-object$(EXEEXT) \ + test-route-fake$(EXEEXT) test-route-linux$(EXEEXT) subdir = src/platform/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ @@ -172,6 +174,9 @@ am_test_cleanup_linux_OBJECTS = \ test_cleanup_linux-test-cleanup.$(OBJEXT) $(am__objects_9) test_cleanup_linux_OBJECTS = $(am_test_cleanup_linux_OBJECTS) test_cleanup_linux_DEPENDENCIES = $(PLATFORM_LDADD) +am_test_general_OBJECTS = test-general.$(OBJEXT) +test_general_OBJECTS = $(am_test_general_OBJECTS) +test_general_DEPENDENCIES = $(top_builddir)/src/libNetworkManager.la am__objects_10 = test_link_fake-nm-platform.$(OBJEXT) \ test_link_fake-nm-fake-platform.$(OBJEXT) \ test_link_fake-nm-linux-platform.$(OBJEXT) @@ -190,6 +195,10 @@ am_test_link_linux_OBJECTS = test_link_linux-test-link.$(OBJEXT) \ $(am__objects_13) test_link_linux_OBJECTS = $(am_test_link_linux_OBJECTS) test_link_linux_DEPENDENCIES = $(PLATFORM_LDADD) +am_test_nmp_object_OBJECTS = test-nmp-object.$(OBJEXT) +test_nmp_object_OBJECTS = $(am_test_nmp_object_OBJECTS) +test_nmp_object_DEPENDENCIES = \ + $(top_builddir)/src/libNetworkManager.la am__objects_14 = test_route_fake-nm-platform.$(OBJEXT) \ test_route_fake-nm-fake-platform.$(OBJEXT) \ test_route_fake-nm-linux-platform.$(OBJEXT) @@ -245,12 +254,14 @@ am__v_CCLD_1 = SOURCES = $(dump_SOURCES) $(monitor_SOURCES) $(platform_SOURCES) \ $(test_address_fake_SOURCES) $(test_address_linux_SOURCES) \ $(test_cleanup_fake_SOURCES) $(test_cleanup_linux_SOURCES) \ - $(test_link_fake_SOURCES) $(test_link_linux_SOURCES) \ + $(test_general_SOURCES) $(test_link_fake_SOURCES) \ + $(test_link_linux_SOURCES) $(test_nmp_object_SOURCES) \ $(test_route_fake_SOURCES) $(test_route_linux_SOURCES) DIST_SOURCES = $(dump_SOURCES) $(monitor_SOURCES) $(platform_SOURCES) \ $(test_address_fake_SOURCES) $(test_address_linux_SOURCES) \ $(test_cleanup_fake_SOURCES) $(test_cleanup_linux_SOURCES) \ - $(test_link_fake_SOURCES) $(test_link_linux_SOURCES) \ + $(test_general_SOURCES) $(test_link_fake_SOURCES) \ + $(test_link_linux_SOURCES) $(test_nmp_object_SOURCES) \ $(test_route_fake_SOURCES) $(test_route_linux_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -298,13 +309,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -418,6 +612,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -642,10 +837,22 @@ 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 = \ + $(top_builddir)/src/libNetworkManager.la + all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -713,6 +920,10 @@ test-cleanup-linux$(EXEEXT): $(test_cleanup_linux_OBJECTS) $(test_cleanup_linux_ @rm -f test-cleanup-linux$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_cleanup_linux_OBJECTS) $(test_cleanup_linux_LDADD) $(LIBS) +test-general$(EXEEXT): $(test_general_OBJECTS) $(test_general_DEPENDENCIES) $(EXTRA_test_general_DEPENDENCIES) + @rm -f test-general$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_general_OBJECTS) $(test_general_LDADD) $(LIBS) + test-link-fake$(EXEEXT): $(test_link_fake_OBJECTS) $(test_link_fake_DEPENDENCIES) $(EXTRA_test_link_fake_DEPENDENCIES) @rm -f test-link-fake$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_link_fake_OBJECTS) $(test_link_fake_LDADD) $(LIBS) @@ -721,6 +932,10 @@ test-link-linux$(EXEEXT): $(test_link_linux_OBJECTS) $(test_link_linux_DEPENDENC @rm -f test-link-linux$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_link_linux_OBJECTS) $(test_link_linux_LDADD) $(LIBS) +test-nmp-object$(EXEEXT): $(test_nmp_object_OBJECTS) $(test_nmp_object_DEPENDENCIES) $(EXTRA_test_nmp_object_DEPENDENCIES) + @rm -f test-nmp-object$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_nmp_object_OBJECTS) $(test_nmp_object_LDADD) $(LIBS) + test-route-fake$(EXEEXT): $(test_route_fake_OBJECTS) $(test_route_fake_DEPENDENCIES) $(EXTRA_test_route_fake_DEPENDENCIES) @rm -f test-route-fake$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_route_fake_OBJECTS) $(test_route_fake_LDADD) $(LIBS) @@ -741,6 +956,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-linux-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/platform.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-general.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-nmp-object.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-fake-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-linux-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-platform.Po@am__quote@ @@ -1463,98 +1680,231 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-address-fake.log: test-address-fake$(EXEEXT) + @p='test-address-fake$(EXEEXT)'; \ + b='test-address-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-address-linux.log: test-address-linux$(EXEEXT) + @p='test-address-linux$(EXEEXT)'; \ + b='test-address-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-cleanup-fake.log: test-cleanup-fake$(EXEEXT) + @p='test-cleanup-fake$(EXEEXT)'; \ + b='test-cleanup-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-cleanup-linux.log: test-cleanup-linux$(EXEEXT) + @p='test-cleanup-linux$(EXEEXT)'; \ + b='test-cleanup-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-general.log: test-general$(EXEEXT) + @p='test-general$(EXEEXT)'; \ + b='test-general'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-link-fake.log: test-link-fake$(EXEEXT) + @p='test-link-fake$(EXEEXT)'; \ + b='test-link-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-link-linux.log: test-link-linux$(EXEEXT) + @p='test-link-linux$(EXEEXT)'; \ + b='test-link-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-nmp-object.log: test-nmp-object$(EXEEXT) + @p='test-nmp-object$(EXEEXT)'; \ + b='test-nmp-object'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-route-fake.log: test-route-fake$(EXEEXT) + @p='test-route-fake$(EXEEXT)'; \ + b='test-route-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-route-linux.log: test-route-linux$(EXEEXT) + @p='test-route-linux$(EXEEXT)'; \ + b='test-route-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -1611,6 +1961,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -1706,7 +2059,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index 3bb61da4..54de1da7 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, link->type_name); - if (link->up) + printf ("%d: %s: %s", link->ifindex, link->name, nm_link_type_to_string (link->type)); + 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,22 +44,22 @@ dump_interface (NMPlatformLink *link) printf ("\n"); if (link->driver) printf (" driver: %s\n", link->driver); - printf (" UDI: %s\n", link->udi); - if (!nm_platform_vlan_get_info (link->ifindex, &vlan_parent, &vlan_id)) + 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) printf (" vlan parent %d id %d\n", vlan_parent, vlan_id); - if (nm_platform_link_is_software (link->ifindex)) + if (nm_platform_link_is_software (NM_PLATFORM_GET, link->ifindex)) printf (" class software\n"); - if (nm_platform_link_supports_slaves (link->ifindex)) + if (nm_platform_link_supports_slaves (NM_PLATFORM_GET, link->ifindex)) printf (" class supports-slaves\n"); - if (nm_platform_link_supports_carrier_detect (link->ifindex)) + if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, link->ifindex)) printf (" feature carrier-detect\n"); - if (nm_platform_link_supports_vlans (link->ifindex)) + if (nm_platform_link_supports_vlans (NM_PLATFORM_GET, link->ifindex)) printf (" feature vlans\n"); - address = nm_platform_link_get_address (link->ifindex, &addrlen); + address = nm_platform_link_get_address (NM_PLATFORM_GET, link->ifindex, &addrlen); if (address) { printf (" link-address "); for (i = 0; i < addrlen; i++) @@ -66,8 +67,8 @@ dump_interface (NMPlatformLink *link) printf ("\n"); } - ip4_addresses = nm_platform_ip4_address_get_all (link->ifindex); - ip6_addresses = nm_platform_ip6_address_get_all (link->ifindex); + ip4_addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, link->ifindex); + ip6_addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, link->ifindex); g_assert (ip4_addresses); g_assert (ip6_addresses); @@ -85,8 +86,8 @@ dump_interface (NMPlatformLink *link) g_array_unref (ip4_addresses); g_array_unref (ip6_addresses); - ip4_routes = nm_platform_ip4_route_get_all (link->ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); - ip6_routes = nm_platform_ip6_route_get_all (link->ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + ip4_routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, link->ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + ip6_routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, link->ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); g_assert (ip4_routes); g_assert (ip6_routes); @@ -112,7 +113,7 @@ dump_interface (NMPlatformLink *link) static void dump_all (void) { - GArray *links = nm_platform_link_get_all (); + GArray *links = nm_platform_link_get_all (NM_PLATFORM_GET); int i; for (i = 0; i < links->len; i++) diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index eea6610d..24e828c1 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -44,13 +44,13 @@ typedef const char *string_t; static gboolean do_sysctl_set (char **argv) { - return nm_platform_sysctl_set (argv[0], argv[1]); + return nm_platform_sysctl_set (NM_PLATFORM_GET, argv[0], argv[1]); } static gboolean do_sysctl_get (char **argv) { - gs_free char *value = nm_platform_sysctl_get (argv[0]); + gs_free char *value = nm_platform_sysctl_get (NM_PLATFORM_GET, argv[0]); printf ("%s\n", value); @@ -66,7 +66,7 @@ parse_ifindex (const char *str) ifindex = strtol (str, &endptr, 10); if (*endptr) { - ifindex = nm_platform_link_get_ifindex (str); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, str); } return ifindex; @@ -79,7 +79,7 @@ do_link_get_all (char **argv) NMPlatformLink *device; int i; - links = nm_platform_link_get_all (); + links = nm_platform_link_get_all (NM_PLATFORM_GET); for (i = 0; i < links->len; i++) { device = &g_array_index (links, NMPlatformLink, i); @@ -93,25 +93,25 @@ do_link_get_all (char **argv) static gboolean do_dummy_add (char **argv) { - return nm_platform_dummy_add (argv[0]); + 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 (argv[0], NULL, 0); + 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 (argv[0]); + 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 (argv[0]); + return nm_platform_team_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -122,13 +122,13 @@ do_vlan_add (char **argv) int vlanid = strtol (*argv++, NULL, 10); guint32 vlan_flags = strtol (*argv++, NULL, 10); - return nm_platform_vlan_add (name, parent, vlanid, vlan_flags); + return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent, vlanid, vlan_flags, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_link_exists (char **argv) { - gboolean value = nm_platform_link_exists (argv[0]); + gboolean value = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, argv[0]); print_boolean (value); @@ -140,7 +140,7 @@ do_link_exists (char **argv) do_link_##cmdname (char **argv) \ { \ int ifindex = parse_ifindex (argv[0]); \ - return ifindex ? nm_platform_link_##cmdname (ifindex) : FALSE; \ + return ifindex ? nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex) : FALSE; \ } #define LINK_CMD_GET_FULL(cmdname, type, cond) \ @@ -149,7 +149,7 @@ do_link_exists (char **argv) { \ int ifindex = parse_ifindex (argv[0]); \ if (ifindex) { \ - type##_t value = nm_platform_link_##cmdname (ifindex); \ + type##_t value = nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex); \ if (cond) { \ print_##type (value); \ return TRUE; \ @@ -169,7 +169,7 @@ LINK_CMD (delete) static gboolean do_link_get_ifindex (char **argv) { - int ifindex = nm_platform_link_get_ifindex (argv[0]); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, argv[0]); if (ifindex) printf ("%d\n", ifindex); @@ -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) @@ -213,7 +220,7 @@ do_link_set_address (char **argv) g_assert (!*endptr); } - return nm_platform_link_set_address (ifindex, address, sizeof (address)); + return nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, address, sizeof (address)); } static gboolean @@ -224,7 +231,7 @@ do_link_get_address (char **argv) size_t length; int i; - address = nm_platform_link_get_address (ifindex, &length); + address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &length); if (!address || length <= 0) return FALSE; @@ -242,7 +249,7 @@ do_link_set_mtu (char **argv) int ifindex = parse_ifindex (*argv++); int mtu = strtoul (*argv++, NULL, 10); - return nm_platform_link_set_mtu (ifindex, mtu); + return nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, mtu); } LINK_CMD_GET (get_mtu, decimal); @@ -255,7 +262,7 @@ do_link_enslave (char **argv) int master = parse_ifindex (*argv++); int slave = parse_ifindex (*argv++); - return nm_platform_link_enslave (master, slave); + return nm_platform_link_enslave (NM_PLATFORM_GET, master, slave); } static gboolean @@ -264,7 +271,7 @@ do_link_release (char **argv) int master = parse_ifindex (*argv++); int slave = parse_ifindex (*argv++); - return nm_platform_link_release (master, slave); + return nm_platform_link_release (NM_PLATFORM_GET, master, slave); } LINK_CMD_GET (get_master, decimal) @@ -276,7 +283,7 @@ do_master_set_option (char **argv) const char *option = *argv++; const char *value = *argv++; - return nm_platform_master_set_option (ifindex, option, value); + return nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option, value); } static gboolean @@ -284,7 +291,7 @@ do_master_get_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; - gs_free char *value = nm_platform_master_get_option (ifindex, option); + gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option); printf ("%s\n", value); @@ -298,7 +305,7 @@ do_slave_set_option (char **argv) const char *option = *argv++; const char *value = *argv++; - return nm_platform_slave_set_option (ifindex, option, value); + return nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option, value); } static gboolean @@ -306,7 +313,7 @@ do_slave_get_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; - gs_free char *value = nm_platform_slave_get_option (ifindex, option); + gs_free char *value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, option); printf ("%s\n", value); @@ -320,7 +327,7 @@ do_vlan_get_info (char **argv) int parent; int vlanid; - if (!nm_platform_vlan_get_info (ifindex, &parent, &vlanid)) + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent, &vlanid)) return FALSE; printf ("%d %d\n", parent, vlanid); @@ -335,7 +342,7 @@ do_vlan_set_ingress_map (char **argv) int from = strtol (*argv++, NULL, 10); int to = strtol (*argv++, NULL, 10); - return nm_platform_vlan_set_ingress_map (ifindex, from, to); + return nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to); } static gboolean @@ -345,7 +352,7 @@ do_vlan_set_egress_map (char **argv) int from = strtol (*argv++, NULL, 10); int to = strtol (*argv++, NULL, 10); - return nm_platform_vlan_set_egress_map (ifindex, from, to); + return nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to); } static gboolean @@ -354,7 +361,7 @@ do_veth_get_properties (char **argv) int ifindex = parse_ifindex (*argv++); NMPlatformVethProperties props; - if (!nm_platform_veth_get_properties (ifindex, &props)) + if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("peer: %d\n", props.peer); @@ -368,7 +375,7 @@ do_tun_get_properties (char **argv) int ifindex = parse_ifindex (*argv++); NMPlatformTunProperties props; - if (!nm_platform_tun_get_properties (ifindex, &props)) + if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("mode: %s\n", props.mode); @@ -396,7 +403,7 @@ do_macvlan_get_properties (char **argv) int ifindex = parse_ifindex (*argv++); NMPlatformMacvlanProperties props; - if (!nm_platform_macvlan_get_properties (ifindex, &props)) + if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("parent: %d\n", props.parent_ifindex); @@ -413,7 +420,7 @@ do_vxlan_get_properties (char **argv) NMPlatformVxlanProperties props; char addrstr[INET6_ADDRSTRLEN]; - if (!nm_platform_vxlan_get_properties (ifindex, &props)) + if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("parent-ifindex: %u\n", props.parent_ifindex); @@ -460,7 +467,7 @@ do_gre_get_properties (char **argv) NMPlatformGreProperties props; char addrstr[INET_ADDRSTRLEN]; - if (!nm_platform_gre_get_properties (ifindex, &props)) + if (!nm_platform_gre_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("parent-ifindex: %u\n", props.parent_ifindex); @@ -496,7 +503,7 @@ do_ip4_address_get_all (char **argv) int i; if (ifindex) { - addresses = nm_platform_ip4_address_get_all (ifindex); + addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); inet_ntop (AF_INET, &address->address, addrstr, sizeof (addrstr)); @@ -518,7 +525,7 @@ do_ip6_address_get_all (char **argv) int i; if (ifindex) { - addresses = nm_platform_ip6_address_get_all (ifindex); + addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP6Address, i); inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr)); @@ -576,7 +583,7 @@ do_ip4_address_add (char **argv) guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); - gboolean value = nm_platform_ip4_address_add (ifindex, address, 0, plen, lifetime, preferred, NULL); + gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, 0, plen, lifetime, preferred, NULL); return value; } else return FALSE; @@ -594,7 +601,7 @@ do_ip6_address_add (char **argv) guint32 preferred = strtol (*argv++, NULL, 10); guint flags = (*argv) ? rtnl_addr_str2flags (*argv++) : 0; - gboolean value = nm_platform_ip6_address_add (ifindex, address, in6addr_any, plen, lifetime, preferred, flags); + gboolean value = nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, address, in6addr_any, plen, lifetime, preferred, flags); return value; } else return FALSE; @@ -608,7 +615,7 @@ do_ip6_address_add (char **argv) v##_t address; \ int plen; \ if (ifindex && parse_##v##_address (*argv++, &address, &plen)) { \ - gboolean value = nm_platform_##v##_address_##cmdname (ifindex, address, plen, ##__VA_ARGS__); \ + gboolean value = !!nm_platform_##v##_address_##cmdname (NM_PLATFORM_GET, ifindex, address, plen, ##__VA_ARGS__); \ if (print) { \ print_boolean (value); \ return TRUE; \ @@ -621,7 +628,7 @@ do_ip6_address_add (char **argv) #define ADDR_CMD_PRINT(cmdname) ADDR_CMD_FULL (ip4, cmdname, TRUE) ADDR_CMD_FULL (ip6, cmdname, TRUE) ADDR_CMD (delete) -ADDR_CMD_PRINT (exists) +ADDR_CMD_PRINT (get) static gboolean do_ip4_route_get_all (char **argv) @@ -633,7 +640,7 @@ do_ip4_route_get_all (char **argv) int i; if (ifindex) { - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); for (i = 0; i < routes->len; i++) { route = &g_array_index (routes, NMPlatformIP4Route, i); inet_ntop (AF_INET, &route->network, networkstr, sizeof (networkstr)); @@ -657,7 +664,7 @@ do_ip6_route_get_all (char **argv) int i; if (ifindex) { - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); for (i = 0; i < routes->len; i++) { route = &g_array_index (routes, NMPlatformIP6Route, i); inet_ntop (AF_INET6, &route->network, networkstr, sizeof (networkstr)); @@ -683,7 +690,7 @@ do_ip4_route_add (char **argv) metric = strtol (*argv++, NULL, 10); mss = strtol (*argv++, NULL, 10); - return nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, + return nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); } @@ -699,7 +706,7 @@ do_ip6_route_add (char **argv) parse_ip6_address (*argv++, &gateway, NULL); metric = strtol (*argv++, NULL, 10); mss = strtol (*argv++, NULL, 10); - return nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, + return nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss); } @@ -714,7 +721,7 @@ do_ip4_route_delete (char **argv) parse_ip4_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - return nm_platform_ip4_route_delete (ifindex, network, plen, metric); + return nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric); } static gboolean @@ -727,11 +734,11 @@ do_ip6_route_delete (char **argv) parse_ip6_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - return nm_platform_ip6_route_delete (ifindex, network, plen, metric); + return nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric); } static gboolean -do_ip4_route_exists (char **argv) +do_ip4_route_get (char **argv) { int ifindex = parse_ifindex (*argv++); in_addr_t network; @@ -740,12 +747,12 @@ do_ip4_route_exists (char **argv) parse_ip4_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - print_boolean (nm_platform_ip4_route_exists (ifindex, network, plen, metric)); + print_boolean (!!nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); return TRUE; } static gboolean -do_ip6_route_exists (char **argv) +do_ip6_route_get (char **argv) { int ifindex = parse_ifindex (*argv++); struct in6_addr network; @@ -754,7 +761,7 @@ do_ip6_route_exists (char **argv) parse_ip6_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - print_boolean (nm_platform_ip6_route_exists (ifindex, network, plen, metric)); + print_boolean (!!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); return TRUE; } @@ -831,9 +838,9 @@ static const command_t commands[] = { "<ifname/ifindex> <address>/<plen>" }, { "ip6-address-delete", "delete IPv6 address", do_ip6_address_delete, 2, "<ifname/ifindex> <address>/<plen>" }, - { "ip4-address-exists", "check for existence of IPv4 address", do_ip4_address_exists, 2, + { "ip4-address-exists", "check for existence of IPv4 address", do_ip4_address_get, 2, "<ifname/ifindex> <address>/<plen>" }, - { "ip6-address-exists", "check for existence of IPv6 address", do_ip6_address_exists, 2, + { "ip6-address-exists", "check for existence of IPv6 address", do_ip6_address_get, 2, "<ifname/ifindex> <address>/<plen>" }, { "ip4-route-get-all", "print all IPv4 routes", do_ip4_route_get_all, 1, "<ifname/ifindex>" }, { "ip6-route-get-all", "print all IPv6 routes", do_ip6_route_get_all, 1, "<ifname/ifindex>" }, @@ -845,9 +852,9 @@ static const command_t commands[] = { "<ifname/ifindex> <network>/<plen> <metric>" }, { "ip6-route-delete", "delete IPv6 route", do_ip6_route_delete, 3, "<ifname/ifindex> <network>/<plen> <metric>" }, - { "ip4-route-exists", "check for existence of IPv4 route", do_ip4_route_exists, 3, + { "ip4-route-exists", "check for existence of IPv4 route", do_ip4_route_get, 3, "<ifname/ifindex> <network>/<plen> <metric>" }, - { "ip6-route-exists", "check for existence of IPv6 route", do_ip6_route_exists, 3, + { "ip6-route-exists", "check for existence of IPv6 route", do_ip6_route_get, 3, "<ifname/ifindex> <network>/<plen> <metric>" }, { NULL, NULL, NULL, 0, NULL }, }; @@ -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 (); - if (error) { - const char *msg = nm_platform_get_error_msg (); - - 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 5561038d..d6f346b3 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -9,7 +9,7 @@ #define IP6_PLEN 64 static void -ip4_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -24,14 +24,12 @@ ip4_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Address *r if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -46,16 +44,14 @@ ip6_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Address *r if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void test_ip4_address (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback, ifindex); SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_address_callback, ifindex); SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback, ifindex); @@ -68,23 +64,18 @@ test_ip4_address (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); - no_error (); - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); - g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); - no_error (); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); - accept_signal (address_changed); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip4_address_get_all (ifindex); + 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); @@ -93,14 +84,12 @@ test_ip4_address (void) g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - no_error (); - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - no_error (); + g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); free_signal (address_added); free_signal (address_changed); @@ -110,7 +99,7 @@ test_ip4_address (void) static void test_ip6_address (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback, ifindex); SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_address_callback, ifindex); SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback, ifindex); @@ -124,23 +113,18 @@ test_ip6_address (void) inet_pton (AF_INET6, IP6_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); - no_error (); - g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); - g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); - no_error (); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); - accept_signal (address_changed); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip6_address_get_all (ifindex); + 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); @@ -149,14 +133,12 @@ test_ip6_address (void) g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); - no_error (); - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); - no_error (); + g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); free_signal (address_added); free_signal (address_changed); @@ -168,7 +150,7 @@ test_ip4_address_external (void) { SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback); SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback); - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); in_addr_t addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -179,28 +161,26 @@ 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_link_get_ifindex (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", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); wait_signal (address_added); - g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); wait_signal (address_removed); - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); /* Add/delete conflict */ 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 (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); - g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + g_assert (nm_platform_ip4_address_get (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 (ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_removed);*/ free_signal (address_added); @@ -212,7 +192,7 @@ test_ip6_address_external (void) { SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback); SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback); - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); struct in6_addr addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -224,22 +204,20 @@ 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); wait_signal (address_added); - g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); wait_signal (address_removed); - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); /* Add/delete conflict */ 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 (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); - g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + g_assert (nm_platform_ip6_address_get (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 (ifindex, addr, IP6_PLEN)); - no_error (); - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); wait_signal (address_removed);*/ free_signal (address_added); @@ -257,9 +235,9 @@ setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); - g_assert (nm_platform_dummy_add (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_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 0d0f3f63..0b825114 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -35,30 +35,30 @@ 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 (DEVICE_NAME)); + 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_link_get_ifindex (DEVICE_NAME))); - ifindex = nm_platform_link_get_ifindex (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); /* Add routes and addresses */ - g_assert (nm_platform_ip4_address_add (ifindex, addr4, 0, plen4, lifetime, preferred, NULL)); - g_assert (nm_platform_ip6_address_add (ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags)); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss)); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss)); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, metric, mss)); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss)); - - addresses4 = nm_platform_ip4_address_get_all (ifindex); - addresses6 = nm_platform_ip6_address_get_all (ifindex); - routes4 = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); - routes6 = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, 0, plen4, lifetime, preferred, NULL)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, metric, mss)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss)); + + addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); 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); @@ -68,12 +68,12 @@ test_cleanup_internal (void) g_array_unref (routes6); /* Delete interface with all addresses and routes */ - g_assert (nm_platform_link_delete (ifindex)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - addresses4 = nm_platform_ip4_address_get_all (ifindex); - addresses6 = nm_platform_ip6_address_get_all (ifindex); - routes4 = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); - routes6 = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); g_assert_cmpint (addresses4->len, ==, 0); g_assert_cmpint (addresses6->len, ==, 0); @@ -95,8 +95,8 @@ init_tests (int *argc, char ***argv) void setup_tests (void) { - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); g_test_add_func ("/internal", test_cleanup_internal); /* FIXME: add external cleanup check */ diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 1980282d..b0eae63b 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1,9 +1,31 @@ #include "config.h" +#include <sys/mount.h> +#include <sched.h> + #include "test-common.h" #include "nm-test-utils.h" +#define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" +#define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count + + +gboolean +nmtst_platform_is_root_test (void) +{ + NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") + return (SETUP == nm_linux_platform_setup); + NM_PRAGMA_WARNING_REENABLE +} + +gboolean +nmtst_platform_is_sysfs_writable (void) +{ + return !nmtst_platform_is_root_test () + || (access ("/sys/devices", W_OK) == 0); +} + SignalData * add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname) { @@ -11,7 +33,7 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall data->name = name; data->change_type = change_type; - data->received = FALSE; + data->received_count = 0; data->handler_id = g_signal_connect (nm_platform_get (), name, callback, data); data->ifindex = ifindex; data->ifname = ifname; @@ -21,58 +43,60 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall return data; } -static const char * -_change_type_to_string (NMPlatformSignalChangeType change_type) +void +_accept_signal (const char *file, int line, const char *func, SignalData *data) { - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - return "added"; - case NM_PLATFORM_SIGNAL_CHANGED: - return "changed"; - case NM_PLATFORM_SIGNAL_REMOVED: - return "removed"; - default: - g_return_val_if_reached ("UNKNOWN"); - } + debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count != 1) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + data->received_count = 0; } void -accept_signal (SignalData *data) +_accept_signals (const char *file, int line, const char *func, SignalData *data, int min, int max) { - debug ("Accepting signal '%s-%s' ifindex %d ifname %s.", data->name, _change_type_to_string (data->change_type), data->ifindex, data->ifname); - if (!data->received) - g_error ("Attemted to accept a non-received signal '%s-%s'.", data->name, _change_type_to_string (data->change_type)); + debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); + if (data->received_count < min || data->received_count > max) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); + data->received_count = 0; +} - data->received = FALSE; +void +_ensure_no_signal (const char *file, int line, const char *func, SignalData *data) +{ + debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count > 0) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); } void -wait_signal (SignalData *data) +_wait_signal (const char *file, int line, const char *func, SignalData *data) { - if (data->received) - g_error ("Signal '%s' received before waiting for it.", data->name); + debug ("NMPlatformSignalAssert: %s:%d, %s(): wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to wait for signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); data->loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (data->loop); g_clear_pointer (&data->loop, g_main_loop_unref); - accept_signal (data); + _accept_signal (file, line, func, data); } void -free_signal (SignalData *data) +_free_signal (const char *file, int line, const char *func, SignalData *data) { - if (data->received) - g_error ("Attempted to free received but not accepted signal '%s-%s'.", data->name, _change_type_to_string (data->change_type)); + debug ("NMPlatformSignalAssert: %s:%d, %s(): free signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count != 0) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to free non-accepted signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); g_signal_handler_disconnect (nm_platform_get (), data->handler_id); g_free (data); } void -link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { - GArray *links; NMPlatformLink *cached; int i; @@ -84,7 +108,7 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl if (data->ifindex && data->ifindex != received->ifindex) return; - if (data->ifname && g_strcmp0 (data->ifname, nm_platform_link_get_name (ifindex)) != 0) + if (data->ifname && g_strcmp0 (data->ifname, nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)) != 0) return; if (change_type != data->change_type) return; @@ -94,20 +118,17 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl g_main_loop_quit (data->loop); } - if (data->received) - g_error ("Received signal '%s-%s' a second time.", data->name, _change_type_to_string (data->change_type)); - - debug ("Received signal '%s-%s' ifindex %d ifname '%s'.", data->name, _change_type_to_string (data->change_type), ifindex, received->name); - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", data->name, nm_platform_signal_change_type_to_string (data->change_type), ifindex, received->name, data->received_count); if (change_type == NM_PLATFORM_SIGNAL_REMOVED) - g_assert (!nm_platform_link_get_name (ifindex)); + g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); else - g_assert (nm_platform_link_get_name (ifindex)); + g_assert (nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); /* Check the data */ g_assert (received->ifindex > 0); - links = nm_platform_link_get_all (); + links = nm_platform_link_get_all (NM_PLATFORM_GET); for (i = 0; i < links->len; i++) { cached = &g_array_index (links, NMPlatformLink, i); if (cached->ifindex == received->ifindex) { @@ -221,9 +242,9 @@ _assert_ip4_route_exists (const char *file, guint line, const char *func, gboole exists ? "doesn't" : "does"); } - ifindex = nm_platform_link_get_ifindex (ifname); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, ifname); g_assert (ifindex > 0); - if (!nm_platform_ip4_route_exists (ifindex, network, plen, metric) != !exists) { + if (!nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric) != !exists) { g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s", file, line, func, nm_utils_inet4_ntop (network, NULL), plen, metric, @@ -249,6 +270,46 @@ run_command (const char *format, ...) NMTST_DEFINE(); +static gboolean +unshare_user (void) +{ + FILE *f; + uid_t uid = geteuid (); + gid_t gid = getegid (); + + /* Already a root? */ + if (gid == 0 && uid == 0) + return TRUE; + + /* Become a root in new user NS. */ + if (unshare (CLONE_NEWUSER) != 0) + return FALSE; + + /* Since Linux 3.19 we have to disable setgroups() in order to map users. + * Just proceed if the file is not there. */ + f = fopen ("/proc/self/setgroups", "w"); + if (f) { + fprintf (f, "deny"); + fclose (f); + } + + /* Map current UID to root in NS to be created. */ + f = fopen ("/proc/self/uid_map", "w"); + if (!f) + return FALSE; + fprintf (f, "0 %d 1", uid); + fclose (f); + + /* Map current GID to root in NS to be created. */ + f = fopen ("/proc/self/gid_map", "w"); + if (!f) + return FALSE; + fprintf (f, "0 %d 1", gid); + fclose (f); + + return TRUE; +} + int main (int argc, char **argv) { @@ -257,20 +318,62 @@ main (int argc, char **argv) init_tests (&argc, &argv); - NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") - if (SETUP == nm_linux_platform_setup && getuid() != 0) { - /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ - nmtst_reexec_sudo (); + if ( nmtst_platform_is_root_test () + && (geteuid () != 0 || getegid () != 0)) { + if ( g_getenv ("NMTST_FORCE_REAL_ROOT") + || !unshare_user ()) { + /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ + nmtst_reexec_sudo (); #ifdef REQUIRE_ROOT_TESTS - g_print ("Fail test: requires root privileges (%s)\n", program); - return EXIT_FAILURE; + g_print ("Fail test: requires root privileges (%s)\n", program); + return EXIT_FAILURE; #else - g_print ("Skipping test: requires root privileges (%s)\n", program); - return 77; + g_print ("Skipping test: requires root privileges (%s)\n", program); + return g_test_run (); #endif + } + } + + if (nmtst_platform_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { + int errsv; + + if (unshare (CLONE_NEWNET | CLONE_NEWNS) != 0) { + errsv = errno; + g_error ("unshare(CLONE_NEWNET|CLONE_NEWNS) failed with %s (%d)", strerror (errsv), errsv); + } + + /* Mount our /sys instance, so that gudev sees only our devices. + * Needs to be read-only, because we don't run udev. */ + if (mount (NULL, "/sys", "sysfs", MS_SLAVE, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/\", MS_SLAVE) failed with %s (%d)", strerror (errsv), errsv); + } + if (mount ("sys", "/sys", "sysfs", MS_RDONLY, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); + } + + /* Create a writable /sys/devices tree. This makes it possible to run tests + * that modify values via sysfs (such as bridge forward delay). */ + if (mount ("sys", "/sys/devices", "sysfs", 0, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); + } + if (mount (NULL, "/sys/devices", "sysfs", MS_REMOUNT, NULL) != 0) { + /* Read-write remount failed. Never mind, we're probably just a root in + * our user NS. */ + if (umount ("/sys/devices") != 0) { + errsv = errno; + g_error ("umount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); + } + } else { + if (mount ("/sys/devices/devices", "/sys/devices", "sysfs", MS_BIND, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); + } + } } - NM_PRAGMA_WARNING_REENABLE SETUP (); @@ -278,8 +381,8 @@ main (int argc, char **argv) result = g_test_run (); - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - nm_platform_free (); + g_object_unref (nm_platform_get ()); return result; } diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 367833d1..3d647543 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -15,33 +15,40 @@ #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) -#define error(err) g_assert (nm_platform_get_error () == err) -#define no_error() error (NM_PLATFORM_ERROR_NONE) - typedef struct { int handler_id; const char *name; NMPlatformSignalChangeType change_type; - gboolean received; + gint received_count; GMainLoop *loop; int ifindex; const char *ifname; } SignalData; +gboolean nmtst_platform_is_root_test (void); +gboolean nmtst_platform_is_sysfs_writable (void); + SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname); #define add_signal(name, change_type, callback) add_signal_full (name, change_type, (GCallback) callback, 0, NULL) #define add_signal_ifindex(name, change_type, callback, ifindex) add_signal_full (name, change_type, (GCallback) callback, ifindex, NULL) #define add_signal_ifname(name, change_type, callback, ifname) add_signal_full (name, change_type, (GCallback) callback, 0, ifname) -void accept_signal (SignalData *data); -void wait_signal (SignalData *data); -void free_signal (SignalData *data); +void _accept_signal (const char *file, int line, const char *func, SignalData *data); +void _accept_signals (const char *file, int line, const char *func, SignalData *data, int min, int max); +void _wait_signal (const char *file, int line, const char *func, SignalData *data); +void _ensure_no_signal (const char *file, int line, const char *func, SignalData *data); +void _free_signal (const char *file, int line, const char *func, SignalData *data); +#define accept_signal(data) _accept_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define accept_signals(data, min, max) _accept_signals(__FILE__, __LINE__, G_STRFUNC, data, min, max) +#define wait_signal(data) _wait_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define ensure_no_signal(data) _ensure_no_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define free_signal(data) _free_signal(__FILE__, __LINE__, G_STRFUNC, data) gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric); void _assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); #define assert_ip4_route_exists(exists, ifname, network, plen, metric) _assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, exists, ifname, network, plen, metric) -void link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data); +void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data); void run_command (const char *format, ...); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c new file mode 100644 index 00000000..0a397b37 --- /dev/null +++ b/src/platform/tests/test-general.c @@ -0,0 +1,67 @@ +/* -*- 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 "nm-platform-utils.h" + +#include <linux/rtnetlink.h> + +#include "nm-linux-platform.h" +#include "nm-logging.h" + +#include "nm-test-utils.h" + + +/******************************************************************/ + +static void +test_init_linux_platform (void) +{ + gs_unref_object NMPlatform *platform = NULL; + + platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL); +} + +/******************************************************************/ + +static void +test_link_get_all (void) +{ + gs_unref_object NMPlatform *platform = NULL; + gs_unref_array GArray *links = NULL; + + platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL); + + links = nm_platform_link_get_all (platform); +} + +/******************************************************************/ + +NMTST_DEFINE (); + +int +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); + g_test_add_func ("/general/link_get_all", test_link_get_all); + + return g_test_run (); +} diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 99556e0a..dc7b8397 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -21,112 +21,94 @@ test_bogus(void) { size_t addrlen; - g_assert (!nm_platform_link_exists (BOGUS_NAME)); - no_error (); - g_assert (!nm_platform_link_delete (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_ifindex (BOGUS_NAME)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_name (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_type (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_type_name (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_link_set_up (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_down (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_arp (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_noarp (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_is_up (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_is_connected (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_uses_arp (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_link_get_address (BOGUS_IFINDEX, &addrlen)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, BOGUS_NAME)); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, BOGUS_NAME)); + g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + 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)); + g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + 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 (BOGUS_IFINDEX, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_mtu (BOGUS_IFINDEX, MTU)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_mtu (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_link_supports_carrier_detect (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_supports_vlans (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_vlan_get_info (BOGUS_IFINDEX, NULL, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_vlan_set_ingress_map (BOGUS_IFINDEX, 0, 0)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_vlan_set_egress_map (BOGUS_IFINDEX, 0, 0)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU)); + g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_assert (!nm_platform_vlan_get_info (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL, NULL)); + g_assert (!nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); + g_assert (!nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); } static void test_loopback (void) { - g_assert (nm_platform_link_exists (LO_NAME)); - g_assert_cmpint (nm_platform_link_get_type (LO_INDEX), ==, NM_LINK_TYPE_LOOPBACK); - g_assert_cmpint (nm_platform_link_get_ifindex (LO_NAME), ==, LO_INDEX); - g_assert_cmpstr (nm_platform_link_get_name (LO_INDEX), ==, LO_NAME); - g_assert_cmpstr (nm_platform_link_get_type_name (LO_INDEX), ==, LO_TYPEDESC); - - g_assert (nm_platform_link_supports_carrier_detect (LO_INDEX)); - g_assert (!nm_platform_link_supports_vlans (LO_INDEX)); + g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, LO_NAME)); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, LO_INDEX), ==, NM_LINK_TYPE_LOOPBACK); + g_assert_cmpint (nm_platform_link_get_ifindex (NM_PLATFORM_GET, LO_NAME), ==, LO_INDEX); + g_assert_cmpstr (nm_platform_link_get_name (NM_PLATFORM_GET, LO_INDEX), ==, LO_NAME); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, LO_INDEX), ==, LO_TYPEDESC); + + g_assert (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, LO_INDEX)); + 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 (name); + return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BRIDGE: - return nm_platform_bridge_add (name, NULL, 0); + 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 ("bond0"); - gboolean result = nm_platform_bond_add (name); - NMPlatformError error = nm_platform_get_error (); + gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0"); + 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 ("bond0")); - - nm_platform_set_error (error); - return result; + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0")); + return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: - return nm_platform_team_add (name); + 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 (PARENT_NAME, NULL, 0)) + 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 (PARENT_NAME); + int parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + gboolean was_up = nm_platform_link_is_up (NM_PLATFORM_GET, parent_ifindex); parent_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex); - g_assert (nm_platform_link_set_up (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 (name, parent_ifindex, VLAN_ID, 0); + return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } } default: @@ -142,9 +124,12 @@ 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 (SLAVE_NAME); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME); g_assert (ifindex > 0); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); @@ -154,46 +139,55 @@ test_slave (int master, int type, SignalData *master_changed) * * See https://bugzilla.redhat.com/show_bug.cgi?id=910348 */ - g_assert (nm_platform_link_set_down (ifindex)); - g_assert (!nm_platform_link_is_up (ifindex)); - accept_signal (link_changed); + 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)); + ensure_no_signal (link_changed); /* Enslave */ link_changed->ifindex = ifindex; - g_assert (nm_platform_link_enslave (master, ifindex)); no_error (); - g_assert_cmpint (nm_platform_link_get_master (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 (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 * * For some reason, bonding and teaming slaves are automatically set up. We * need to set them back down for this test. */ - switch (nm_platform_link_get_type (master)) { + switch (nm_platform_link_get_type (NM_PLATFORM_GET, master)) { case NM_LINK_TYPE_BOND: case NM_LINK_TYPE_TEAM: - g_assert (nm_platform_link_set_down (ifindex)); + 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; } - g_assert (!nm_platform_link_is_up (ifindex)); - g_assert (!nm_platform_link_is_connected (ifindex)); - if (nm_platform_link_is_connected (master)) { - if (nm_platform_link_get_type (master) == NM_LINK_TYPE_TEAM) { + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + if (nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { + if (nm_platform_link_get_type (NM_PLATFORM_GET, master) == NM_LINK_TYPE_TEAM) { /* Older team versions (e.g. Fedora 17) have a bug that team master stays * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if * `ip link` also claims master to be up, accept it. */ char *stdout_str = NULL; - nmtst_spawn_sync (NULL, &stdout_str, NULL, 0, "/sbin/ip", "link", "show", "dev", nm_platform_link_get_name (master)); + nmtst_spawn_sync (NULL, &stdout_str, NULL, 0, "/sbin/ip", "link", "show", "dev", nm_platform_link_get_name (NM_PLATFORM_GET, master)); g_assert (strstr (stdout_str, "LOWER_UP")); g_free (stdout_str); @@ -202,47 +196,59 @@ 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 (ifindex)); no_error (); - g_assert (nm_platform_link_is_connected (ifindex)); - g_assert (nm_platform_link_is_connected (master)); - accept_signal (link_changed); - accept_signal (master_changed); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); + accept_signals (link_changed, 1, 3); + /* 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 (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: - g_assert (nm_platform_slave_set_option (ifindex, "priority", "789")); - no_error (); - value = nm_platform_slave_get_option (ifindex, "priority"); - no_error (); - g_assert_cmpstr (value, ==, "789"); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); + value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); + g_assert_cmpstr (value, ==, "789"); + g_free (value); + } break; default: break; } /* Release */ - g_assert (nm_platform_link_release (master, ifindex)); - g_assert_cmpint (nm_platform_link_get_master (ifindex), ==, 0); no_error (); - accept_signal (link_changed); - accept_signal (master_changed); + 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); + 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 */ - g_assert (!nm_platform_link_release (master, ifindex)); - error (NM_PLATFORM_ERROR_NOT_SLAVE); + ensure_no_signal (link_changed); + g_assert (!nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); + + ensure_no_signal (master_changed); /* Remove */ - g_assert (nm_platform_link_delete (ifindex)); - no_error (); + ensure_no_signal (link_changed); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + accept_signals (master_changed, 0, 1); + accept_signals (link_changed, 0, 1); accept_signal (link_removed); free_signal (link_added); @@ -262,53 +268,50 @@ 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 (DEVICE_NAME)); - ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex >= 0); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, link_type); - g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, link_typename); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, link_type); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex), ==, link_typename); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); if (link_type == NM_LINK_TYPE_VLAN) { - g_assert (nm_platform_vlan_get_info (ifindex, &vlan_parent, &vlan_id)); - g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (PARENT_NAME)); + 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 (ifindex)); - g_assert (nm_platform_link_set_noarp (ifindex)); - g_assert (!nm_platform_link_uses_arp (ifindex)); - accept_signal (link_changed); - g_assert (nm_platform_link_set_arp (ifindex)); - g_assert (nm_platform_link_uses_arp (ifindex)); + 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_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); /* Set master option */ switch (link_type) { case NM_LINK_TYPE_BRIDGE: - g_assert (nm_platform_master_set_option (ifindex, "forward_delay", "789")); - no_error (); - value = nm_platform_master_get_option (ifindex, "forward_delay"); - no_error (); - g_assert_cmpstr (value, ==, "789"); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); + value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); + g_assert_cmpstr (value, ==, "789"); + g_free (value); + } break; case NM_LINK_TYPE_BOND: - g_assert (nm_platform_master_set_option (ifindex, "mode", "active-backup")); - no_error (); - value = nm_platform_master_get_option (ifindex, "mode"); - no_error (); - /* When reading back, the output looks slightly different. */ - g_assert (g_str_has_prefix (value, "active-backup")); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); + value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); + /* When reading back, the output looks slightly different. */ + g_assert (g_str_has_prefix (value, "active-backup")); + g_free (value); + } break; default: break; @@ -326,33 +329,29 @@ test_software (NMLinkType link_type, const char *link_typename) default: break; } + free_signal (link_changed); /* Delete */ - g_assert (nm_platform_link_delete (ifindex)); - no_error (); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error (); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_NONE); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_type (ifindex)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_NONE); + g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, ifindex)); accept_signal (link_removed); /* Delete again */ - g_assert (!nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME))); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); /* VLAN: Delete parent */ if (link_type == NM_LINK_TYPE_VLAN) { SignalData *link_removed_parent = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent); - g_assert (nm_platform_link_delete (vlan_parent)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, vlan_parent)); accept_signal (link_removed_parent); free_signal (link_removed_parent); } /* No pending signal */ free_signal (link_added); - free_signal (link_changed); free_signal (link_removed); } @@ -365,14 +364,12 @@ test_bridge (void) static void test_bond (void) { - NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") - if (SETUP == nm_linux_platform_setup && + if (nmtst_platform_is_root_test () && !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) && system("modprobe --show bonding") != 0) { g_test_skip ("Skipping test for bonding: bonding module not available"); return; } - NM_PRAGMA_WARNING_REENABLE test_software (NM_LINK_TYPE_BOND, "bond"); } @@ -400,76 +397,70 @@ test_internal (void) int ifindex; /* Check the functions for non-existent devices */ - g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error (); - g_assert (!nm_platform_link_get_ifindex (DEVICE_NAME)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); /* Add device */ - g_assert (nm_platform_dummy_add (DEVICE_NAME)); - 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 (DEVICE_NAME)); - 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 (DEVICE_NAME); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex > 0); - g_assert_cmpstr (nm_platform_link_get_name (ifindex), ==, DEVICE_NAME); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_DUMMY); - g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, DUMMY_TYPEDESC); + g_assert_cmpstr (nm_platform_link_get_name (NM_PLATFORM_GET, ifindex), ==, DEVICE_NAME); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_DUMMY); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex), ==, DUMMY_TYPEDESC); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); 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 (ifindex)); no_error (); - g_assert (!nm_platform_link_is_connected (ifindex)); no_error (); - g_assert (nm_platform_link_set_up (ifindex)); no_error (); - g_assert (nm_platform_link_is_up (ifindex)); no_error (); - g_assert (nm_platform_link_is_connected (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 (ifindex)); no_error (); - g_assert (!nm_platform_link_is_up (ifindex)); no_error (); - g_assert (!nm_platform_link_is_connected (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 */ - g_assert (!nm_platform_link_uses_arp (ifindex)); - g_assert (nm_platform_link_set_arp (ifindex)); - g_assert (nm_platform_link_uses_arp (ifindex)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); + 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); - g_assert (nm_platform_link_set_noarp (ifindex)); - g_assert (!nm_platform_link_uses_arp (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); /* Features */ - g_assert (!nm_platform_link_supports_carrier_detect (ifindex)); - g_assert (nm_platform_link_supports_vlans (ifindex)); + g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_supports_vlans (NM_PLATFORM_GET, ifindex)); /* Set MAC address */ - g_assert (nm_platform_link_set_address (ifindex, mac, sizeof (mac))); - address = nm_platform_link_get_address (ifindex, &addrlen); + g_assert (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac))); + address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addrlen); g_assert (addrlen == sizeof(mac)); g_assert (!memcmp (address, mac, addrlen)); - address = nm_platform_link_get_address (ifindex, NULL); + address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, NULL); g_assert (!memcmp (address, mac, addrlen)); accept_signal (link_changed); /* Set MTU */ - g_assert (nm_platform_link_set_mtu (ifindex, MTU)); - no_error (); - g_assert_cmpint (nm_platform_link_get_mtu (ifindex), ==, MTU); + g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU)); + 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 (ifindex)); - no_error (); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); accept_signal (link_removed); /* Try to delete again */ - g_assert (!nm_platform_link_delete (ifindex)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); free_signal (link_added); free_signal (link_changed); @@ -479,60 +470,56 @@ test_internal (void) static void test_external (void) { - NMPlatformLink link; + const NMPlatformLink *pllink; SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); SignalData *link_changed, *link_removed; int ifindex; - gboolean success; run_command ("ip link add %s type %s", DEVICE_NAME, "dummy"); wait_signal (link_added); - g_assert (nm_platform_link_exists (DEVICE_NAME)); - ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + + g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex > 0); - g_assert_cmpstr (nm_platform_link_get_name (ifindex), ==, DEVICE_NAME); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_DUMMY); - g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, DUMMY_TYPEDESC); + g_assert_cmpstr (nm_platform_link_get_name (NM_PLATFORM_GET, ifindex), ==, DEVICE_NAME); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_DUMMY); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex), ==, DUMMY_TYPEDESC); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); - success = nm_platform_link_get (ifindex, &link); - g_assert (success); - if (!link.driver) { + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + g_assert (pllink); + if (!pllink->initialized) { /* we still lack the notification via UDEV. Expect another link changed signal. */ wait_signal (link_changed); } /* Up/connected/arp */ - g_assert (!nm_platform_link_is_up (ifindex)); - g_assert (!nm_platform_link_is_connected (ifindex)); - g_assert (!nm_platform_link_uses_arp (ifindex)); + 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_uses_arp (NM_PLATFORM_GET, ifindex)); + run_command ("ip link set %s up", DEVICE_NAME); wait_signal (link_changed); - g_assert (nm_platform_link_is_up (ifindex)); - g_assert (nm_platform_link_is_connected (ifindex)); + + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); run_command ("ip link set %s down", DEVICE_NAME); wait_signal (link_changed); - g_assert (!nm_platform_link_is_up (ifindex)); - g_assert (!nm_platform_link_is_connected (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. - */ + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + 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); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); + accept_signals (link_changed, 0, 1); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); free_signal (link_added); free_signal (link_changed); @@ -548,12 +535,12 @@ init_tests (int *argc, char ***argv) void setup_tests (void) { - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - nm_platform_link_delete (nm_platform_link_get_ifindex (SLAVE_NAME)); - nm_platform_link_delete (nm_platform_link_get_ifindex (PARENT_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (SLAVE_NAME)); - g_assert (!nm_platform_link_exists (PARENT_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, SLAVE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, PARENT_NAME)); g_test_add_func ("/link/bogus", test_bogus); g_test_add_func ("/link/loopback", test_loopback); diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c new file mode 100644 index 00000000..88bb0707 --- /dev/null +++ b/src/platform/tests/test-nmp-object.c @@ -0,0 +1,425 @@ +/* -*- 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 (void) +{ + 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 (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1); + g_assert (!nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + 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 (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* updating the same link with identical value, has no effect. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + 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 (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (!nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED); + 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); + + 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_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + nmp_object_unref (obj2); + } + + /* add it in netlink too. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* remove again from netlink. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = FALSE; + g_assert (!nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); + 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_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + } 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_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE); + g_assert_cmpint (obj2->link.initialized, ==, FALSE); + nmp_object_unref (obj2); + + /* add it in netlink too. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj2->link.initialized, ==, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* 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_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache)); + nmp_object_unref (obj2); + } + + 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 2d3caaf1..cf322dec 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" @@ -7,7 +9,7 @@ #define DEVICE_NAME "nm-test-device" static void -ip4_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -22,14 +24,12 @@ ip4_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Route *recei if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -44,16 +44,14 @@ ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *recei if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void test_ip4_route_metric0 (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); @@ -67,48 +65,42 @@ test_ip4_route_metric0 (void) assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); /* add the first route */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); accept_signal (route_added); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Deleting route with metric 0 does nothing */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); - no_error (); - g_assert (!route_removed->received); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + ensure_no_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* add the second route */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); accept_signal (route_added); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Delete route with metric 0 */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); accept_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Delete route with metric 0 again (we expect nothing to happen) */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); - no_error (); - g_assert (!route_removed->received); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + ensure_no_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Delete the other route */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -122,7 +114,7 @@ test_ip4_route_metric0 (void) static void test_ip4_route (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); @@ -139,40 +131,31 @@ test_ip4_route (void) inet_pton (AF_INET, "198.51.100.1", &gateway); /* Add route to gateway */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss)); 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 (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); 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 (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); + 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 (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); 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 (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); + accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); memset (rts, 0, sizeof (rts)); rts[0].source = NM_IP_CONFIG_SOURCE_USER; rts[0].network = gateway; @@ -181,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; @@ -188,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; @@ -195,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 (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); free_signal (route_added); free_signal (route_changed); @@ -218,7 +201,7 @@ test_ip4_route (void) static void test_ip6_route (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_route_callback); @@ -235,40 +218,31 @@ test_ip6_route (void) inet_pton (AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway); /* Add route to gateway */ - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss)); - no_error (); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss)); accept_signal (route_added); /* Add route */ - g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, metric)); - no_error (); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); - no_error (); - g_assert (nm_platform_ip6_route_exists (ifindex, network, plen, metric)); - no_error (); + g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); + g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_added); /* Add route again */ - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); + accept_signals (route_changed, 0, 1); /* Add default route */ - g_assert (!nm_platform_ip6_route_exists (ifindex, in6addr_any, 0, metric)); - no_error (); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); - no_error (); - g_assert (nm_platform_ip6_route_exists (ifindex, in6addr_any, 0, metric)); - no_error (); + g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); + g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); accept_signal (route_added); /* Add default route again */ - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); + accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); memset (rts, 0, sizeof (rts)); rts[0].source = NM_IP_CONFIG_SOURCE_USER; rts[0].network = gateway; @@ -292,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 (ifindex, network, plen, metric)); - no_error (); - g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, metric)); + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); free_signal (route_added); free_signal (route_changed); @@ -322,13 +293,13 @@ setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); - g_assert (nm_platform_dummy_add (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_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_link_get_ifindex (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/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index daef881b..068c404e 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -162,20 +162,13 @@ wifi_utils_deinit (WifiData *data) } gboolean -wifi_utils_is_wifi (const char *iface, const char *sysfs_path, const char *devtype) +wifi_utils_is_wifi (const char *iface, const char *sysfs_path) { char phy80211_path[255]; struct stat s; g_return_val_if_fail (iface != NULL, FALSE); - if (g_strcmp0 (devtype, "wlan") == 0) { - /* All Wi-Fi drivers should set DEVTYPE=wlan. Since the kernel's - * cfg80211/nl80211 stack does, this check should match any nl80211 - * capable driver (including mac82011-based ones). */ - return TRUE; - } - if (sysfs_path) { /* Check for nl80211 sysfs paths */ g_snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", sysfs_path); diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index eb37bc22..588386ab 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -29,7 +29,7 @@ typedef struct WifiData WifiData; -gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path, const char *devtype); +gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path); WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan); |