diff options
| author | Michael Biebl <biebl@debian.org> | 2016-01-20 16:26:51 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2016-01-20 16:26:51 +0100 |
| commit | 494f296a3baab08522617b24b1f126d8f9a17502 (patch) | |
| tree | c8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/platform | |
| parent | 54f6333410ffd570e62717d9e77c5c987175e397 (diff) | |
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/platform')
25 files changed, 9462 insertions, 3876 deletions
diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in index e8b73294..854c6712 100644 --- a/src/platform/Makefile.in +++ b/src/platform/Makefile.in @@ -206,7 +206,6 @@ BLUEZ5_LIBS = @BLUEZ5_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CKDB_PATH = @CKDB_PATH@ CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ @@ -218,8 +217,6 @@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ -DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ -DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SYS_DIR = @DBUS_SYS_DIR@ DEFS = @DEFS@ @@ -229,6 +226,7 @@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ DNSMASQ_PATH = @DNSMASQ_PATH@ +DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -283,16 +281,13 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ -IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ -IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ +LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ +LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ LIBDL = @LIBDL@ -LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ -LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ -LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -329,6 +324,8 @@ NEWT_LIBS = @NEWT_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@ +NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@ NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ NM_MICRO_VERSION = @NM_MICRO_VERSION@ NM_MINOR_VERSION = @NM_MINOR_VERSION@ @@ -357,7 +354,6 @@ POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ PPPD_PATH = @PPPD_PATH@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ -PPPOE_PATH = @PPPOE_PATH@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ @@ -372,6 +368,8 @@ SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@ +SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@ SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ @@ -432,6 +430,7 @@ mkdir_p = @mkdir_p@ nmbinary = @nmbinary@ nmconfdir = @nmconfdir@ nmdatadir = @nmdatadir@ +nmlibdir = @nmlibdir@ nmrundir = @nmrundir@ nmstatedir = @nmstatedir@ oldincludedir = @oldincludedir@ @@ -439,6 +438,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index d5aeb030..a4eae902 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -26,15 +26,41 @@ #include <netinet/in.h> #include <linux/rtnetlink.h> -#include "gsystem-local-alloc.h" #include "nm-utils.h" +#include "nmp-object.h" #include "NetworkManagerUtils.h" #include "nm-fake-platform.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-test-utils.h" -#define debug(format, ...) nm_log_dbg (LOGD_PLATFORM, format, __VA_ARGS__) +/*********************************************************************************************/ + +#define _NMLOG_PREFIX_NAME "platform-fake" +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) + +#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 = _NMLOG_PREFIX_NAME; \ + const void *const __self = (self); \ + \ + if (__self && __self != nm_platform_try_get ()) { \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_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 + +/*********************************************************************************************/ typedef struct { GHashTable *options; @@ -49,8 +75,7 @@ typedef struct { NMPlatformLink link; char *udi; - int vlan_id; - int ib_p_key; + NMPObject *lnk; struct in6_addr ip6_lladdr; } NMFakePlatformLink; @@ -62,14 +87,27 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal); -static gboolean ip6_address_add (NMPlatform *platform, int ifindex, - struct in6_addr addr, struct in6_addr peer_addr, - int plen, guint32 lifetime, guint32 preferred, guint flags); +static gboolean ip6_address_add (NMPlatform *platform, + int ifindex, + struct in6_addr addr, + int plen, + struct in6_addr peer_addr, + guint32 lifetime, + guint32 preferred, + guint flags); static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen); /******************************************************************/ static gboolean +_ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, int plen) +{ + return ((peer1 ^ peer2) & nm_utils_ip4_prefix_to_netmask (plen)) == 0; +} + +/******************************************************************/ + +static gboolean sysctl_set (NMPlatform *platform, const char *path, const char *value) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -157,7 +195,7 @@ link_get (NMPlatform *platform, int ifindex) return device; not_found: - debug ("link not found: %d", ifindex); + _LOGD ("link not found: %d", ifindex); return NULL; } @@ -222,29 +260,63 @@ _nm_platform_link_get_by_address (NMPlatform *platform, return NULL; } +static const NMPObject * +link_get_lnk (NMPlatform *platform, + int ifindex, + NMLinkType link_type, + const NMPlatformLink **out_link) +{ + NMFakePlatformLink *device = link_get (platform, ifindex); + + if (!device) + return NULL; + + NM_SET_OUT (out_link, &device->link); + + if (!device->lnk) + return NULL; + + if (link_type == NM_LINK_TYPE_NONE) + return device->lnk; + + if ( link_type != device->link.type + || link_type != NMP_OBJECT_GET_CLASS (device->lnk)->lnk_link_type) + return NULL; + + return device->lnk; +} + static gboolean link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len, - NMPlatformLink *out_link) + const NMPlatformLink **out_link) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMFakePlatformLink device; + NMFakePlatformLink *new_device; link_init (&device, priv->links->len, type, name); + if (address) { + g_return_val_if_fail (address_len > 0 && address_len <= sizeof (device.link.addr.data), FALSE); + memcpy (device.link.addr.data, address, address_len); + device.link.addr.len = address_len; + } + g_array_append_val (priv->links, device); + new_device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED); link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE); } if (out_link) - *out_link = device.link; + *out_link = &new_device->link; return TRUE; } @@ -261,6 +333,8 @@ link_delete (NMPlatform *platform, int ifindex) memcpy (&deleted_device, &device->link, sizeof (deleted_device)); memset (&device->link, 0, sizeof (device->link)); + g_clear_pointer (&device->lnk, nmp_object_unref); + g_clear_pointer (&device->udi, g_free); /* Remove addresses and routes which belong to the deleted interface */ for (i = 0; i < priv->ip4_addresses->len; i++) { @@ -288,7 +362,7 @@ link_delete (NMPlatform *platform, int ifindex) memset (route, 0, sizeof (*route)); } - 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED); return TRUE; } @@ -306,11 +380,11 @@ link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_s int i; 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED); 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); + ip6_address_add (platform, device->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); else ip6_address_delete (platform, device->link.ifindex, device->ip6_lladdr, 64); } @@ -345,8 +419,10 @@ link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) if (out_no_firmware) *out_no_firmware = FALSE; - if (!device) + if (!device) { + _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; + } up = TRUE; connected = TRUE; @@ -379,8 +455,10 @@ link_set_down (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (!device) + if (!device) { + _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; + } if (NM_FLAGS_HAS (device->link.flags, IFF_UP) || device->link.connected) { device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_UP); @@ -397,8 +475,10 @@ link_set_arp (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (!device) + if (!device) { + _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; + } device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_NOARP); @@ -412,8 +492,10 @@ link_set_noarp (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (!device) + if (!device) { + _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; + } device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_NOARP); @@ -451,7 +533,8 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) if (device) { device->link.mtu = mtu; link_changed (platform, device, TRUE); - } + } else + _LOGE ("failure changing link: netlink error (No such device)"); return !!device; } @@ -559,86 +642,69 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) } static gboolean -master_set_option (NMPlatform *platform, int master, const char *option, const char *value) +vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, const NMPlatformLink **out_link) { - gs_free char *path = g_strdup_printf ("master:%d:%s", master, option); + NMFakePlatformLink *device; - return sysctl_set (platform, path, value); -} + if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0, out_link)) + return FALSE; -static char * -master_get_option (NMPlatform *platform, int master, const char *option) -{ - gs_free char *path = g_strdup_printf ("master:%d:%s", master, option); + device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); - return sysctl_get (platform, path); -} + g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (!device->lnk, FALSE); -static gboolean -slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value) -{ - gs_free char *path = g_strdup_printf ("slave:%d:%s", slave, option); + device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + device->lnk->lnk_vlan.id = vlan_id; + device->link.parent = parent; - return sysctl_set (platform, path, value); + if (out_link) + *out_link = &device->link; + return TRUE; } -static char * -slave_get_option (NMPlatform *platform, int slave, const char *option) +static gboolean +link_vlan_change (NMPlatform *platform, + int ifindex, + NMVlanFlags flags_mask, + NMVlanFlags flags_set, + gboolean ingress_reset_all, + const NMVlanQosMapping *ingress_map, + gsize n_ingress_map, + gboolean egress_reset_all, + const NMVlanQosMapping *egress_map, + gsize n_egress_map) { - gs_free char *path = g_strdup_printf ("slave:%d:%s", slave, option); - - return sysctl_get (platform, path); + return FALSE; } static gboolean -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, NMPlatformLink *out_link) +link_vxlan_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkVxlan *props, + const NMPlatformLink **out_link) { NMFakePlatformLink *device; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0, NULL)) + if (!link_add (platform, name, NM_LINK_TYPE_VXLAN, NULL, 0, out_link)) return FALSE; device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (!device->lnk, FALSE); - device->vlan_id = vlan_id; - device->link.parent = parent; + device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + device->lnk->lnk_vxlan = *props; + device->link.parent = props->parent_ifindex; if (out_link) - *out_link = device->link; + *out_link = &device->link; return TRUE; } static gboolean -vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - g_return_val_if_fail (device, FALSE); - - if (parent) - *parent = device->link.parent; - if (vlan_id) - *vlan_id = device->vlan_id; - - return TRUE; -} - -static gboolean -vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) -{ - return !!link_get (platform, ifindex); -} - -static gboolean -vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) -{ - return !!link_get (platform, ifindex); -} - -static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatformLink *out_link) +infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) { NMFakePlatformLink *device, *parent_device; gs_free char *name = NULL; @@ -652,63 +718,17 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatfor device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (!device->lnk, FALSE); - device->ib_p_key = p_key; + device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + device->lnk->lnk_infiniband.p_key = p_key; + device->lnk->lnk_infiniband.mode = "datagram"; device->link.parent = parent; 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 -veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) -{ - return FALSE; -} - -static gboolean -tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) -{ - return FALSE; -} - -static gboolean -macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props) -{ - return FALSE; -} - -static gboolean -vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props) -{ - return FALSE; -} - -static gboolean -gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props) -{ - return FALSE; -} - -static gboolean wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) { NMFakePlatformLink *device = link_get (platform, ifindex); @@ -861,9 +881,13 @@ ip6_address_get_all (NMPlatform *platform, int ifindex) } static gboolean -ip4_address_add (NMPlatform *platform, int ifindex, - in_addr_t addr, in_addr_t peer_addr, - int plen, guint32 lifetime, guint32 preferred, +ip4_address_add (NMPlatform *platform, + int ifindex, + in_addr_t addr, + int plen, + in_addr_t peer_addr, + guint32 lifetime, + guint32 preferred, const char *label) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -884,29 +908,36 @@ ip4_address_add (NMPlatform *platform, int ifindex, for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + gboolean changed; - if (item->ifindex != address.ifindex) - continue; - if (item->address != address.address) - continue; - if (item->plen != address.plen) + if ( item->ifindex != address.ifindex + || item->address != address.address + || item->plen != address.plen + || !_ip4_address_equal_peer_net (item->peer_address, address.peer_address, address.plen)) continue; + changed = !nm_platform_ip4_address_cmp (item, &address); + memcpy (item, &address, sizeof (address)); - 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); + if (changed) + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED); return TRUE; } g_array_append_val (priv->ip4_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, 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); return TRUE; } 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) +ip6_address_add (NMPlatform *platform, + int ifindex, + struct in6_addr addr, + int plen, + struct in6_addr peer_addr, + guint32 lifetime, + guint32 preferred, guint flags) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMPlatformIP6Address address; @@ -916,7 +947,7 @@ ip6_address_add (NMPlatform *platform, int ifindex, address.source = NM_IP_CONFIG_SOURCE_KERNEL; address.ifindex = ifindex; address.address = addr; - address.peer_address = peer_addr; + address.peer_address = (IN6_IS_ADDR_UNSPECIFIED (&peer_addr) || IN6_ARE_ADDR_EQUAL (&addr, &peer_addr)) ? in6addr_any : peer_addr; address.plen = plen; address.timestamp = nm_utils_get_monotonic_timestamp_s (); address.lifetime = lifetime; @@ -925,23 +956,22 @@ ip6_address_add (NMPlatform *platform, int ifindex, for (i = 0; i < priv->ip6_addresses->len; i++) { NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); + gboolean changed; - if (item->ifindex != address.ifindex) - continue; - if (!IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) - continue; - if (item->plen != address.plen) + if ( item->ifindex != address.ifindex + || !IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) continue; - 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); - } + changed = !nm_platform_ip6_address_cmp (item, &address); + + memcpy (item, &address, sizeof (address)); + if (changed) + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED); return TRUE; } g_array_append_val (priv->ip6_addresses, address); - 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED); return TRUE; } @@ -955,13 +985,15 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address->ifindex == ifindex && address->plen == plen && address->address == addr && - (!peer_address || address->peer_address == peer_address)) { + if ( address->ifindex == ifindex + && address->plen == plen + && address->address == addr + && ((peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (plen)) == 0) { NMPlatformIP4Address deleted_address; memcpy (&deleted_address, address, sizeof (deleted_address)); memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, 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); return TRUE; } } @@ -978,13 +1010,14 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int for (i = 0; i < priv->ip6_addresses->len; i++) { NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address->ifindex == ifindex && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { + if ( address->ifindex == ifindex + && address->plen == plen + && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { NMPlatformIP6Address deleted_address; memcpy (&deleted_address, address, sizeof (deleted_address)); memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, 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); return TRUE; } } @@ -993,7 +1026,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int } static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; @@ -1001,7 +1034,10 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address->ifindex == ifindex && address->plen == plen && address->address == addr) + if ( address->ifindex == ifindex + && address->plen == plen + && address->address == addr + && _ip4_address_equal_peer_net (address->peer_address, peer_address, plen)) return address; } @@ -1105,7 +1141,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED); } return TRUE; @@ -1131,7 +1167,7 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED); } return TRUE; @@ -1140,7 +1176,7 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in 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) + in_addr_t pref_src, guint32 metric, guint32 mss) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMPlatformIP4Route route; @@ -1194,12 +1230,12 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, } memcpy (item, &route, sizeof (route)); - 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED); return TRUE; } g_array_append_val (priv->ip4_routes, route); - 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED); return TRUE; } @@ -1261,12 +1297,12 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, } memcpy (item, &route, sizeof (route)); - 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED); return TRUE; } g_array_append_val (priv->ip6_routes, route); - 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); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED); return TRUE; } @@ -1358,6 +1394,7 @@ nm_fake_platform_finalize (GObject *object) NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_free (device->udi); + g_clear_pointer (&device->lnk, nmp_object_unref); } g_array_unref (priv->links); g_array_unref (priv->ip4_addresses); @@ -1390,6 +1427,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_delete = link_delete; platform_class->link_get_type_name = link_get_type_name; + platform_class->link_get_lnk = link_get_lnk; + platform_class->link_get_udi = link_get_udi; platform_class->link_set_up = link_set_up; @@ -1407,24 +1446,12 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; - platform_class->master_set_option = master_set_option; - platform_class->master_get_option = master_get_option; - platform_class->slave_set_option = slave_set_option; - platform_class->slave_get_option = slave_get_option; platform_class->vlan_add = vlan_add; - platform_class->vlan_get_info = vlan_get_info; - platform_class->vlan_set_ingress_map = vlan_set_ingress_map; - platform_class->vlan_set_egress_map = vlan_set_egress_map; + platform_class->link_vlan_change = link_vlan_change; + platform_class->link_vxlan_add = link_vxlan_add; 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; - platform_class->macvlan_get_properties = macvlan_get_properties; - platform_class->vxlan_get_properties = vxlan_get_properties; - platform_class->gre_get_properties = gre_get_properties; platform_class->wifi_get_capabilities = wifi_get_capabilities; platform_class->wifi_get_bssid = wifi_get_bssid; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index ee6ba421..f73c228b 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -22,8 +22,10 @@ #include <errno.h> #include <unistd.h> #include <sys/socket.h> +#include <sys/ioctl.h> #include <fcntl.h> #include <dlfcn.h> +#include <arpa/inet.h> #include <netinet/icmp6.h> #include <netinet/in.h> #include <linux/ip.h> @@ -40,24 +42,13 @@ #include <netlink/route/route.h> #include <gudev/gudev.h> -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN -#include <netlink/route/link/inet6.h> -#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 -#define IN6_ADDR_GEN_MODE_NONE 1 -#endif -#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 "nm-default.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" #include "nmp-object.h" @@ -65,11 +56,67 @@ /* This is only included for the translation of VLAN flags */ #include "nm-setting-vlan.h" +#define VLAN_FLAG_MVRP 0x8 + +/*********************************************************************************************/ + +#define IFQDISCSIZ 32 + +/*********************************************************************************************/ + +#ifndef IFLA_PROMISCUITY +#define IFLA_PROMISCUITY 30 +#endif +#define IFLA_NUM_TX_QUEUES 31 +#define IFLA_NUM_RX_QUEUES 32 +#define IFLA_CARRIER 33 +#define IFLA_PHYS_PORT_ID 34 +#define IFLA_LINK_NETNSID 37 +#define __IFLA_MAX 39 + +#define IFLA_INET6_TOKEN 7 +#define IFLA_INET6_ADDR_GEN_MODE 8 +#define __IFLA_INET6_MAX 9 + +#define IFLA_VLAN_PROTOCOL 5 +#define __IFLA_VLAN_MAX 6 + +#define IFA_FLAGS 8 +#define __IFA_MAX 9 + +#define IFLA_MACVLAN_FLAGS 2 +#define __IFLA_MACVLAN_MAX 3 + +#define IFLA_IPTUN_LINK 1 +#define IFLA_IPTUN_LOCAL 2 +#define IFLA_IPTUN_REMOTE 3 +#define IFLA_IPTUN_TTL 4 +#define IFLA_IPTUN_TOS 5 +#define IFLA_IPTUN_ENCAP_LIMIT 6 +#define IFLA_IPTUN_FLOWINFO 7 +#define IFLA_IPTUN_FLAGS 8 +#define IFLA_IPTUN_PROTO 9 +#define IFLA_IPTUN_PMTUDISC 10 +#define __IFLA_IPTUN_MAX 19 +#ifndef IFLA_IPTUN_MAX +#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) +#endif + +#ifndef MACVLAN_FLAG_NOPROMISC +#define MACVLAN_FLAG_NOPROMISC 1 +#endif + +#define IP6_FLOWINFO_TCLASS_MASK 0x0FF00000 +#define IP6_FLOWINFO_TCLASS_SHIFT 20 +#define IP6_FLOWINFO_FLOWLABEL_MASK 0x000FFFFF + /*********************************************************************************************/ -#define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform-linux" -#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG2_DOMAIN LOGD_PLATFORM +#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) +#define _NMLOG2(level, ...) _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) #define _LOG(level, domain, self, ...) \ G_STMT_START { \ @@ -91,11 +138,7 @@ } \ } G_STMT_END -#define trace(...) _LOG (LOGL_TRACE, _NMLOG_DOMAIN, NULL, __VA_ARGS__) -#define debug(...) _LOG (LOGL_DEBUG, _NMLOG_DOMAIN, NULL, __VA_ARGS__) -#define info(...) _LOG (LOGL_INFO, _NMLOG_DOMAIN, NULL, __VA_ARGS__) -#define warning(...) _LOG (LOGL_WARN , _NMLOG_DOMAIN, NULL, __VA_ARGS__) -#define error(...) _LOG (LOGL_ERR , _NMLOG_DOMAIN, NULL, __VA_ARGS__) +#define LOG_FMT_IP_TUNNEL "adding %s '%s' parent %u local %s remote %s" /****************************************************************** * Forward declarations and enums @@ -111,6 +154,7 @@ typedef enum { DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 5), DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 6), DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 7), + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = (1LL << 8), __DELAYED_ACTION_TYPE_MAX, DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | @@ -122,494 +166,108 @@ typedef enum { DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, } DelayedActionType; -static gboolean tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props); +typedef enum { + /* Negative values are errors from kernel. Add dummy member to + * make enum signed. */ + _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = -1, + + WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0, + WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK, + WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING, +} WaitForNlResponseResult; + +typedef void (*WaitForNlResponseCallback) (NMPlatform *platform, + guint32 seq_number, + WaitForNlResponseResult seq_result, + gpointer user_data); + 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 do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name); +static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type); static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); -static 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 -{ - void *handle; - void *handle_route; - - int (*f_nl_has_capability) (int capability); - int (*f_rtnl_link_get_link_netnsid) (const struct rtnl_link *link, gint32 *out_link_netnsid); -}; - -static int -_nl_f_nl_has_capability (int capability) -{ - return FALSE; -} - -static const struct libnl_vtable * -_nl_get_vtable (void) -{ - static struct libnl_vtable vtable; - - if (G_UNLIKELY (!vtable.f_nl_has_capability)) { - vtable.handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD); - if (vtable.handle) { - vtable.f_nl_has_capability = dlsym (vtable.handle, "nl_has_capability"); - } - vtable.handle_route = dlopen ("libnl-route-3.so.200", RTLD_LAZY | RTLD_NOLOAD); - if (vtable.handle_route) { - vtable.f_rtnl_link_get_link_netnsid = dlsym (vtable.handle_route, "rtnl_link_get_link_netnsid"); - } - - if (!vtable.f_nl_has_capability) - vtable.f_nl_has_capability = &_nl_f_nl_has_capability; - - trace ("libnl: rtnl_link_get_link_netnsid() %s", vtable.f_rtnl_link_get_link_netnsid ? "supported" : "not supported"); - - g_return_val_if_fail (vtable.handle, &vtable); - g_return_val_if_fail (vtable.handle_route, &vtable); - } - - return &vtable; -} - -static gboolean -_nl_has_capability (int capability) -{ - return (_nl_get_vtable ()->f_nl_has_capability) (capability); -} - -static int -_rtnl_link_get_link_netnsid (const struct rtnl_link *link, gint32 *out_link_netnsid) -{ - const struct libnl_vtable *vtable; - - g_return_val_if_fail (link, -NLE_INVAL); - g_return_val_if_fail (out_link_netnsid, -NLE_INVAL); - - vtable = _nl_get_vtable (); - return vtable->f_rtnl_link_get_link_netnsid - ? vtable->f_rtnl_link_get_link_netnsid (link, out_link_netnsid) - : -NLE_OPNOTSUPP; -} - -gboolean -nm_platform_check_support_libnl_link_netnsid (void) -{ - return !!(_nl_get_vtable ()->f_rtnl_link_get_link_netnsid); -} - -/* Automatic deallocation of local variables */ -#define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object))) -static void -_nl_auto_nl_object (void *ptr) -{ - struct nl_object **object = ptr; - - if (object && *object) { - nl_object_put (*object); - *object = NULL; - } -} - -#define auto_nl_addr __attribute__((cleanup(_nl_auto_nl_addr))) -static void -_nl_auto_nl_addr (void *ptr) -{ - struct nl_addr **object = ptr; - - if (object && *object) { - nl_addr_put (*object); - *object = NULL; - } -} - -/* wrap the libnl alloc functions and abort on out-of-memory*/ - -static struct nl_addr * -_nl_addr_build (int family, const void *buf, size_t size) -{ - struct nl_addr *addr; - - addr = nl_addr_build (family, (void *) buf, size); - if (!addr) - g_error ("nl_addr_build() failed with out of memory"); - - return addr; -} +static void cache_prune_candidates_prune (NMPlatform *platform); +static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); -static struct rtnl_link * -_nl_rtnl_link_alloc (int ifindex, const char*name) -{ - struct rtnl_link *rtnllink; - - rtnllink = rtnl_link_alloc (); - if (!rtnllink) - g_error ("rtnl_link_alloc() failed with out of memory"); - - if (ifindex > 0) - rtnl_link_set_ifindex (rtnllink, ifindex); - if (name) - rtnl_link_set_name (rtnllink, name); - return rtnllink; -} - -static struct rtnl_addr * -_nl_rtnl_addr_alloc (int ifindex) -{ - struct rtnl_addr *rtnladdr; - - rtnladdr = rtnl_addr_alloc (); - if (!rtnladdr) - g_error ("rtnl_addr_alloc() failed with out of memory"); - if (ifindex > 0) - rtnl_addr_set_ifindex (rtnladdr, ifindex); - return rtnladdr; -} - -static struct rtnl_route * -_nl_rtnl_route_alloc (void) -{ - struct rtnl_route *rtnlroute = rtnl_route_alloc (); - - if (!rtnlroute) - g_error ("rtnl_route_alloc() failed with out of memory"); - return rtnlroute; -} - -static struct rtnl_nexthop * -_nl_rtnl_route_nh_alloc (void) -{ - struct rtnl_nexthop *nexthop; - - nexthop = rtnl_route_nh_alloc (); - if (!nexthop) - g_error ("rtnl_route_nh_alloc () failed with out of memory"); - return nexthop; -} - -/* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */ -static void -_nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) -{ - struct nl_addr *nladdr; - - rtnl_addr_set_prefixlen (rtnladdr, plen); - - nladdr = rtnl_addr_get_local (rtnladdr); - if (nladdr) - nl_addr_set_prefixlen (nladdr, plen); -} +/*****************************************************************************/ static const char * -_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size) { - const char *str_type = NULL; + char *buf0 = buf; - switch (type) { - case RTM_NEWLINK: str_type = "NEWLINK"; break; - case RTM_DELLINK: str_type = "DELLINK"; break; - case RTM_NEWADDR: str_type = "NEWADDR"; break; - case RTM_DELADDR: str_type = "DELADDR"; break; - case RTM_NEWROUTE: str_type = "NEWROUTE"; break; - case RTM_DELROUTE: str_type = "DELROUTE"; break; + switch (seq_result) { + case WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN: + nm_utils_strbuf_append_str (&buf, &buf_size, "unknown"); + break; + case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK: + nm_utils_strbuf_append_str (&buf, &buf_size, "success"); + break; + case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN: + nm_utils_strbuf_append_str (&buf, &buf_size, "failure"); + break; + default: + if (seq_result < 0) + nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s)", -((int) seq_result), g_strerror (-((int) seq_result))); + else + nm_utils_strbuf_append (&buf, &buf_size, "internal failure %d", (int) seq_result); + break; } - if (str_type) - g_strlcpy (buf, str_type, len); - else - g_snprintf (buf, len, "(%d)", type); - return buf; + return buf0; } -/******************************************************************/ - -/* _nl_link_parse_info_data(): Re-fetches a link from the kernel - * and parses its IFLA_INFO_DATA using a caller-provided parser. - * - * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser(). - */ - -typedef int (*NMNLInfoDataParser) (struct nlattr *info_data, gpointer parser_data); - -typedef struct { - NMNLInfoDataParser parser; - gpointer parser_data; -} NMNLInfoDataClosure; - -static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = { - [IFLA_LINKINFO] = { .type = NLA_NESTED }, -}; - -static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { - [IFLA_INFO_DATA] = { .type = NLA_NESTED }, -}; - -static int -_nl_link_parse_info_data_cb (struct nl_msg *msg, void *arg) -{ - NMNLInfoDataClosure *closure = arg; - struct nlmsghdr *n = nlmsg_hdr (msg); - struct nlattr *tb[IFLA_MAX + 1]; - struct nlattr *li[IFLA_INFO_MAX + 1]; - int err; - - if (!nlmsg_valid_hdr (n, sizeof (struct ifinfomsg))) - return -NLE_MSG_TOOSHORT; - - err = nlmsg_parse (n, sizeof (struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy); - if (err < 0) - return err; - - if (!tb[IFLA_LINKINFO]) - return -NLE_MISSING_ATTR; - - err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy); - if (err < 0) - return err; - - if (!li[IFLA_INFO_DATA]) - return -NLE_MISSING_ATTR; - - return closure->parser (li[IFLA_INFO_DATA], closure->parser_data); -} - -static int -_nl_link_parse_info_data (struct nl_sock *sk, int ifindex, - NMNLInfoDataParser parser, gpointer parser_data) -{ - NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; - struct nl_msg *msg = NULL; - struct nl_cb *cb; - struct nl_cb *cb0; - int err; - - err = rtnl_link_build_get_request (ifindex, NULL, &msg); - if (err < 0) - return err; - - err = nl_send_auto (sk, msg); - nlmsg_free (msg); - if (err < 0) - return err; - - cb0 = nl_socket_get_cb (sk); - cb = nl_cb_clone (cb0); - nl_cb_put (cb0); - if (cb == NULL) - return -NLE_NOMEM; - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, _nl_link_parse_info_data_cb, &data); - - err = nl_recvmsgs (sk, cb); - nl_cb_put (cb); - if (err < 0) - return err; - - nl_wait_for_ack (sk); - return 0; -} - -/******************************************************************/ - -static int -_nl_sock_flush_data (struct nl_sock *sk) -{ - int nle; - struct nl_cb *cb; - struct nl_cb *cb0; - - cb0 = nl_socket_get_cb (sk); - cb = nl_cb_clone (cb0); - nl_cb_put (cb0); - 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) -{ - guint32 seq; - - /* choose our own sequence number, because libnl does not ensure that - * it isn't zero -- which would confuse our checking for outstanding - * messages. */ - seq = nl_socket_use_seq (sk); - if (seq == 0) - seq = nl_socket_use_seq (sk); - - nlmsg_hdr (msg)->nlmsg_seq = seq; - if (out_seq) - *out_seq = seq; -} - -static int -_nl_sock_request_link (NMPlatform *platform, struct nl_sock *sk, int ifindex, const char *name, guint32 *out_seq) -{ - struct nl_msg *msg = NULL; - int err; - - if (name && !name[0]) - name = NULL; - - g_return_val_if_fail (ifindex > 0 || name, -NLE_INVAL); - - _LOGt ("sock: request-link %d%s%s%s", ifindex, name ? ", \"" : "", name ? name : "", name ? "\"" : ""); - - if ((err = rtnl_link_build_get_request (ifindex, name, &msg)) < 0) - return err; - - _nl_msg_set_seq (sk, msg, out_seq); - - err = nl_send_auto (sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return 0; -} - -static int -_nl_sock_request_all (NMPlatform *platform, struct nl_sock *sk, NMPObjectType obj_type, guint32 *out_seq) -{ - const NMPClass *klass; - struct rtgenmsg gmsg = { 0 }; - struct nl_msg *msg; - int err; - - klass = nmp_class_from_type (obj_type); - - _LOGt ("sock: request-all-%s", klass->obj_type_name); - - /* reimplement - * nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP); - * because we need the sequence number. - */ - msg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); - if (!msg) - return -NLE_NOMEM; - - gmsg.rtgen_family = klass->addr_family; - err = nlmsg_append (msg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO); - if (err < 0) - goto errout; - - _nl_msg_set_seq (sk, msg, out_seq); - - err = nl_send_auto (sk, msg); -errout: - nlmsg_free(msg); - - return err >= 0 ? 0 : err; -} - -/******************************************************************/ +/****************************************************************** + * Support IFLA_INET6_ADDR_GEN_MODE + ******************************************************************/ -#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 + _LOG2W ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "failed to detect; assume no support"); + return FALSE; + } + return _support_user_ipv6ll > 0; - return FALSE; } static void -_support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) +_support_user_ipv6ll_detect (struct nlattr **tb) { -#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) { + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { _support_user_ipv6ll = 1; - nm_log_dbg (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "detected"); + _LOG2D ("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"); + _LOG2D ("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 -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; +/****************************************************************** + * Various utilities + ******************************************************************/ - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; +const NMIPAddr nm_ip_addr_zero = NMIPAddrInit; - msg_hdr = nlmsg_hdr (msg); - if (msg_hdr->nlmsg_type != RTM_NEWADDR) - return; - - /* the extended address flags are only set for AF_INET6 */ - if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) - return; - - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE (they were added together). - **/ - _support_kernel_extended_ifa_flags = - nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */) - ? 1 : -1; -} +#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) +#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) static gboolean -_support_kernel_extended_ifa_flags_get (void) +ip4_address_is_link_local (in_addr_t addr) { - 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; + return (addr & IPV4LL_NETMASK) == IPV4LL_NETWORK; } -/****************************************************************** - * Object type specific utilities - ******************************************************************/ - static guint _nm_ip_config_source_to_rtprot (NMIPConfigSource source) { @@ -649,7 +307,46 @@ _nm_ip_config_source_from_rtprot (guint rtprot) } } -/******************************************************************/ +static void +clear_host_address (int family, const void *network, int plen, void *dst) +{ + g_return_if_fail (plen == (guint8)plen); + g_return_if_fail (network); + + switch (family) { + case AF_INET: + *((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen); + break; + case AF_INET6: + nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen); + break; + default: + g_assert_not_reached (); + } +} + +static int +_vlan_qos_mapping_cmp_from (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const NMVlanQosMapping *map_a = a; + const NMVlanQosMapping *map_b = b; + + if (map_a->from != map_b->from) + return map_a->from < map_b->from ? -1 : 1; + return 0; +} + +static int +_vlan_qos_mapping_cmp_from_ptr (gconstpointer a, gconstpointer b, gpointer user_data) +{ + return _vlan_qos_mapping_cmp_from (*((const NMVlanQosMapping **) a), + *((const NMVlanQosMapping **) b), + NULL); +} + +/****************************************************************** + * NMLinkType functions + ******************************************************************/ typedef struct { const NMLinkType nm_type; @@ -687,10 +384,13 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, { NM_LINK_TYPE_IFB, "ifb", "ifb", NULL }, + { NM_LINK_TYPE_IP6TNL, "ip6tnl", "ip6tnl", NULL }, + { NM_LINK_TYPE_IPIP, "ipip", "ipip", 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_SIT, "sit", "sit", NULL }, { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, @@ -728,125 +428,172 @@ nm_link_type_to_string (NMLinkType type) } /****************************************************************** - * NMPlatform types and functions + * Utilities ******************************************************************/ -typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; - -struct _NMLinuxPlatformPrivate { - struct nl_sock *nlh; - struct nl_sock *nlh_event; - guint32 nlh_seq_expect; - guint32 nlh_seq_last; - NMPCache *cache; - GIOChannel *event_channel; - guint event_id; - - GUdevClient *udev_client; - - struct { - DelayedActionType flags; - GPtrArray *list_master_connected; - GPtrArray *list_refresh_link; - gint is_handling; - guint idle_id; - } delayed_action; - - GHashTable *prune_candidates; +/* _timestamp_nl_to_ms: + * @timestamp_nl: a timestamp from ifa_cacheinfo. + * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current + * uptime and how often timestamp_nl wrapped. + * + * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds. + * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts + * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However, + * the uint32 counter wraps every 497 days of uptime, so we have to compensate + * for that. */ +static gint64 +_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) +{ + const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); + gint64 timestamp_nl_ms; - GHashTable *wifi_data; -}; + /* convert timestamp from 1/100th of a second to msec. */ + timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); -static inline NMLinuxPlatformPrivate * -NM_LINUX_PLATFORM_GET_PRIVATE (const void *self) -{ - nm_assert (NM_IS_LINUX_PLATFORM (self)); + /* timestamp wraps every 497 days. Try to compensate for that.*/ + if (timestamp_nl_ms > monotonic_ms) { + /* timestamp_nl_ms is in the future. Truncate it to *now* */ + timestamp_nl_ms = monotonic_ms; + } else if (monotonic_ms >= WRAP_INTERVAL) { + timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; + if (timestamp_nl_ms > monotonic_ms) + timestamp_nl_ms -= WRAP_INTERVAL; + } - return ((NMLinuxPlatform *) self)->priv; + return timestamp_nl_ms; } -G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) - -void -nm_linux_platform_setup (void) +static guint32 +_addrtime_timestamp_to_nm (guint32 timestamp, gint32 *out_now_nm) { - g_object_new (NM_TYPE_LINUX_PLATFORM, - NM_PLATFORM_REGISTER_SINGLETON, TRUE, - NULL); -} + struct timespec tp; + gint64 now_nl, now_nm, result; + int err; -/******************************************************************/ + /* timestamp is unset. Default to 1. */ + if (!timestamp) { + if (out_now_nm) + *out_now_nm = 0; + return 1; + } -NMPObjectType -_nlo_get_object_type (const struct nl_object *object) -{ - const char *type_str; + /* do all the calculations in milliseconds scale */ - if (!object || !(type_str = nl_object_get_type (object))) - return NMP_OBJECT_TYPE_UNKNOWN; + err = clock_gettime (CLOCK_MONOTONIC, &tp); + g_assert (err == 0); + now_nm = nm_utils_get_monotonic_timestamp_ms (); + now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + + (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); - 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; -} + result = now_nm - (now_nl - _timestamp_nl_to_ms (timestamp, now_nl)); -/******************************************************************/ + if (out_now_nm) + *out_now_nm = now_nm / 1000; -static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + /* converting the timestamp into nm_utils_get_monotonic_timestamp_ms() scale is + * a good guess but fails in the following situations: + * + * - If the address existed before start of the process, the timestamp in nm scale would + * be negative or zero. In this case we default to 1. + * - during hibernation, the CLOCK_MONOTONIC/timestamp drifts from + * nm_utils_get_monotonic_timestamp_ms() scale. + */ + if (result <= 1000) + return 1; - return _support_kernel_extended_ifa_flags_get (); + if (result > now_nm) + return now_nm / 1000; + + return result / 1000; } -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) +static guint32 +_addrtime_extend_lifetime (guint32 lifetime, guint32 seconds) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + guint64 v; - return _support_user_ipv6ll_get (); + if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + || seconds == 0) + return lifetime; + + v = (guint64) lifetime + (guint64) seconds; + return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); } +/* The rtnl_addr object contains relative lifetimes @valid and @preferred + * that count in seconds, starting from the moment when the kernel constructed + * the netlink message. + * + * There is also a field rtnl_addr_last_update_time(), which is the absolute + * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address + * was modified (wrapping every 497 days). + * Immediately at the time when the address was last modified, #NOW and @last_update_time + * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. + * However, this is not true in general. As time goes by, whenever kernel sends a new address + * via netlink, the lifetimes keep counting down. + **/ static void -process_events (NMPlatform *platform) +_addrtime_get_lifetimes (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 *out_timestamp, + guint32 *out_lifetime, + guint32 *out_preferred) { - delayed_action_handle_all (platform, TRUE); + gint32 now; + + if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT + || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + if (preferred > lifetime) + preferred = lifetime; + timestamp = _addrtime_timestamp_to_nm (timestamp, &now); + + if (now == 0) { + /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ + nm_assert (timestamp == 1); + now = nm_utils_get_monotonic_timestamp_s (); + } + if (timestamp < now) { + guint32 diff = now - timestamp; + + lifetime = _addrtime_extend_lifetime (lifetime, diff); + preferred = _addrtime_extend_lifetime (preferred, diff); + } else + nm_assert (timestamp == now); + } else + timestamp = 0; + *out_timestamp = timestamp; + *out_lifetime = lifetime; + *out_preferred = preferred; } /******************************************************************/ -#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)) +static const NMPObject * +_lookup_cached_link (const NMPCache *cache, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) +{ + const NMPObject *obj; + nm_assert (completed_from_cache && link_cached); + + if (!*completed_from_cache) { + obj = ifindex > 0 && cache ? nmp_cache_lookup_link (cache, ifindex) : NULL; + + if (obj && !obj->_link.netlink.is_in_netlink) + *link_cached = obj; + else + *link_cached = NULL; + *completed_from_cache = TRUE; + } + return *link_cached; +} /******************************************************************/ #define DEVTYPE_PREFIX "DEVTYPE=" static char * -read_devtype (const char *sysfs_path) +_linktype_read_devtype (const char *sysfs_path) { gs_free char *uevent = g_strdup_printf ("%s/uevent", sysfs_path); char *contents = NULL; @@ -868,69 +615,63 @@ read_devtype (const char *sysfs_path) return NULL; } -static const NMPObject * -_lookup_link_cached (NMPlatform *platform, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) -{ - const NMPObject *obj; - - nm_assert (completed_from_cache && link_cached); - - if (!*completed_from_cache) { - obj = ifindex > 0 ? nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex) : NULL; - - 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, gboolean *completed_from_cache, const NMPObject **link_cached, const char **out_kind) -{ - const char *rtnl_type, *ifname; - int i, arptype; - - 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) { +_linktype_get_type (NMPlatform *platform, + const NMPCache *cache, + const char *kind, + int ifindex, + const char *ifname, + unsigned flags, + unsigned arptype, + gboolean *completed_from_cache, + const NMPObject **link_cached, + const char **out_kind) +{ + guint i; + + if (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); + obj = _lookup_cached_link (cache, ifindex, completed_from_cache, link_cached); + + /* If we detected the link type before, we stick to that + * decision unless the "kind" changed. + * + * This way, we save edditional ethtool/sysctl lookups, but moreover, + * we keep the linktype stable and don't change it as long as the link + * exists. + * + * Note that kernel *can* reuse the ifindex (on integer overflow, and + * when moving interfce to other netns). Thus here there is a tiny potential + * of messing stuff up. */ + if ( obj + && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) + && ( !kind + || !g_strcmp0 (kind, obj->link.kind))) { + nm_assert (obj->link.kind == g_intern_string (obj->link.kind)); + *out_kind = obj->link.kind; + return obj->link.type; } } - if (out_kind) - *out_kind = rtnl_type; - if (rtnl_type) { + + *out_kind = g_intern_string (kind); + + if (kind) { for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { - if (g_strcmp0 (rtnl_type, linktypes[i].rtnl_type) == 0) + if (g_strcmp0 (kind, linktypes[i].rtnl_type) == 0) return linktypes[i].nm_type; } - if (!strcmp (rtnl_type, "tun")) { + if (!strcmp (kind, "tun")) { NMPlatformTunProperties props; - guint flags; - if (tun_get_properties_ifname (platform, rtnl_link_get_name (rtnllink), &props)) { + if ( platform + && nm_platform_link_tun_get_properties_ifname (platform, ifname, &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) @@ -939,13 +680,15 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *c } } - 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; + else if (arptype == ARPHRD_SIT) + return NM_LINK_TYPE_SIT; + else if (arptype == ARPHRD_TUNNEL6) + return NM_LINK_TYPE_IP6TNL; - ifname = rtnl_link_get_name (rtnllink); if (ifname) { gs_free char *driver = NULL; gs_free char *sysfs_path = NULL; @@ -971,7 +714,7 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *c if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS)) return NM_LINK_TYPE_OLPC_MESH; - devtype = read_devtype (sysfs_path); + devtype = _linktype_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) { @@ -994,366 +737,1138 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *c * prevent future virtual network drivers from being treated as Ethernet * when they should be Generic instead. */ - if (arptype == ARPHRD_ETHER && !rtnl_type && !devtype) + if (arptype == ARPHRD_ETHER && !kind && !devtype) return NM_LINK_TYPE_ETHERNET; } return NM_LINK_TYPE_UNKNOWN; } -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) +/****************************************************************** + * libnl unility functions and wrappers + ******************************************************************/ + +#define nm_auto_nlmsg __attribute__((cleanup(_nm_auto_nl_msg_cleanup))) +static void +_nm_auto_nl_msg_cleanup (void *ptr) { - 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; - int parent; + nlmsg_free (*((struct nl_msg **) ptr)); +} - nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); +static const char * +_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +{ + const char *str_type = NULL; - 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)); + switch (type) { + case RTM_NEWLINK: str_type = "NEWLINK"; break; + case RTM_DELLINK: str_type = "DELLINK"; break; + case RTM_NEWADDR: str_type = "NEWADDR"; break; + case RTM_DELADDR: str_type = "DELADDR"; break; + case RTM_NEWROUTE: str_type = "NEWROUTE"; break; + case RTM_DELROUTE: str_type = "DELROUTE"; break; + } + if (str_type) + g_strlcpy (buf, str_type, len); + else + g_snprintf (buf, len, "(%d)", type); + return buf; +} - obj->ifindex = rtnl_link_get_ifindex (nlo); +/****************************************************************** + * NMPObject/netlink functions + ******************************************************************/ - if (id_only) - return TRUE; +#define _check_addr_or_errout(tb, attr, addr_len) \ + ({ \ + const struct nlattr *__t = (tb)[(attr)]; \ + \ + if (__t) { \ + if (nla_len (__t) != (addr_len)) { \ + goto errout; \ + } \ + } \ + !!__t; \ + }) - 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); - parent = rtnl_link_get_link (nlo); - if (parent > 0) { - gint32 link_netnsid; - - if (_rtnl_link_get_link_netnsid (nlo, &link_netnsid) == 0) - obj->parent = NM_PLATFORM_LINK_OTHER_NETNS; - else - obj->parent = parent; - } - 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 ((nladdr = rtnl_link_get_addr (nlo))) { - unsigned int l = 0; +/* Copied and heavily modified from libnl3's inet6_parse_protinfo(). */ +static gboolean +_parse_af_inet6 (NMPlatform *platform, + struct nlattr *attr, + NMUtilsIPv6IfaceId *out_iid, + guint8 *out_iid_is_valid, + guint8 *out_addr_gen_mode_inv) +{ + static struct nla_policy policy[IFLA_INET6_MAX+1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, + [IFLA_INET6_CACHEINFO] = { .minlen = sizeof(struct ifla_cacheinfo) }, + [IFLA_INET6_CONF] = { .minlen = 4 }, + [IFLA_INET6_STATS] = { .minlen = 8 }, + [IFLA_INET6_ICMP6STATS] = { .minlen = 8 }, + [IFLA_INET6_TOKEN] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_INET6_MAX+1]; + int err; + struct in6_addr i6_token; + gboolean iid_is_valid = FALSE; + guint8 i6_addr_gen_mode_inv = 0; + gboolean success = FALSE; - 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; - } - } + err = nla_parse_nested (tb, IFLA_INET6_MAX, attr, policy); + if (err < 0) + goto errout; -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (_support_user_ipv6ll_get ()) { - guint8 mode = 0; + if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) + goto errout; + if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) + goto errout; + if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) + goto errout; - if (rtnl_link_inet6_get_addr_gen_mode (nlo, &mode) == 0) - obj->inet6_addr_gen_mode_inv = _nm_platform_uint8_inv (mode); + if (_check_addr_or_errout (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr))) { + nla_memcpy (&i6_token, tb[IFLA_INET6_TOKEN], sizeof (struct in6_addr)); + if (!IN6_IS_ADDR_UNSPECIFIED (&i6_token)) + iid_is_valid = TRUE; } -#endif -#if HAVE_LIBNL_INET6_TOKEN - if ((rtnl_link_inet6_get_token (nlo, &nladdr)) == 0) { - if ( nl_addr_get_family (nladdr) == AF_INET6 - && nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) { - struct in6_addr *addr; - NMUtilsIPv6IfaceId *iid = &obj->inet6_token.iid; - - addr = nl_addr_get_binary_addr (nladdr); - iid->id_u8[7] = addr->s6_addr[15]; - iid->id_u8[6] = addr->s6_addr[14]; - iid->id_u8[5] = addr->s6_addr[13]; - iid->id_u8[4] = addr->s6_addr[12]; - iid->id_u8[3] = addr->s6_addr[11]; - iid->id_u8[2] = addr->s6_addr[10]; - iid->id_u8[1] = addr->s6_addr[9]; - iid->id_u8[0] = addr->s6_addr[8]; - obj->inet6_token.is_valid = TRUE; + /* Hack to detect support addrgenmode of the kernel. We only parse + * netlink messages that we receive from kernel, hence this check + * is valid. */ + _support_user_ipv6ll_detect (tb); + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { + i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); + if (i6_addr_gen_mode_inv == 0) { + /* an inverse addrgenmode of zero is unexpected. We need to reserve zero + * to signal "unset". */ + goto errout; } - nl_addr_put (nladdr); } -#endif - obj_priv->netlink.is_in_netlink = TRUE; + success = TRUE; + if (iid_is_valid) { + out_iid->id_u8[7] = i6_token.s6_addr[15]; + out_iid->id_u8[6] = i6_token.s6_addr[14]; + out_iid->id_u8[5] = i6_token.s6_addr[13]; + out_iid->id_u8[4] = i6_token.s6_addr[12]; + out_iid->id_u8[3] = i6_token.s6_addr[11]; + out_iid->id_u8[2] = i6_token.s6_addr[10]; + out_iid->id_u8[1] = i6_token.s6_addr[9]; + out_iid->id_u8[0] = i6_token.s6_addr[8]; + *out_iid_is_valid = TRUE; + } + *out_addr_gen_mode_inv = i6_addr_gen_mode_inv; +errout: + return success; +} - return TRUE; +/*****************************************************************************/ + +static NMPObject * +_parse_lnk_gre (const char *kind, struct nlattr *info_data) +{ + static struct nla_policy policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, + [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_TOS] = { .type = NLA_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_GRE_MAX + 1]; + int err; + NMPObject *obj; + NMPlatformLnkGre *props; + + if (!info_data || g_strcmp0 (kind, "gre")) + return NULL; + + err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy); + if (err < 0) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_GRE, NULL); + props = &obj->lnk_gre; + + props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0; + props->input_flags = tb[IFLA_GRE_IFLAGS] ? ntohs (nla_get_u16 (tb[IFLA_GRE_IFLAGS])) : 0; + props->output_flags = tb[IFLA_GRE_OFLAGS] ? ntohs (nla_get_u16 (tb[IFLA_GRE_OFLAGS])) : 0; + props->input_key = tb[IFLA_GRE_IKEY] ? ntohl (nla_get_u32 (tb[IFLA_GRE_IKEY])) : 0; + props->output_key = tb[IFLA_GRE_OKEY] ? ntohl (nla_get_u32 (tb[IFLA_GRE_OKEY])) : 0; + props->local = tb[IFLA_GRE_LOCAL] ? nla_get_u32 (tb[IFLA_GRE_LOCAL]) : 0; + props->remote = tb[IFLA_GRE_REMOTE] ? nla_get_u32 (tb[IFLA_GRE_REMOTE]) : 0; + props->tos = tb[IFLA_GRE_TOS] ? nla_get_u8 (tb[IFLA_GRE_TOS]) : 0; + props->ttl = tb[IFLA_GRE_TTL] ? nla_get_u8 (tb[IFLA_GRE_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_GRE_PMTUDISC] || !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]); + + return obj; } -/* _timestamp_nl_to_ms: - * @timestamp_nl: a timestamp from ifa_cacheinfo. - * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current - * uptime and how often timestamp_nl wrapped. - * - * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds. - * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts - * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However, - * the uint32 counter wraps every 497 days of uptime, so we have to compensate - * for that. */ -static gint64 -_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) +/*****************************************************************************/ + +/* 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 NMPObject * +_parse_lnk_infiniband (const char *kind, struct nlattr *info_data) { - const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); - gint64 timestamp_nl_ms; + static struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { + [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, + [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, + [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, + }; + struct nlattr *tb[IFLA_IPOIB_MAX + 1]; + NMPlatformLnkInfiniband *info; + NMPObject *obj; + int err; + const char *mode; - /* convert timestamp from 1/100th of a second to msec. */ - timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); + if (!info_data || g_strcmp0 (kind, "ipoib")) + return NULL; - /* timestamp wraps every 497 days. Try to compensate for that.*/ - if (timestamp_nl_ms > monotonic_ms) { - /* timestamp_nl_ms is in the future. Truncate it to *now* */ - timestamp_nl_ms = monotonic_ms; - } else if (monotonic_ms >= WRAP_INTERVAL) { - timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; - if (timestamp_nl_ms > monotonic_ms) - timestamp_nl_ms -= WRAP_INTERVAL; + err = nla_parse_nested (tb, IFLA_IPOIB_MAX, info_data, policy); + if (err < 0) + return NULL; + + if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) + return NULL; + + switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) { + case IPOIB_MODE_DATAGRAM: + mode = "datagram"; + break; + case IPOIB_MODE_CONNECTED: + mode = "connected"; + break; + default: + return NULL; } - return timestamp_nl_ms; + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); + info = &obj->lnk_infiniband; + + info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); + info->mode = mode; + + return obj; } -static guint32 -_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr, gint32 *out_now_nm) +/*****************************************************************************/ + +static NMPObject * +_parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) { - guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr); - struct timespec tp; - gint64 now_nl, now_nm, result; + static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct in6_addr)}, + [IFLA_IPTUN_REMOTE] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct in6_addr)}, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; int err; + NMPObject *obj; + NMPlatformLnkIp6Tnl *props; + guint32 flowinfo; - /* timestamp is unset. Default to 1. */ - if (!last_update_time) { - if (out_now_nm) - *out_now_nm = 0; - return 1; + if (!info_data || g_strcmp0 (kind, "ip6tnl")) + return NULL; + + err = nla_parse_nested (tb, IFLA_IPTUN_MAX, info_data, policy); + if (err < 0) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_IP6TNL, NULL); + props = &obj->lnk_ip6tnl; + + if (tb[IFLA_IPTUN_LINK]) + props->parent_ifindex = nla_get_u32 (tb[IFLA_IPTUN_LINK]); + if (tb[IFLA_IPTUN_LOCAL]) + memcpy (&props->local, nla_data (tb[IFLA_IPTUN_LOCAL]), sizeof (props->local)); + if (tb[IFLA_IPTUN_REMOTE]) + memcpy (&props->remote, nla_data (tb[IFLA_IPTUN_REMOTE]), sizeof (props->remote)); + if (tb[IFLA_IPTUN_TTL]) + props->ttl = nla_get_u8 (tb[IFLA_IPTUN_TTL]); + if (tb[IFLA_IPTUN_ENCAP_LIMIT]) + props->encap_limit = nla_get_u8 (tb[IFLA_IPTUN_ENCAP_LIMIT]); + if (tb[IFLA_IPTUN_FLOWINFO]) { + flowinfo = ntohl (nla_get_u32 (tb[IFLA_IPTUN_FLOWINFO])); + props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; + props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; } + if (tb[IFLA_IPTUN_PROTO]) + props->proto = nla_get_u8 (tb[IFLA_IPTUN_PROTO]); - /* do all the calculations in milliseconds scale */ + return obj; +} - err = clock_gettime (CLOCK_MONOTONIC, &tp); - g_assert (err == 0); - now_nm = nm_utils_get_monotonic_timestamp_ms (); - now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); +/*****************************************************************************/ - result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl)); +static NMPObject * +_parse_lnk_ipip (const char *kind, struct nlattr *info_data) +{ + static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + int err; + NMPObject *obj; + NMPlatformLnkIpIp *props; - if (out_now_nm) - *out_now_nm = now_nm / 1000; + if (!info_data || g_strcmp0 (kind, "ipip")) + return NULL; - /* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is - * a good guess but fails in the following situations: - * - * - If the address existed before start of the process, the timestamp in nm scale would - * be negative or zero. In this case we default to 1. - * - during hibernation, the CLOCK_MONOTONIC/last_update_time drifts from - * nm_utils_get_monotonic_timestamp_ms() scale. - */ - if (result <= 1000) - return 1; + err = nla_parse_nested (tb, IFLA_IPTUN_MAX, info_data, policy); + if (err < 0) + return NULL; - if (result > now_nm) - return now_nm / 1000; + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_IPIP, NULL); + props = &obj->lnk_ipip; - return result / 1000; + props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32 (tb[IFLA_IPTUN_LINK]) : 0; + props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32 (tb[IFLA_IPTUN_LOCAL]) : 0; + props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32 (tb[IFLA_IPTUN_REMOTE]) : 0; + props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8 (tb[IFLA_IPTUN_TOS]) : 0; + props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8 (tb[IFLA_IPTUN_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8 (tb[IFLA_IPTUN_PMTUDISC]); + + return obj; } -static guint32 -_extend_lifetime (guint32 lifetime, guint32 seconds) +/*****************************************************************************/ + +static NMPObject * +_parse_lnk_macvlan (const char *kind, struct nlattr *info_data) { - guint64 v; + static struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, + }; + NMPlatformLnkMacvlan *props; + struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; + int err; + NMPObject *obj; + gboolean tap; - if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT - || seconds == 0) - return lifetime; + if (!info_data) + return NULL; - v = (guint64) lifetime + (guint64) seconds; - return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); + if (!g_strcmp0 (kind, "macvlan")) + tap = FALSE; + else if (!g_strcmp0 (kind, "macvtap")) + tap = TRUE; + else + return NULL; + + err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data, policy); + if (err < 0) + return NULL; + + if (!tb[IFLA_MACVLAN_MODE]) + return NULL; + + obj = nmp_object_new (tap ? NMP_OBJECT_TYPE_LNK_MACVTAP : NMP_OBJECT_TYPE_LNK_MACVLAN, NULL); + props = &obj->lnk_macvlan; + props->mode = nla_get_u32 (tb[IFLA_MACVLAN_MODE]); + props->tap = tap; + + if (tb[IFLA_MACVLAN_FLAGS]) + props->no_promisc = NM_FLAGS_HAS (nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC); + + return obj; } -/* 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) +/*****************************************************************************/ + +static NMPObject * +_parse_lnk_sit (const char *kind, struct nlattr *info_data) { - 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); + static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + int err; + NMPObject *obj; + NMPlatformLnkSit *props; - 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 (!info_data || g_strcmp0 (kind, "sit")) + return NULL; - 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; + err = nla_parse_nested (tb, IFLA_IPTUN_MAX, info_data, policy); + if (err < 0) + return NULL; - lifetime = _extend_lifetime (lifetime, diff); - preferred = _extend_lifetime (preferred, diff); - } else - nm_assert (timestamp == now); - } - *out_timestamp = timestamp; - *out_lifetime = lifetime; - *out_preferred = preferred; + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_SIT, NULL); + props = &obj->lnk_sit; + + props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32 (tb[IFLA_IPTUN_LINK]) : 0; + props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32 (tb[IFLA_IPTUN_LOCAL]) : 0; + props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32 (tb[IFLA_IPTUN_REMOTE]) : 0; + props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8 (tb[IFLA_IPTUN_TOS]) : 0; + props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8 (tb[IFLA_IPTUN_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8 (tb[IFLA_IPTUN_PMTUDISC]); + props->flags = tb[IFLA_IPTUN_FLAGS] ? nla_get_u16 (tb[IFLA_IPTUN_FLAGS]) : 0; + props->proto = tb[IFLA_IPTUN_PROTO] ? nla_get_u8 (tb[IFLA_IPTUN_PROTO]) : 0; + + return obj; } -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) +/*****************************************************************************/ + +static gboolean +_vlan_qos_mapping_from_nla (struct nlattr *nlattr, + const NMVlanQosMapping **out_map, + guint *out_n_map) { - 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; + struct nlattr *nla; + int remaining; + gs_unref_ptrarray GPtrArray *array = NULL; - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) - g_return_val_if_reached (FALSE); + G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == sizeof (struct ifla_vlan_qos_mapping)); + G_STATIC_ASSERT (sizeof (((NMVlanQosMapping *) 0)->to) == sizeof (((struct ifla_vlan_qos_mapping *) 0)->to)); + G_STATIC_ASSERT (sizeof (((NMVlanQosMapping *) 0)->from) == sizeof (((struct ifla_vlan_qos_mapping *) 0)->from)); + G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == sizeof (((NMVlanQosMapping *) 0)->from) + sizeof (((NMVlanQosMapping *) 0)->to)); - 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)); + nm_assert (out_map && !*out_map); + nm_assert (out_n_map && !*out_n_map); - if (id_only) + if (!nlattr) return TRUE; - obj->source = NM_IP_CONFIG_SOURCE_KERNEL; - _nlo_rtnl_addr_get_lifetimes (nlo, - &obj->timestamp, - &obj->lifetime, - &obj->preferred); - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) - g_warn_if_reached (); - else - memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); + array = g_ptr_array_new (); + nla_for_each_nested (nla, nlattr, remaining) { + if (nla_len (nla) < sizeof(NMVlanQosMapping)) + return FALSE; + g_ptr_array_add (array, nla_data (nla)); + } + + if (array->len > 0) { + NMVlanQosMapping *list; + guint i, j; + + /* The sorting is necessary, because for egress mapping, kernel + * doesn't sent the items strictly sorted by the from field. */ + g_ptr_array_sort_with_data (array, _vlan_qos_mapping_cmp_from_ptr, NULL); + + list = g_new (NMVlanQosMapping, array->len); + + for (i = 0, j = 0; i < array->len; i++) { + NMVlanQosMapping *map; + + map = array->pdata[i]; + + /* kernel doesn't really send us duplicates. Just be extra cautious + * because we want strong guarantees about the sort order and uniqueness + * of our mapping list (for simpler equality comparison). */ + if ( j > 0 + && list[j - 1].from == map->from) + list[j - 1] = *map; + else + list[j++] = *map; + } + + *out_n_map = j; + *out_map = list; } - label = rtnl_addr_get_label (nlo); - /* Check for ':'; we're only interested in labels used as interface aliases */ - if (label && strchr (label, ':')) - g_strlcpy (obj->label, label, sizeof (obj->label)); return TRUE; } -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) +/* Copied and heavily modified from libnl3's vlan_parse() */ +static NMPObject * +_parse_lnk_vlan (const char *kind, struct nlattr *info_data) { - 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); + static struct nla_policy policy[IFLA_VLAN_MAX+1] = { + [IFLA_VLAN_ID] = { .type = NLA_U16 }, + [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, + }; + struct nlattr *tb[IFLA_VLAN_MAX+1]; + int err; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result; - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) - g_return_val_if_reached (FALSE); + if (!info_data || g_strcmp0 (kind, "vlan")) + return NULL; + + if ((err = nla_parse_nested (tb, IFLA_VLAN_MAX, info_data, policy)) < 0) + return NULL; + + if (!tb[IFLA_VLAN_ID]) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + obj->lnk_vlan.id = nla_get_u16 (tb[IFLA_VLAN_ID]); + + if (tb[IFLA_VLAN_FLAGS]) { + struct ifla_vlan_flags flags; + + nla_memcpy (&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); + + obj->lnk_vlan.flags = flags.flags; + } + + if (!_vlan_qos_mapping_from_nla (tb[IFLA_VLAN_INGRESS_QOS], + &obj->_lnk_vlan.ingress_qos_map, + &obj->_lnk_vlan.n_ingress_qos_map)) + return NULL; + + if (!_vlan_qos_mapping_from_nla (tb[IFLA_VLAN_EGRESS_QOS], + &obj->_lnk_vlan.egress_qos_map, + &obj->_lnk_vlan.n_egress_qos_map)) + return NULL; - 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)); + + obj_result = obj; + obj = NULL; + return obj_result; +} + +/*****************************************************************************/ + +/* The installed kernel headers might not have VXLAN stuff at all, or + * they might have the original properties, but not PORT, GROUP6, or LOCAL6. + * So until we depend on kernel >= 3.11, we just ignore the actual enum + * in if_link.h and define the values ourselves. + */ +#define IFLA_VXLAN_UNSPEC 0 +#define IFLA_VXLAN_ID 1 +#define IFLA_VXLAN_GROUP 2 +#define IFLA_VXLAN_LINK 3 +#define IFLA_VXLAN_LOCAL 4 +#define IFLA_VXLAN_TTL 5 +#define IFLA_VXLAN_TOS 6 +#define IFLA_VXLAN_LEARNING 7 +#define IFLA_VXLAN_AGEING 8 +#define IFLA_VXLAN_LIMIT 9 +#define IFLA_VXLAN_PORT_RANGE 10 +#define IFLA_VXLAN_PROXY 11 +#define IFLA_VXLAN_RSC 12 +#define IFLA_VXLAN_L2MISS 13 +#define IFLA_VXLAN_L3MISS 14 +#define IFLA_VXLAN_PORT 15 +#define IFLA_VXLAN_GROUP6 16 +#define IFLA_VXLAN_LOCAL6 17 +#undef IFLA_VXLAN_MAX +#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6 + +/* older kernel header might not contain 'struct ifla_vxlan_port_range'. + * Redefine it. */ +struct nm_ifla_vxlan_port_range { + guint16 low; + guint16 high; +}; + +static NMPObject * +_parse_lnk_vxlan (const char *kind, struct nlattr *info_data) +{ + static struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { + [IFLA_VXLAN_ID] = { .type = NLA_U32 }, + [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, + [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct in6_addr) }, + [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, + [IFLA_VXLAN_LOCAL] = { .type = NLA_U32 }, + [IFLA_VXLAN_LOCAL6] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct in6_addr) }, + [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, + [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, + [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, + [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, + [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct nm_ifla_vxlan_port_range) }, + [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, + [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, + [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, + }; + NMPlatformLnkVxlan *props; + struct nlattr *tb[IFLA_VXLAN_MAX + 1]; + struct nm_ifla_vxlan_port_range *range; + int err; + NMPObject *obj; + + if (!info_data || g_strcmp0 (kind, "vxlan")) + return NULL; + + err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data, policy); + if (err < 0) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + + props = &obj->lnk_vxlan; + + if (tb[IFLA_VXLAN_LINK]) + props->parent_ifindex = nla_get_u32 (tb[IFLA_VXLAN_LINK]); + if (tb[IFLA_VXLAN_ID]) + props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]); + if (tb[IFLA_VXLAN_GROUP]) + props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]); + if (tb[IFLA_VXLAN_LOCAL]) + props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]); + if (tb[IFLA_VXLAN_GROUP6]) + memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6)); + if (tb[IFLA_VXLAN_LOCAL6]) + memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6)); + + if (tb[IFLA_VXLAN_AGEING]) + props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]); + if (tb[IFLA_VXLAN_LIMIT]) + props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]); + if (tb[IFLA_VXLAN_TOS]) + props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]); + if (tb[IFLA_VXLAN_TTL]) + props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]); + + if (tb[IFLA_VXLAN_PORT]) + props->dst_port = ntohs (nla_get_u16 (tb[IFLA_VXLAN_PORT])); + + if (tb[IFLA_VXLAN_PORT_RANGE]) { + range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]); + props->src_port_min = ntohs (range->low); + props->src_port_max = ntohs (range->high); + } + + if (tb[IFLA_VXLAN_LEARNING]) + props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]); + if (tb[IFLA_VXLAN_PROXY]) + props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]); + if (tb[IFLA_VXLAN_RSC]) + props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]); + if (tb[IFLA_VXLAN_L2MISS]) + props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]); + if (tb[IFLA_VXLAN_L3MISS]) + props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]); + + return obj; +} + +/*****************************************************************************/ + +/* Copied and heavily modified from libnl3's link_msg_parser(). */ +static NMPObject * +_new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) +{ + static struct nla_policy policy[IFLA_MAX+1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_OPERSTATE] = { .type = NLA_U8 }, + [IFLA_LINKMODE] = { .type = NLA_U8 }, + [IFLA_LINKINFO] = { .type = NLA_NESTED }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) }, + [IFLA_STATS64] = { .minlen = sizeof(struct rtnl_link_stats64)}, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, + [IFLA_NUM_VF] = { .type = NLA_U32 }, + [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_UNSPEC }, + [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_NET_NS_FD] = { .type = NLA_U32 }, + }; + static struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { + [IFLA_INFO_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, + [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, + }; + const struct ifinfomsg *ifi; + struct nlattr *tb[IFLA_MAX+1]; + struct nlattr *li[IFLA_INFO_MAX+1]; + struct nlattr *nl_info_data = NULL; + const char *nl_info_kind = NULL; + int err; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result = NULL; + gboolean completed_from_cache_val = FALSE; + gboolean *completed_from_cache = cache ? &completed_from_cache_val : NULL; + const NMPObject *link_cached = NULL; + NMPObject *lnk_data = NULL; + + if (!nlmsg_valid_hdr (nlh, sizeof (*ifi))) + return NULL; + ifi = nlmsg_data(nlh); + + obj = nmp_object_new_link (ifi->ifi_index); if (id_only) - return TRUE; + goto id_only_handled; + + err = nlmsg_parse (nlh, sizeof (*ifi), tb, IFLA_MAX, policy); + if (err < 0) + goto errout; + + if (!tb[IFLA_IFNAME]) + goto errout; + nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ); + if (!obj->link.name[0]) + goto errout; - obj->source = NM_IP_CONFIG_SOURCE_KERNEL; - _nlo_rtnl_addr_get_lifetimes (nlo, - &obj->timestamp, - &obj->lifetime, - &obj->preferred); - obj->flags = rtnl_addr_get_flags (nlo); + if (tb[IFLA_LINKINFO]) { + err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], policy_link_info); + if (err < 0) + goto errout; - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) - g_warn_if_reached (); + if (li[IFLA_INFO_KIND]) + nl_info_kind = nla_get_string (li[IFLA_INFO_KIND]); + + nl_info_data = li[IFLA_INFO_DATA]; + } + + obj->link.flags = ifi->ifi_flags; + obj->link.connected = NM_FLAGS_HAS (obj->link.flags, IFF_LOWER_UP); + obj->link.arptype = ifi->ifi_type; + + obj->link.type = _linktype_get_type (platform, + cache, + nl_info_kind, + obj->link.ifindex, + obj->link.name, + obj->link.flags, + obj->link.arptype, + completed_from_cache, + &link_cached, + &obj->link.kind); + + if (tb[IFLA_MASTER]) + obj->link.master = nla_get_u32 (tb[IFLA_MASTER]); + + if (tb[IFLA_LINK]) { + if (!tb[IFLA_LINK_NETNSID]) + obj->link.parent = nla_get_u32 (tb[IFLA_LINK]); else - memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); + obj->link.parent = NM_PLATFORM_LINK_OTHER_NETNS; } - return TRUE; + if (tb[IFLA_ADDRESS]) { + int l = nla_len (tb[IFLA_ADDRESS]); + + if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->link.addr.data)); + memcpy (obj->link.addr.data, nla_data (tb[IFLA_ADDRESS]), l); + obj->link.addr.len = l; + } + } + + if (tb[IFLA_AF_SPEC]) { + struct nlattr *af_attr; + int remaining; + + nla_for_each_nested (af_attr, tb[IFLA_AF_SPEC], remaining) { + switch (nla_type (af_attr)) { + case AF_INET6: + _parse_af_inet6 (platform, + af_attr, + &obj->link.inet6_token.iid, + &obj->link.inet6_token.is_valid, + &obj->link.inet6_addr_gen_mode_inv); + break; + } + } + } + + if (tb[IFLA_MTU]) + obj->link.mtu = nla_get_u32 (tb[IFLA_MTU]); + + switch (obj->link.type) { + case NM_LINK_TYPE_GRE: + lnk_data = _parse_lnk_gre (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_INFINIBAND: + lnk_data = _parse_lnk_infiniband (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_IP6TNL: + lnk_data = _parse_lnk_ip6tnl (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_IPIP: + lnk_data = _parse_lnk_ipip (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_MACVLAN: + case NM_LINK_TYPE_MACVTAP: + lnk_data = _parse_lnk_macvlan (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_SIT: + lnk_data = _parse_lnk_sit (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VLAN: + lnk_data = _parse_lnk_vlan (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VXLAN: + lnk_data = _parse_lnk_vxlan (nl_info_kind, nl_info_data); + break; + default: + goto lnk_data_handled; + } + + /* We always try to look into the cache and reuse the object there. + * We do that, because we consider the lnk object as immutable and don't + * modify it after creating. Hence we can share it and reuse. + * + * Also, sometimes the info-data is missing for updates. In this case + * we want to keep the previously received lnk_data. */ + if (completed_from_cache) { + _lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached); + if ( link_cached + && link_cached->link.type == obj->link.type + && ( !lnk_data + || nmp_object_equal (lnk_data, link_cached->_link.netlink.lnk))) { + nmp_object_unref (lnk_data); + lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); + } + } + +lnk_data_handled: + obj->_link.netlink.lnk = lnk_data; + + obj->_link.netlink.is_in_netlink = TRUE; +id_only_handled: + obj_result = obj; + obj = NULL; +errout: + return obj_result; } -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) +/* Copied and heavily modified from libnl3's addr_msg_parser(). */ +static NMPObject * +_new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) { - 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; + static struct nla_policy policy[IFA_MAX+1] = { + [IFA_LABEL] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) }, + }; + const struct ifaddrmsg *ifa; + struct nlattr *tb[IFA_MAX+1]; + int err; + gboolean is_v4; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result = NULL; + int addr_len; + guint32 lifetime, preferred, timestamp; - 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; + if (!nlmsg_valid_hdr (nlh, sizeof (*ifa))) + return NULL; + ifa = nlmsg_data(nlh); - nexthop = rtnl_route_nexthop_n (nlo, 0); - if (!nexthop) - g_return_val_if_reached (FALSE); + if (!NM_IN_SET (ifa->ifa_family, AF_INET, AF_INET6)) + goto errout; + is_v4 = ifa->ifa_family == AF_INET; - dst = rtnl_route_get_dst (nlo); - if (!dst) - g_return_val_if_reached (FALSE); + err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, policy); + if (err < 0) + goto errout; + + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); + + /*****************************************************************/ + + obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + + obj->ip_address.ifindex = ifa->ifa_index; + obj->ip_address.plen = ifa->ifa_prefixlen; + + _check_addr_or_errout (tb, IFA_ADDRESS, addr_len); + _check_addr_or_errout (tb, IFA_LOCAL, addr_len); + if (is_v4) { + /* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they + * are effectively 0.0.0.0 (all-zero). */ + if (tb[IFA_LOCAL]) + memcpy (&obj->ip4_address.address, nla_data (tb[IFA_LOCAL]), addr_len); + if (tb[IFA_ADDRESS]) + memcpy (&obj->ip4_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len); + } else { + /* For IPv6, IFA_ADDRESS is always present. + * + * If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address + * is :: (all-zero). + * + * If unexpectely IFA_ADDRESS is missing, make the best of it -- but it _should_ + * actually be there. */ + if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) { + if (tb[IFA_LOCAL]) { + memcpy (&obj->ip6_address.address, nla_data (tb[IFA_LOCAL]), addr_len); + if (tb[IFA_ADDRESS]) + memcpy (&obj->ip6_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len); + else + obj->ip6_address.peer_address = obj->ip6_address.address; + } else + memcpy (&obj->ip6_address.address, nla_data (tb[IFA_ADDRESS]), addr_len); + } + } - if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (obj->network)) - g_return_val_if_reached (FALSE); - memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); + obj->ip_address.source = NM_IP_CONFIG_SOURCE_KERNEL; + + if (!is_v4) { + obj->ip6_address.flags = tb[IFA_FLAGS] + ? nla_get_u32 (tb[IFA_FLAGS]) + : ifa->ifa_flags; } - obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); - obj->plen = nl_addr_get_prefixlen (dst); - obj->metric = rtnl_route_get_priority (nlo); - obj->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (nlo)); - - gw = rtnl_route_nh_get_gateway (nexthop); - if (gw) { - if (nl_addr_get_len (gw) != sizeof (obj->gateway)) - g_warn_if_reached (); - else - memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + + if (is_v4) { + if (tb[IFA_LABEL]) { + char label[IFNAMSIZ]; + + nla_strlcpy (label, tb[IFA_LABEL], IFNAMSIZ); + + /* Check for ':'; we're only interested in labels used as interface aliases */ + if (strchr (label, ':')) + g_strlcpy (obj->ip4_address.label, label, sizeof (obj->ip4_address.label)); + } } - rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); - if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) { + + lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + preferred = NM_PLATFORM_LIFETIME_PERMANENT; + timestamp = 0; + /* IPv6 only */ + if (tb[IFA_CACHEINFO]) { + const struct ifa_cacheinfo *ca = nla_data(tb[IFA_CACHEINFO]); + + lifetime = ca->ifa_valid; + preferred = ca->ifa_prefered; + timestamp = ca->tstamp; + } + _addrtime_get_lifetimes (timestamp, + lifetime, + preferred, + &obj->ip_address.timestamp, + &obj->ip_address.lifetime, + &obj->ip_address.preferred); + + obj_result = obj; + obj = NULL; +errout: + return obj_result; +} + +/* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ +static NMPObject * +_new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) +{ + static struct nla_policy policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, + }; + const struct rtmsg *rtm; + struct nlattr *tb[RTA_MAX + 1]; + int err; + gboolean is_v4; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result = NULL; + int addr_len; + struct { + gboolean is_present; + int ifindex; + NMIPAddr gateway; + } nh; + guint32 mss; + guint32 table; + + if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) + return NULL; + rtm = nlmsg_data(nlh); + + /***************************************************************** + * only handle ~normal~ routes. + *****************************************************************/ + + if (!NM_IN_SET (rtm->rtm_family, AF_INET, AF_INET6)) + goto errout; + + if ( rtm->rtm_type != RTN_UNICAST + || rtm->rtm_tos != 0) + goto errout; + + err = nlmsg_parse (nlh, sizeof (struct rtmsg), tb, RTA_MAX, policy); + if (err < 0) + goto errout; + + table = tb[RTA_TABLE] + ? nla_get_u32 (tb[RTA_TABLE]) + : (guint32) rtm->rtm_table; + if (table != RT_TABLE_MAIN) + goto errout; + + /*****************************************************************/ + + is_v4 = rtm->rtm_family == AF_INET; + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); + + /***************************************************************** + * parse nexthops. Only handle routes with one nh. + *****************************************************************/ + + memset (&nh, 0, sizeof (nh)); + + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *rtnh = nla_data (tb[RTA_MULTIPATH]); + size_t tlen = nla_len(tb[RTA_MULTIPATH]); + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + + if (nh.is_present) { + /* we don't support multipath routes. */ + goto errout; + } + nh.is_present = TRUE; + + nh.ifindex = rtnh->rtnh_ifindex; + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + err = nla_parse (ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof (*rtnh), + policy); + if (err < 0) + goto errout; + + if (_check_addr_or_errout (ntb, RTA_GATEWAY, addr_len)) + memcpy (&nh.gateway, nla_data (ntb[RTA_GATEWAY]), addr_len); + } + + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + } + + if ( tb[RTA_OIF] + || tb[RTA_GATEWAY] + || tb[RTA_FLOW]) { + int ifindex = 0; + NMIPAddr gateway = NMIPAddrInit; + + if (tb[RTA_OIF]) + ifindex = nla_get_u32 (tb[RTA_OIF]); + if (_check_addr_or_errout (tb, RTA_GATEWAY, addr_len)) + memcpy (&gateway, nla_data (tb[RTA_GATEWAY]), addr_len); + + if (!nh.is_present) { + /* If no nexthops have been provided via RTA_MULTIPATH + * we add it as regular nexthop to maintain backwards + * compatibility */ + nh.ifindex = ifindex; + nh.gateway = gateway; + } else { + /* Kernel supports new style nexthop configuration, + * verify that it is a duplicate and ignore old-style nexthop. */ + if ( nh.ifindex != ifindex + || memcmp (&nh.gateway, &gateway, addr_len) != 0) + goto errout; + } + } else if (!nh.is_present) + goto errout; + + /*****************************************************************/ + + mss = 0; + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + goto errout; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i]) { + if (i == RTAX_ADVMSS) { + if (nla_len (mtb[i]) >= sizeof (uint32_t)) + mss = nla_get_u32(mtb[i]); + break; + } + } + } + } + + /*****************************************************************/ + + obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + + obj->ip_route.ifindex = nh.ifindex; + + if (_check_addr_or_errout (tb, RTA_DST, addr_len)) + memcpy (obj->ip_route.network_ptr, nla_data (tb[RTA_DST]), addr_len); + + obj->ip_route.plen = rtm->rtm_dst_len; + + if (tb[RTA_PRIORITY]) + obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]); + + if (is_v4) + obj->ip4_route.gateway = nh.gateway.addr4; + else + obj->ip6_route.gateway = nh.gateway.addr6; + + if (is_v4) + obj->ip4_route.scope_inv = nm_platform_route_scope_inv (rtm->rtm_scope); + + if (is_v4) { + if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len)) + memcpy (&obj->ip4_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); + } + + obj->ip_route.mss = mss; + + if (NM_FLAGS_HAS (rtm->rtm_flags, 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 @@ -1361,75 +1876,739 @@ _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject * * This happens, because this route is not nmp_object_is_alive(). * */ - obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; + obj->ip_route.source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; } else - obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); + obj->ip_route.source = _nm_ip_config_source_from_rtprot (rtm->rtm_protocol); - 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)); + obj_result = obj; + obj = NULL; +errout: + return obj_result; +} + +/** + * nmp_object_new_from_nl: + * @platform: (allow-none): for creating certain objects, the constructor wants to check + * sysfs. For this the platform instance is needed. If missing, the object might not + * be correctly detected. + * @cache: (allow-none): for certain objects, the netlink message doesn't contain all the information. + * If a cache is given, the object is completed with information from the cache. + * @nlh: the netlink message header + * @id_only: whether only to create an empty object with only the ID fields set. + * + * Returns: %NULL or a newly created NMPObject instance. + **/ +static NMPObject * +nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_msg *msg, gboolean id_only) +{ + struct nlmsghdr *msghdr; + + if (nlmsg_get_proto (msg) != NETLINK_ROUTE) + return NULL; + + msghdr = nlmsg_hdr (msg); + + switch (msghdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + case RTM_SETLINK: + return _new_from_nl_link (platform, cache, msghdr, id_only); + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + return _new_from_nl_addr (msghdr, id_only); + case RTM_NEWROUTE: + case RTM_DELROUTE: + case RTM_GETROUTE: + return _new_from_nl_route (msghdr, id_only); + default: + return NULL; + } +} + +/******************************************************************/ + +static gboolean +_nl_msg_new_link_set_afspec (struct nl_msg *msg, + int addr_gen_mode) +{ + struct nlattr *af_spec; + struct nlattr *af_attr; + + nm_assert (msg); + + if (!(af_spec = nla_nest_start (msg, IFLA_AF_SPEC))) + goto nla_put_failure; + + if (addr_gen_mode >= 0) { + if (!(af_attr = nla_nest_start (msg, AF_INET6))) + goto nla_put_failure; + + NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode); + + nla_nest_end (msg, af_attr); } + nla_nest_end (msg, af_spec); + return TRUE; +nla_put_failure: + return FALSE; } -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) +static gboolean +_nl_msg_new_link_set_linkinfo (struct nl_msg *msg, + NMLinkType link_type) { - NMPlatformIP6Route *obj = (NMPlatformIP6Route *) _obj; - struct rtnl_route *nlo = (struct rtnl_route *) _nlo; - struct nl_addr *dst, *gw; - struct rtnl_nexthop *nexthop; + struct nlattr *info; + const char *kind; + + nm_assert (msg); + + kind = nm_link_type_to_rtnl_type_string (link_type); + if (!kind) + goto nla_put_failure; + + if (!(info = nla_nest_start (msg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind); + + nla_nest_end (msg, info); + + return TRUE; +nla_put_failure: + return FALSE; +} + +static gboolean +_nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg, + int vlan_id, + guint32 flags_mask, + guint32 flags_set, + const NMVlanQosMapping *ingress_qos, + int ingress_qos_len, + const NMVlanQosMapping *egress_qos, + int egress_qos_len) +{ + struct nlattr *info; + struct nlattr *data; + guint i; + gboolean has_any_vlan_properties = FALSE; + +#define VLAN_XGRESS_PRIO_VALID(from) (((from) & ~(guint32) 0x07) == 0) + + nm_assert (msg); + + /* We must not create an empty IFLA_LINKINFO section. Otherwise, kernel + * rejects the request as invalid. */ + if ( flags_mask != 0 + || vlan_id >= 0) + has_any_vlan_properties = TRUE; + if ( !has_any_vlan_properties + && ingress_qos && ingress_qos_len > 0) { + for (i = 0; i < ingress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID (ingress_qos[i].from)) { + has_any_vlan_properties = TRUE; + break; + } + } + } + if ( !has_any_vlan_properties + && egress_qos && egress_qos_len > 0) { + for (i = 0; i < egress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) { + has_any_vlan_properties = TRUE; + break; + } + } + } + if (!has_any_vlan_properties) + return TRUE; + + if (!(info = nla_nest_start (msg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING (msg, IFLA_INFO_KIND, "vlan"); + + if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (vlan_id >= 0) + NLA_PUT_U16 (msg, IFLA_VLAN_ID, vlan_id); + + if (flags_mask != 0) { + struct ifla_vlan_flags flags = { + .flags = flags_mask & flags_set, + .mask = flags_mask, + }; + + NLA_PUT (msg, IFLA_VLAN_FLAGS, sizeof (flags), &flags); + } + + if (ingress_qos && ingress_qos_len > 0) { + struct nlattr *qos = NULL; + + for (i = 0; i < ingress_qos_len; i++) { + /* Silently ignore invalid mappings. Kernel would truncate + * them and modify the wrong mapping. */ + if (VLAN_XGRESS_PRIO_VALID (ingress_qos[i].from)) { + if (!qos) { + if (!(qos = nla_nest_start (msg, IFLA_VLAN_INGRESS_QOS))) + goto nla_put_failure; + } + NLA_PUT (msg, i, sizeof (ingress_qos[i]), &ingress_qos[i]); + } + } + + if (qos) + nla_nest_end (msg, qos); + } + + if (egress_qos && egress_qos_len > 0) { + struct nlattr *qos = NULL; + + for (i = 0; i < egress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) { + if (!qos) { + if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) + goto nla_put_failure; + } + NLA_PUT (msg, i, sizeof (egress_qos[i]), &egress_qos[i]); + } + } + + if (qos) + nla_nest_end(msg, qos); + } + + nla_nest_end (msg, data); + nla_nest_end (msg, info); + + return TRUE; +nla_put_failure: + return FALSE; +} + +static struct nl_msg * +_nl_msg_new_link (int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname, + unsigned flags_mask, + unsigned flags_set) +{ + struct nl_msg *msg; + struct ifinfomsg ifi = { + .ifi_change = flags_mask, + .ifi_flags = flags_set, + .ifi_index = ifindex, + }; + + nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK)); + + if (!(msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags))) + g_return_val_if_reached (NULL); + + if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (ifname) + NLA_PUT_STRING (msg, IFLA_IFNAME, ifname); + + return msg; +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + +/* Copied and modified from libnl3's build_addr_msg(). */ +static struct nl_msg * +_nl_msg_new_address (int nlmsg_type, + int nlmsg_flags, + int family, + int ifindex, + gconstpointer address, + int plen, + gconstpointer peer_address, + guint32 flags, + int scope, + guint32 lifetime, + guint32 preferred, + const char *label) +{ + struct nl_msg *msg; + struct ifaddrmsg am = { + .ifa_family = family, + .ifa_index = ifindex, + .ifa_prefixlen = plen, + .ifa_flags = flags, + }; + gsize addr_len; + + nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + if (!msg) + g_return_val_if_reached (NULL); + + if (scope == -1) { + /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ + if ( family == AF_INET + && address + && *((char *) address) == 127) + scope = RT_SCOPE_HOST; + else + scope = RT_SCOPE_UNIVERSE; + } + am.ifa_scope = scope, + + addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + + if (nlmsg_append (msg, &am, sizeof (am), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (address) + NLA_PUT (msg, IFA_LOCAL, addr_len, address); + + if (peer_address) + NLA_PUT (msg, IFA_ADDRESS, addr_len, peer_address); + else if (address) + NLA_PUT (msg, IFA_ADDRESS, addr_len, address); + + if (label && label[0]) + NLA_PUT_STRING (msg, IFA_LABEL, label); + + if ( family == AF_INET + && nlmsg_type != RTM_DELADDR + && address + && *((in_addr_t *) address) != 0) { + in_addr_t broadcast; + + broadcast = *((in_addr_t *) address) | ~nm_utils_ip4_prefix_to_netmask (plen); + NLA_PUT (msg, IFA_BROADCAST, addr_len, &broadcast); + } + + if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT + || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + struct ifa_cacheinfo ca = { + .ifa_valid = lifetime, + .ifa_prefered = preferred, + }; + + NLA_PUT (msg, IFA_CACHEINFO, sizeof(ca), &ca); + } + + if (flags & ~0xFF) { + /* only set the IFA_FLAGS attribute, if they actually contain additional + * flags that are not already set to am.ifa_flags. + * + * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL + * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which + * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. */ + NLA_PUT_U32 (msg, IFA_FLAGS, flags); + } + + return msg; + +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + +/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ +static struct nl_msg * +_nl_msg_new_route (int nlmsg_type, + int nlmsg_flags, + int family, + int ifindex, + NMIPConfigSource source, + unsigned char scope, + gconstpointer network, + int plen, + gconstpointer gateway, + guint32 metric, + guint32 mss, + gconstpointer pref_src) +{ + struct nl_msg *msg; + struct rtmsg rtmsg = { + .rtm_family = family, + .rtm_tos = 0, + .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ + .rtm_protocol = _nm_ip_config_source_to_rtprot (source), + .rtm_scope = scope, + .rtm_type = RTN_UNICAST, + .rtm_flags = 0, + .rtm_dst_len = plen, + .rtm_src_len = 0, + }; + NMIPAddr network_clean; + + gsize addr_len; + + nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); + nm_assert (network); + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + if (!msg) + g_return_val_if_reached (NULL); + + if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + + clear_host_address (family, network, plen, &network_clean); + NLA_PUT (msg, RTA_DST, addr_len, &network_clean); + + NLA_PUT_U32 (msg, RTA_PRIORITY, metric); + + if (pref_src) + NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src); + + if (mss > 0) { + struct nlattr *metrics; + + metrics = nla_nest_start (msg, RTA_METRICS); + if (!metrics) + goto nla_put_failure; + + NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); + + nla_nest_end(msg, metrics); + } + + /* We currently don't have need for multi-hop routes... */ + if ( gateway + && memcmp (gateway, &nm_ip_addr_zero, addr_len) != 0) + NLA_PUT (msg, RTA_GATEWAY, addr_len, gateway); + NLA_PUT_U32 (msg, RTA_OIF, ifindex); + + return msg; + +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + +/******************************************************************/ + +static int _support_kernel_extended_ifa_flags = -1; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) + +static void +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr; + + if (!_support_kernel_extended_ifa_flags_still_undecided ()) + return; + + msg_hdr = nlmsg_hdr (msg); + if (msg_hdr->nlmsg_type != RTM_NEWADDR) + return; + + /* the extended address flags are only set for AF_INET6 */ + if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE (they were added together). + **/ + _support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */); + _LOG2D ("support: kernel-extended-ifa-flags: %ssupported", _support_kernel_extended_ifa_flags ? "" : "not "); +} + +static gboolean +_support_kernel_extended_ifa_flags_get (void) +{ + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + _LOG2W ("support: kernel-extended-ifa-flags: unable to detect kernel support for handling IPv6 temporary addresses. Assume none"); + _support_kernel_extended_ifa_flags = 0; + } + return _support_kernel_extended_ifa_flags; +} + +/****************************************************************** + * NMPlatform types and functions + ******************************************************************/ + +typedef struct { + guint32 seq_number; + gint64 timeout_abs_ns; + WaitForNlResponseResult seq_result; + WaitForNlResponseResult *out_seq_result; +} DelayedActionWaitForNlResponseData; + +typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; + +struct _NMLinuxPlatformPrivate { + struct nl_sock *nlh; + guint32 nlh_seq_next; + guint32 nlh_seq_last_handled; + NMPCache *cache; + GIOChannel *event_channel; + guint event_id; + + gboolean sysctl_get_warned; + GHashTable *sysctl_get_prev_values; + + GUdevClient *udev_client; + + struct { + DelayedActionType flags; + GPtrArray *list_master_connected; + GPtrArray *list_refresh_link; + GArray *list_wait_for_nl_response; + gint is_handling; + } delayed_action; + + GHashTable *prune_candidates; + + GHashTable *wifi_data; +}; + +static inline NMLinuxPlatformPrivate * +NM_LINUX_PLATFORM_GET_PRIVATE (const void *self) +{ + nm_assert (NM_IS_LINUX_PLATFORM (self)); + + return ((NMLinuxPlatform *) self)->priv; +} + +G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) + +void +nm_linux_platform_setup (void) +{ + g_object_new (NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_REGISTER_SINGLETON, TRUE, + NULL); +} - 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) +/******************************************************************/ + +static void +_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value) +{ + GError *error = NULL; + char *contents, *contents_escaped; + char *value_escaped = g_strescape (value, NULL); + + if (!g_file_get_contents (path, &contents, NULL, &error)) { + _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", path, value_escaped, error->message); + g_clear_error (&error); + } else { + g_strstrip (contents); + contents_escaped = g_strescape (contents, NULL); + if (strcmp (contents, value) == 0) + _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", path, value_escaped); + else + _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped); + g_free (contents); + g_free (contents_escaped); + } + g_free (value_escaped); +} + +#define _log_dbg_sysctl_set(platform, path, value) \ + G_STMT_START { \ + if (_LOGD_ENABLED ()) { \ + _log_dbg_sysctl_set_impl (platform, path, value); \ + } \ + } G_STMT_END + +static gboolean +sysctl_set (NMPlatform *platform, const char *path, const char *value) +{ + int fd, len, nwrote, tries; + char *actual; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + /* Don't write outside known locations */ + g_assert (g_str_has_prefix (path, "/proc/sys/") + || g_str_has_prefix (path, "/sys/")); + /* Don't write to suspicious locations */ + g_assert (!strstr (path, "/../")); + + fd = open (path, O_WRONLY | O_TRUNC); + if (fd == -1) { + if (errno == ENOENT) { + _LOGD ("sysctl: failed to open '%s': (%d) %s", + path, errno, strerror (errno)); + } else { + _LOGE ("sysctl: failed to open '%s': (%d) %s", + path, errno, strerror (errno)); + } return FALSE; + } - nexthop = rtnl_route_nexthop_n (nlo, 0); - if (!nexthop) - g_return_val_if_reached (FALSE); + _log_dbg_sysctl_set (platform, path, value); - dst = rtnl_route_get_dst (nlo); - if (!dst) - g_return_val_if_reached (FALSE); + /* Most sysfs and sysctl options don't care about a trailing LF, while some + * (like infiniband) do. So always add the LF. Also, neither sysfs nor + * sysctl support partial writes so the LF must be added to the string we're + * about to write. + */ + actual = g_strdup_printf ("%s\n", value); - if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (obj->network)) - g_return_val_if_reached (FALSE); - memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); + /* Try to write the entire value three times if a partial write occurs */ + len = strlen (actual); + for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) { + nwrote = write (fd, actual, len); + if (nwrote == -1) { + if (errno == EINTR) { + _LOGD ("sysctl: interrupted, will try again"); + continue; + } + break; + } + } + if (nwrote == -1 && errno != EEXIST) { + _LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s", + path, value, errno, strerror (errno)); + } else if (nwrote < len) { + _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts", + path, value); } - 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; + g_free (actual); + close (fd); + return (nwrote == len); +} + +static GSList *sysctl_clear_cache_list; + +void +_nm_linux_platform_sysctl_clear_cache (void) +{ + while (sysctl_clear_cache_list) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (sysctl_clear_cache_list->data); + + sysctl_clear_cache_list = g_slist_delete_link (sysctl_clear_cache_list, sysctl_clear_cache_list); + + g_hash_table_destroy (priv->sysctl_get_prev_values); + priv->sysctl_get_prev_values = NULL; + priv->sysctl_get_warned = FALSE; + } +} + +static void +_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *contents) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const char *prev_value = NULL; + + if (!priv->sysctl_get_prev_values) { + sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform); + priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + } else + prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, path); + + if (prev_value) { + if (strcmp (prev_value, contents) != 0) { + char *contents_escaped = g_strescape (contents, NULL); + char *prev_value_escaped = g_strescape (prev_value, NULL); + + _LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", path, contents_escaped, prev_value_escaped); + g_free (contents_escaped); + g_free (prev_value_escaped); + g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents)); + } + } else { + char *contents_escaped = g_strescape (contents, NULL); - gw = rtnl_route_nh_get_gateway (nexthop); - if (gw) { - if (nl_addr_get_len (gw) != sizeof (obj->gateway)) - g_warn_if_reached (); + _LOGD ("sysctl: reading '%s': '%s'", path, contents_escaped); + g_free (contents_escaped); + g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents)); + } + + if ( !priv->sysctl_get_warned + && g_hash_table_size (priv->sysctl_get_prev_values) > 50000) { + _LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`."); + priv->sysctl_get_warned = TRUE; + } +} + +#define _log_dbg_sysctl_get(platform, path, contents) \ + G_STMT_START { \ + if (_LOGD_ENABLED ()) \ + _log_dbg_sysctl_get_impl (platform, path, contents); \ + } G_STMT_END + +static char * +sysctl_get (NMPlatform *platform, const char *path) +{ + GError *error = NULL; + char *contents; + + /* Don't write outside known locations */ + g_assert (g_str_has_prefix (path, "/proc/sys/") + || g_str_has_prefix (path, "/sys/")); + /* Don't write to suspicious locations */ + g_assert (!strstr (path, "/../")); + + if (!g_file_get_contents (path, &contents, NULL, &error)) { + /* We assume FAILED means EOPNOTSUP */ + if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) + || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) + _LOGD ("error reading %s: %s", path, error->message); else - memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + _LOGE ("error reading %s: %s", path, error->message); + g_clear_error (&error); + return NULL; } - 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; + g_strstrip (contents); + + _log_dbg_sysctl_get (platform, path, contents); + + return contents; } /******************************************************************/ +static gboolean +check_support_kernel_extended_ifa_flags (NMPlatform *platform) +{ + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + + return _support_kernel_extended_ifa_flags_get (); +} + +static gboolean +check_support_user_ipv6ll (NMPlatform *platform) +{ + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + + return _support_user_ipv6ll_get (); +} + static void -do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason) +process_events (NMPlatform *platform) +{ + delayed_action_handle_all (platform, 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)) + +/******************************************************************/ + +static void +do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible) { gboolean is_visible; NMPObject obj_clone; @@ -1476,16 +2655,21 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach klass = NMP_OBJECT_GET_CLASS (obj); - _LOGt ("emit signal %s %s: %s (%ld)", + _LOGt ("emit signal %s %s: %s", klass->signal_type, nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0), - (long) reason); + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); /* don't expose @obj directly, but clone the public fields. A signal handler might * call back into NMPlatform which could invalidate (or modify) @obj. */ memcpy (&obj_clone.object, &obj->object, klass->sizeof_public); - g_signal_emit_by_name (platform, klass->signal_type, klass->obj_type, obj_clone.object.ifindex, &obj_clone.object, (NMPlatformSignalChangeType) cache_op, reason); + g_signal_emit (platform, + _nm_platform_signal_id_get (klass->signal_type_id), + 0, + klass->obj_type, + obj_clone.object.ifindex, + &obj_clone.object, + (NMPlatformSignalChangeType) cache_op); } /******************************************************************/ @@ -1528,42 +2712,140 @@ delayed_action_to_string (DelayedActionType action_type) 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"; + case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE : return "wait-for-nl-response"; 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 const char * +delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data, char *buf, gsize buf_size) +{ + char *buf0 = buf; + const DelayedActionWaitForNlResponseData *data; + + nm_utils_strbuf_append_str (&buf, &buf_size, delayed_action_to_string (action_type)); + switch (action_type) { + case DELAYED_ACTION_TYPE_MASTER_CONNECTED: + nm_utils_strbuf_append (&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT (user_data)); + break; + case DELAYED_ACTION_TYPE_REFRESH_LINK: + nm_utils_strbuf_append (&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT (user_data)); + break; + case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: + data = user_data; + + if (data) { + gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_ns (); + char b[255]; + + nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT"%s%s)", + data->seq_number, + timeout < 0 ? "-" : "", + (timeout < 0 ? -timeout : timeout) / NM_UTILS_NS_PER_SECOND, + (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND, + data->seq_result ? ", " : "", + data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : ""); + } else + nm_utils_strbuf_append_str (&buf, &buf_size, " (any)"); + break; + default: + nm_assert (!user_data); + break; + } + return buf0; +} + +#define _LOGt_delayed_action(action_type, user_data, operation) \ + G_STMT_START { \ + char _buf[255]; \ + \ + _LOGt ("delayed-action: %s %s", \ + ""operation, \ + delayed_action_to_string_full (action_type, user_data, _buf, sizeof (_buf))); \ + } G_STMT_END + +/*****************************************************************************/ + +static void +delayed_action_wait_for_nl_response_complete (NMPlatform *platform, + guint idx, + WaitForNlResponseResult seq_result) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionWaitForNlResponseData *data; + WaitForNlResponseResult *out_seq_result; + + nm_assert (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert (idx < priv->delayed_action.list_wait_for_nl_response->len); + nm_assert (seq_result); + + data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, idx); + + _LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete"); + + out_seq_result = data->out_seq_result; + + g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); + /* Note: @data is invalidated at this point */ + + if (priv->delayed_action.list_wait_for_nl_response->len <= 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; + + if (out_seq_result) + *out_seq_result = seq_result; +} + +static void +delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, + WaitForNlResponseResult result) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + while (priv->delayed_action.list_wait_for_nl_response->len > 0) + delayed_action_wait_for_nl_response_complete (platform, priv->delayed_action.list_wait_for_nl_response->len - 1, result); + } + nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0); +} + +/*****************************************************************************/ static void delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nmp_obj NMPObject *obj_cache = NULL; + nm_auto_nmpobj NMPObject *obj_cache = NULL; gboolean was_visible; NMPCacheOpsType cache_op; cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); + do_emit_signal (platform, obj_cache, cache_op, was_visible); } static void delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex) { - do_request_link (platform, ifindex, NULL, FALSE); + do_request_link_no_delayed_actions (platform, ifindex, NULL); } static void delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags) { - do_request_all (platform, flags, FALSE); + do_request_all_no_delayed_actions (platform, flags); } static void delayed_action_handle_READ_NETLINK (NMPlatform *platform) { - event_handler_read_netlink_all (platform, TRUE); + event_handler_read_netlink (platform, FALSE); +} + +static void +delayed_action_handle_WAIT_FOR_NL_RESPONSE (NMPlatform *platform) +{ + event_handler_read_netlink (platform, TRUE); } static gboolean @@ -1572,10 +2854,8 @@ 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); + if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) return FALSE; - } /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. * This type of action is entirely cache-internal and is here to resolve a @@ -1622,20 +2902,30 @@ delayed_action_handle_one (NMPlatform *platform) return TRUE; } - nm_assert (priv->delayed_action.flags == DELAYED_ACTION_TYPE_REFRESH_LINK); - nm_assert (priv->delayed_action.list_refresh_link->len > 0); + if (NM_FLAGS_HAS (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_refresh_link->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); + 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_refresh_link->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"); + _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle"); - delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data)); + delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data)); - return TRUE; + return TRUE; + } + + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); + _LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle"); + delayed_action_handle_WAIT_FOR_NL_RESPONSE (platform); + return TRUE; + } + + return FALSE; } static gboolean @@ -1644,22 +2934,18 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); gboolean any = FALSE; - nm_clear_g_source (&priv->delayed_action.idle_id); + g_return_val_if_fail (priv->delayed_action.is_handling == 0, FALSE); + priv->delayed_action.is_handling++; if (read_netlink) delayed_action_schedule (platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); while (delayed_action_handle_one (platform)) any = TRUE; priv->delayed_action.is_handling--; - return any; -} -static gboolean -delayed_action_handle_idle (gpointer user_data) -{ - NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0; - delayed_action_handle_all (user_data, FALSE); - return G_SOURCE_REMOVE; + cache_prune_candidates_prune (platform); + + return any; } static void @@ -1670,16 +2956,25 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp 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)); + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_LINK: 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)); + break; + case DELAYED_ACTION_TYPE_MASTER_CONNECTED: 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 + break; + case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: + g_array_append_vals (priv->delayed_action.list_wait_for_nl_response, user_data, 1); + break; + default: nm_assert (!user_data); + nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)); + nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)); + nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + break; + } priv->delayed_action.flags |= action_type; @@ -1689,9 +2984,22 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp _LOGt_delayed_action (iflags, user_data, "schedule"); } } +} - if (priv->delayed_action.is_handling == 0 && priv->delayed_action.idle_id == 0) - priv->delayed_action.idle_id = g_idle_add (delayed_action_handle_idle, platform); +static void +delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, + guint32 seq_number, + WaitForNlResponseResult *out_seq_result) +{ + DelayedActionWaitForNlResponseData data = { + .seq_number = seq_number, + .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)), + .out_seq_result = out_seq_result, + }; + + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, + &data); } /******************************************************************/ @@ -1760,11 +3068,11 @@ cache_prune_candidates_prune (NMPlatform *platform) 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; + nm_auto_nmpobj NMPObject *obj_cache = NULL; _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); + do_emit_signal (platform, obj_cache, cache_op, was_visible); } g_hash_table_unref (prune_candidates); @@ -1776,8 +3084,8 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP 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)]; + char str_buf[sizeof (_nm_utils_to_string_buffer)]; + char str_buf2[sizeof (_nm_utils_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)); @@ -1850,7 +3158,8 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP /* removal of a link could be caused by moving the link to another netns. * In this case, we potentially have to update other links that have this link as parent. - * Currently, kernel misses to sent us a notification in this case (rh #1262908). */ + * Currently, kernel misses to sent us a notification in this case + * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ if ( ops_type == NMP_CACHE_OPS_REMOVED && old /* <-- nonsensical, make coverity happy */ @@ -1894,6 +3203,29 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP && (new && new->_link.netlink.is_in_netlink) && (!old || !old->_link.netlink.is_in_netlink)) { + if (!new->_link.netlink.lnk) { + /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that + * kernel didn't send this notification, thus when we first learn about a link + * that lacks an lnk_data we re-request it again. + * + * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ + switch (new->link.type) { + case NM_LINK_TYPE_GRE: + case NM_LINK_TYPE_IP6TNL: + case NM_LINK_TYPE_INFINIBAND: + case NM_LINK_TYPE_MACVLAN: + case NM_LINK_TYPE_MACVTAP: + case NM_LINK_TYPE_SIT: + case NM_LINK_TYPE_VLAN: + case NM_LINK_TYPE_VXLAN: + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_LINK, + GINT_TO_POINTER (new->link.ifindex)); + break; + default: + break; + } + } if ( new->link.type == NM_LINK_TYPE_VETH && new->link.parent == 0) { /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK @@ -1959,100 +3291,74 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP } } -static NMPCacheOpsType -cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPObject *obj_cache; - gboolean was_visible; - NMPCacheOpsType cache_op; - - cache_op = nmp_cache_remove_netlink (priv->cache, obj_needle, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); - - if (out_obj_cache) - *out_obj_cache = obj_cache; - else - nmp_object_unref (obj_cache); - if (out_was_visible) - *out_was_visible = was_visible; - - return cache_op; -} +/******************************************************************/ -static NMPCacheOpsType -cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) +static int +_nl_send_auto_with_seq (NMPlatform *platform, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPObject *obj_cache; - gboolean was_visible; - NMPCacheOpsType cache_op; + guint32 seq; + int nle; - /* This is basically a convenience method to call nmp_cache_update() and do_emit_signal() - * at once. */ + /* complete the message with a sequence number (ensuring it's not zero). */ + seq = priv->nlh_seq_next++ ?: priv->nlh_seq_next++; - 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); + nlmsg_hdr (nlmsg)->nlmsg_seq = seq; - if (out_obj_cache) - *out_obj_cache = obj_cache; - else - nmp_object_unref (obj_cache); - if (out_was_visible) - *out_was_visible = was_visible; + nle = nl_send_auto (priv->nlh, nlmsg); - return cache_op; -} + if (nle >= 0) { + nle = 0; + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result); + } else + _LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle); -/******************************************************************/ + return nle; +} static void -_new_sequence_number (NMPlatform *platform, guint32 seq) +do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - _LOGt ("_new_sequence_number(): new sequence number %u", seq); - - priv->nlh_seq_expect = seq; -} + if (name && !name[0]) + name = NULL; -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; + g_return_if_fail (ifindex > 0 || name); - _LOGt ("do_request_link (%d,%s)", ifindex, name ? name : ""); + _LOGD ("do-request-link: %d %s", ifindex, name ? name : ""); if (ifindex > 0) { cache_prune_candidates_record_one (platform, (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); } - 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 (platform, FALSE); - event_handler_read_netlink_all (platform, TRUE); - - cache_prune_candidates_prune (platform); - - if (handle_delayed_action) - delayed_action_handle_all (platform, FALSE); + nlmsg = _nl_msg_new_link (RTM_GETLINK, + 0, + ifindex, + name, + 0, + 0); + if (nlmsg) + _nl_send_auto_with_seq (platform, nlmsg, NULL); } static void -do_request_one_type (NMPlatform *platform, NMPObjectType obj_type, gboolean handle_delayed_action) +do_request_link (NMPlatform *platform, int ifindex, const char *name) { - do_request_all (platform, delayed_action_refresh_from_object_type (obj_type), handle_delayed_action); + do_request_link_no_delayed_actions (platform, ifindex, name); + delayed_action_handle_all (platform, FALSE); } static void -do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action) +do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - guint32 seq; DelayedActionType iflags; nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); @@ -2066,6 +3372,12 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha 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); + const NMPClass *klass = nmp_class_from_type (obj_type); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct rtgenmsg gmsg = { + .rtgen_family = klass->addr_family, + }; + int nle; /* clear any delayed action that request a refresh of this object type. */ priv->delayed_action.flags &= ~iflags; @@ -2076,496 +3388,129 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)"); } - event_handler_read_netlink_all (platform, FALSE); + event_handler_read_netlink (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); - - cache_prune_candidates_prune (platform); - - if (handle_delayed_action) - delayed_action_handle_all (platform, FALSE); -} - -static gboolean -kernel_add_object (NMPlatform *platform, NMPObjectType obj_type, const struct nl_object *nlo) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; - - g_return_val_if_fail (nlo, FALSE); + /* reimplement + * nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP); + * because we need the sequence number. + */ + nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); + if (!nlmsg) + goto next; - 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); - } + nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO); + if (nle < 0) + goto next; - _LOGt ("kernel-add-%s: returned %s (%d)", - nmp_class_from_type (obj_type)->obj_type_name, nl_geterror (nle), -nle); - - switch (nle) { - case -NLE_SUCCESS: - return -NLE_SUCCESS; - case -NLE_EXIST: - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. */ - if (obj_type != NMP_OBJECT_TYPE_LINK) - return -NLE_SUCCESS; - /* fall-through */ - default: - return nle; + _nl_send_auto_with_seq (platform, nlmsg, NULL); + } +next: + ; } } -static int -kernel_delete_object (NMPlatform *platform, NMPObjectType object_type, const struct nl_object *object) +static void +do_request_one_type (NMPlatform *platform, NMPObjectType obj_type) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; - - switch (object_type) { - case NMP_OBJECT_TYPE_LINK: - nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); - break; - 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 NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0); - break; - default: - g_assert_not_reached (); - } - - switch (nle) { - case -NLE_SUCCESS: - return NLE_SUCCESS; - case -NLE_OBJ_NOTFOUND: - _LOGt ("kernel-delete-%s: failed with \"%s\" (%d), meaning the object was already removed", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return -NLE_SUCCESS; - case -NLE_FAILURE: - if (object_type == NMP_OBJECT_TYPE_IP6_ADDRESS) { - /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */ - _LOGt ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return NLE_SUCCESS; - } - break; - case -NLE_NOADDR: - if (object_type == 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; - } - break; - default: - break; - } - _LOGt ("kernel-delete-%s: failed with %s (%d)", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return nle; + do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_object_type (obj_type)); + delayed_action_handle_all (platform, FALSE); } -static int -kernel_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean *complete_from_cache) +static void +event_seq_check (NMPlatform *platform, struct nl_msg *msg, WaitForNlResponseResult seq_result) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_msg *msg; - int nle; - const int nlflags = 0; - int ifindex; - - ifindex = rtnl_link_get_ifindex (nlo); + DelayedActionWaitForNlResponseData *data; + guint32 seq_number; + guint i; - g_return_val_if_fail (ifindex > 0, FALSE); + seq_number = nlmsg_hdr (msg)->nlmsg_seq; - /* 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; + if (seq_number == 0) + return; - 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; + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); + + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if (data->seq_number == seq_number) { + /* We potentially receive many parts partial responses for the same sequence number. + * Thus, we only remember the result, and collect it later. */ + if (data->seq_result < 0) { + /* we already saw an error for this seqence number. + * Preserve it. */ + } else if ( seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN + || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) + data->seq_result = seq_result; + return; + } } - - 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; + if (seq_number != priv->nlh_seq_last_handled) + _LOGt ("netlink: recvmsg: unwaited sequence number %u", seq_number); + priv->nlh_seq_last_handled = seq_number; } static void -ref_object (struct nl_object *obj, void *data) -{ - struct nl_object **out = data; - - nl_object_get (obj); - *out = obj; -} - -static int -event_seq_check (struct nl_msg *msg, gpointer user_data) +event_valid_msg (NMPlatform *platform, struct nl_msg *msg) { - NMPlatform *platform = NM_PLATFORM (user_data); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nlmsghdr *hdr; - - hdr = nlmsg_hdr (msg); - - if (hdr->nlmsg_seq == 0) - return NL_OK; - - priv->nlh_seq_last = hdr->nlmsg_seq; - - if (priv->nlh_seq_expect == 0) - _LOGt ("event_seq_check(): seq %u received (not waited)", hdr->nlmsg_seq); - else if (hdr->nlmsg_seq == priv->nlh_seq_expect) { - _LOGt ("event_seq_check(): seq %u received", hdr->nlmsg_seq); - - priv->nlh_seq_expect = 0; - } else - _LOGt ("event_seq_check(): seq %u received (wait for %u)", hdr->nlmsg_seq, priv->nlh_seq_last); - - return NL_OK; -} - -static int -event_err (struct sockaddr_nl *nla, struct nlmsgerr *nlerr, gpointer 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 - * by concurrent usage of synchronous commands and an asynchronous cache. This - * might be a nice future addition to libnl but it requires to do all operations - * through the cache manager. In this case, nm-linux-platform serves as the - * cache manager instead of the one provided by libnl. - */ -static int -event_notification (struct nl_msg *msg, gpointer user_data) -{ - NMPlatform *platform = NM_PLATFORM (user_data); - auto_nl_object struct nl_object *nlo = NULL; - auto_nmp_obj NMPObject *obj = NULL; + nm_auto_nmpobj NMPObject *obj = NULL; + nm_auto_nmpobj NMPObject *obj_cache = NULL; + NMPCacheOpsType cache_op; struct nlmsghdr *msghdr; char buf_nlmsg_type[16]; + gboolean id_only = FALSE; + gboolean was_visible; msghdr = nlmsg_hdr (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, &nlo); - if (!nlo) - return NL_OK; - - if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK) - _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); - - switch (msghdr->nlmsg_type) { - case RTM_DELADDR: - case RTM_DELLINK: - case RTM_DELROUTE: - /* The event notifies about a deleted object. We don't need to initialize all the - * fields of the nmp-object. Shortcut nmp_object_from_nl(). */ - obj = nmp_object_from_nl (platform, nlo, TRUE, TRUE); - _LOGT ("event-notification: %s, seq %u: %s", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - break; - default: - obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - _LOGT ("event-notification: %s, seq %u: %s", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - break; - } - - if (obj) { - auto_nmp_obj NMPObject *obj_cache = NULL; - - switch (msghdr->nlmsg_type) { - - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - cache_update_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); - break; - - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); - break; - - default: - break; - } - - cache_prune_candidates_drop (platform, obj_cache); + if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, RTM_DELADDR, RTM_DELROUTE)) { + /* The event notifies about a deleted object. We don't need to initialize all + * fields of the object. */ + id_only = TRUE; } - return NL_OK; -} - -/******************************************************************/ - -static void -_log_dbg_sysctl_set_impl (const char *path, const char *value) -{ - GError *error = NULL; - char *contents, *contents_escaped; - char *value_escaped = g_strescape (value, NULL); - - if (!g_file_get_contents (path, &contents, NULL, &error)) { - debug ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", path, value_escaped, error->message); - g_clear_error (&error); - } else { - g_strstrip (contents); - contents_escaped = g_strescape (contents, NULL); - if (strcmp (contents, value) == 0) - debug ("sysctl: setting '%s' to '%s' (current value is identical)", path, value_escaped); - else - debug ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped); - g_free (contents); - g_free (contents_escaped); - } - g_free (value_escaped); -} - -#define _log_dbg_sysctl_set(path, value) \ - G_STMT_START { \ - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { \ - _log_dbg_sysctl_set_impl (path, value); \ - } \ - } G_STMT_END - -static gboolean -sysctl_set (NMPlatform *platform, const char *path, const char *value) -{ - int fd, len, nwrote, tries; - char *actual; - - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - /* Don't write outside known locations */ - g_assert (g_str_has_prefix (path, "/proc/sys/") - || g_str_has_prefix (path, "/sys/")); - /* Don't write to suspicious locations */ - g_assert (!strstr (path, "/../")); - - fd = open (path, O_WRONLY | O_TRUNC); - if (fd == -1) { - if (errno == ENOENT) { - debug ("sysctl: failed to open '%s': (%d) %s", - path, errno, strerror (errno)); - } else { - error ("sysctl: failed to open '%s': (%d) %s", - path, errno, strerror (errno)); - } - return FALSE; + obj = nmp_object_new_from_nl (platform, priv->cache, msg, id_only); + if (!obj) { + _LOGT ("event-notification: %s, seq %u: ignore", + _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), + msghdr->nlmsg_seq); + return; } - _log_dbg_sysctl_set (path, value); - - /* Most sysfs and sysctl options don't care about a trailing LF, while some - * (like infiniband) do. So always add the LF. Also, neither sysfs nor - * sysctl support partial writes so the LF must be added to the string we're - * about to write. - */ - actual = g_strdup_printf ("%s\n", value); - - /* Try to write the entire value three times if a partial write occurs */ - len = strlen (actual); - for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) { - nwrote = write (fd, actual, len); - if (nwrote == -1) { - if (errno == EINTR) { - debug ("sysctl: interrupted, will try again"); - continue; - } - break; - } - } - if (nwrote == -1 && errno != EEXIST) { - error ("sysctl: failed to set '%s' to '%s': (%d) %s", - path, value, errno, strerror (errno)); - } else if (nwrote < len) { - error ("sysctl: failed to set '%s' to '%s' after three attempts", - path, value); - } + _LOGT ("event-notification: %s, seq %u: %s", + _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), + msghdr->nlmsg_seq, nmp_object_to_string (obj, + id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_free (actual); - close (fd); - return (nwrote == len); -} - -static GHashTable *sysctl_get_prev_values; - -static void -_log_dbg_sysctl_get_impl (const char *path, const char *contents) -{ - const char *prev_value = NULL; - - if (!sysctl_get_prev_values) - sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - else - prev_value = g_hash_table_lookup (sysctl_get_prev_values, path); - - if (prev_value) { - if (strcmp (prev_value, contents) != 0) { - char *contents_escaped = g_strescape (contents, NULL); - char *prev_value_escaped = g_strescape (prev_value, NULL); - - debug ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", path, contents_escaped, prev_value_escaped); - g_free (contents_escaped); - g_free (prev_value_escaped); - g_hash_table_insert (sysctl_get_prev_values, g_strdup (path), g_strdup (contents)); - } - } else { - char *contents_escaped = g_strescape (contents, NULL); - - debug ("sysctl: reading '%s': '%s'", path, contents_escaped); - g_free (contents_escaped); - g_hash_table_insert (sysctl_get_prev_values, g_strdup (path), g_strdup (contents)); - } -} - -#define _log_dbg_sysctl_get(path, contents) \ - G_STMT_START { \ - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { \ - _log_dbg_sysctl_get_impl (path, contents); \ - } else if (sysctl_get_prev_values) { \ - g_hash_table_destroy (sysctl_get_prev_values); \ - sysctl_get_prev_values = NULL; \ - } \ - } G_STMT_END + switch (msghdr->nlmsg_type) { -static char * -sysctl_get (NMPlatform *platform, const char *path) -{ - GError *error = NULL; - char *contents; + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + 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); + break; - /* Don't write outside known locations */ - g_assert (g_str_has_prefix (path, "/proc/sys/") - || g_str_has_prefix (path, "/sys/")); - /* Don't write to suspicious locations */ - g_assert (!strstr (path, "/../")); + case RTM_DELLINK: + case RTM_DELADDR: + case RTM_DELROUTE: + cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible); + break; - if (!g_file_get_contents (path, &contents, NULL, &error)) { - /* We assume FAILED means EOPNOTSUP */ - if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) - || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) - debug ("error reading %s: %s", path, error->message); - else - error ("error reading %s: %s", path, error->message); - g_clear_error (&error); - return NULL; + default: + break; } - g_strstrip (contents); - - _log_dbg_sysctl_get (path, contents); - - return contents; + cache_prune_candidates_drop (platform, obj_cache); } /******************************************************************/ @@ -2647,195 +3592,261 @@ _nm_platform_link_get_by_address (NMPlatform *platform, return obj ? &obj->link : NULL; } -static struct nl_object * -build_rtnl_link (int ifindex, const char *name, NMLinkType type) +/*****************************************************************************/ + +static const NMPObject * +link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) { - struct rtnl_link *rtnllink; - int nle; + const NMPObject *obj = cache_lookup_link (platform, ifindex); - rtnllink = _nl_rtnl_link_alloc (ifindex, name); - if (type) { - nle = rtnl_link_set_type (rtnllink, nm_link_type_to_rtnl_type_string (type)); - g_assert (!nle); - } - return (struct nl_object *) rtnllink; -} + if (!obj) + return NULL; -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformLink *obj = (const NMPlatformLink *) _obj; + NM_SET_OUT (out_link, &obj->link); + + if (!obj->_link.netlink.lnk) + return NULL; + if ( link_type != NM_LINK_TYPE_NONE + && ( link_type != obj->link.type + || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) + return NULL; - return build_rtnl_link (obj->ifindex, - obj->name[0] ? obj->name : NULL, - obj->type); + return obj->_link.netlink.lnk; } +/*****************************************************************************/ + static gboolean -do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo) +do_add_link_with_lookup (NMPlatform *platform, + NMLinkType link_type, + const char *name, + struct nl_msg *nlmsg, + const NMPlatformLink **out_link) { - NMPObject obj_needle; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj = NULL; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; + char s_buf[256]; + + event_handler_read_netlink (platform, FALSE); - event_handler_read_netlink_all (platform, FALSE); + if (nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { + /* hm, a link with such a name already exists. Try reloading first. */ + do_request_link (platform, 0, name); - nle = kernel_add_object (platform, NMP_OBJECT_TYPE_LINK, (const struct nl_object *) nlo); + obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); + if (obj) { + _LOGE ("do-add-link[%s/%s]: link already exists: %s", + name, + nm_link_type_to_string (link_type), + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + return FALSE; + } + } + + nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result); if (nle < 0) { - _LOGE ("do-add-link: failure adding link '%s': %s", name, nl_geterror (nle)); + _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", + name, + nm_link_type_to_string (link_type), + nl_geterror (nle), -nle); return FALSE; } - _LOGD ("do-add-link: success adding link '%s'", name); - nmp_object_stackinit_id_link (&obj_needle, 0); - g_strlcpy (obj_needle.link.name, name, sizeof (obj_needle.link.name)); + delayed_action_handle_all (platform, FALSE); - delayed_action_handle_all (platform, TRUE); + nm_assert (seq_result); - /* 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); - } + _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + ? LOGL_DEBUG + : LOGL_ERR, + "do-add-link[%s/%s]: %s", + name, + nm_link_type_to_string (link_type), + wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - /* Return true, because kernel_add_object() succeeded. This doesn't indicate that the - * object is now actuall in the cache, because there could be a race. - * - * For that, you'd have to look at @out_obj. */ - return TRUE; -} - -static gboolean -do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, NMLinkType expected_link_type, NMPlatformLink *out_link) -{ - const NMPObject *obj; + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); - do_add_link (platform, name, nlo); + if (!obj) { + /* either kernel signaled failure, or it signaled success and the link object + * is not (yet) in the cache. Try to reload it... */ + do_request_link (platform, 0, name); + obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); + } - 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; + if (out_link) + *out_link = obj ? &obj->link : NULL; return !!obj; } static gboolean -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; + char s_buf[256]; + const NMPObject *obj; 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); + event_handler_read_netlink (platform, FALSE); - nle = kernel_add_object (platform, NMP_OBJECT_GET_CLASS (obj_id)->obj_type, (const struct nl_object *) nlo); + nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result); if (nle < 0) { - _LOGW ("do-add-%s: failure adding %s '%s': %s (%d)", - NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); return FALSE; } - _LOGD ("do-add-%s: success adding object %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - delayed_action_handle_all (platform, TRUE); + delayed_action_handle_all (platform, FALSE); + + nm_assert (seq_result); + + _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + ? LOGL_DEBUG + : LOGL_ERR, + "do-add-%s[%s]: %s", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - /* 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); + /* In rare cases, the object is not yet ready as we received the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * FIXME: if the object already existed previously, we might not notice a + * missing update. It's not clear how to fix that reliably without refechting + * all the time. */ + obj = nmp_cache_lookup_obj (priv->cache, obj_id); + if (!obj) { + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + obj = nmp_cache_lookup_obj (priv->cache, obj_id); } - /* 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; + /* Adding is only successful, if kernel reported success *and* we have the + * expected object in cache afterwards. */ + return obj && seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } - static gboolean -do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct nl_object *nlo_free = NULL; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; + char s_buf[256]; + gboolean success = TRUE; + const char *log_detail = ""; - event_handler_read_netlink_all (platform, FALSE); + event_handler_read_netlink (platform, FALSE); - if (!nlo) - nlo = nlo_free = nmp_object_to_nl (platform, obj_id, FALSE); + nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result); + if (nle < 0) { + _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + goto out; + } - 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, FALSE); - delayed_action_handle_all (platform, TRUE); + nm_assert (seq_result); + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + /* ok */ + } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) + log_detail = ", meaning the object was already removed"; + else if ( NM_IN_SET (-((int) seq_result), ENXIO) + && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) { + /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */ + log_detail = ", meaning the address was already removed"; + } else if ( NM_IN_SET (-((int) seq_result), EADDRNOTAVAIL) + && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)) + log_detail = ", meaning the address was already removed"; + else + success = FALSE; - /* 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; + _NMLOG (success ? LOGL_DEBUG : LOGL_ERR, + "do-delete-%s[%s]: %s%s", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), + log_detail); - 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); - } - } +out: + if (!nmp_cache_lookup_obj (priv->cache, obj_id)) + return 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; + /* such an object still exists in the cache. To be sure, refetch it (and + * hope it's gone) */ + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + return !!nmp_cache_lookup_obj (priv->cache, obj_id); } static NMPlatformError -do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache) +do_change_link (NMPlatform *platform, + int ifindex, + struct nl_msg *nlmsg) { + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; - int ifindex; - gboolean complete_from_cache2 = complete_from_cache; + char s_buf[256]; + NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; + NMLogLevel log_level = LOGL_DEBUG; + const char *log_result = "failure", *log_detail = ""; - ifindex = rtnl_link_get_ifindex (nlo); - if (ifindex <= 0) - g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); +retry: + nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result); + if (nle < 0) { + _LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)", + ifindex, + nl_geterror (nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } - nle = kernel_change_link (platform, nlo, &complete_from_cache2); + /* always refetch the link after changing it. There seems to be issues + * and we sometimes lack events. Nuke it from the orbit... */ + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex)); - 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: - /* fall-through */ - default: - 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; + delayed_action_handle_all (platform, FALSE); + + nm_assert (seq_result); + + if ( NM_IN_SET (-((int) seq_result), EOPNOTSUPP) + && nlmsg_hdr (nlmsg)->nlmsg_type == RTM_NEWLINK) { + nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK; + goto retry; + } + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + log_result = "success"; + } else if (NM_IN_SET (-((int) seq_result), EEXIST, EADDRINUSE)) { + /* */ + } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) { + log_detail = ", firmware not found"; + result = NM_PLATFORM_ERROR_NO_FIRMWARE; + } else { + log_level = LOGL_ERR; + result = NM_PLATFORM_ERROR_UNSPECIFIED; } + _NMLOG (log_level, + "do-change-link[%d]: %s changing link: %s%s", + ifindex, + log_result, + wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), + log_detail); - /* 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; + return result; } static gboolean @@ -2844,9 +3855,9 @@ link_add (NMPlatform *platform, NMLinkType type, const void *address, size_t address_len, - NMPlatformLink *out_link) + const NMPlatformLink **out_link) { - auto_nl_object struct nl_object *l = NULL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; if (type == NM_LINK_TYPE_BOND) { /* When the kernel loads the bond module, either via explicit modprobe @@ -2860,34 +3871,50 @@ link_add (NMPlatform *platform, nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); } - debug ("link: add link '%s' of type '%s' (%d)", + _LOGD ("link: add link '%s' of type '%s' (%d)", name, nm_link_type_to_string (type), (int) type); - l = build_rtnl_link (0, name, type); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; - g_assert ( (address != NULL) ^ (address_len == 0) ); - if (address) { - auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, address_len); + if (address && address_len) + NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); - rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); - } + if (!_nl_msg_new_link_set_linkinfo (nlmsg, type)) + return FALSE; - return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link); + return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } static gboolean link_delete (NMPlatform *platform, int ifindex) { + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; obj = nmp_cache_lookup_link (priv->cache, ifindex); if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; - nmp_object_stackinit_id_link (&obj_needle, ifindex); - return do_delete_object (platform, &obj_needle, NULL); + nlmsg = _nl_msg_new_link (RTM_DELLINK, + 0, + ifindex, + NULL, + 0, + 0); + + nmp_object_stackinit_id_link (&obj_id, ifindex); + return do_delete_object (platform, &obj_id, nlmsg); } static const char * @@ -2933,33 +3960,35 @@ link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) static gboolean link_refresh (NMPlatform *platform, int ifindex) { - do_request_link (platform, ifindex, NULL, TRUE); + do_request_link (platform, ifindex, NULL); return !!cache_lookup_link (platform, ifindex); } static NMPlatformError -link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value) -{ - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); - 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); - - _LOGD ("link: change %d: flags %s '%s' (%d)", ifindex, - value ? "set" : "unset", - rtnl_link_flags2str (flags, buf, sizeof (buf)), - flags); - - return do_change_link (platform, change, FALSE); +link_change_flags (NMPlatform *platform, + int ifindex, + unsigned flags_mask, + unsigned flags_set) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + char s_flags[100]; + + _LOGD ("link: change %d: flags: set 0x%x/0x%x ([%s] / [%s])", + ifindex, + flags_set, + flags_mask, + nm_platform_link_flags2str (flags_set, s_flags, sizeof (s_flags)), + nm_platform_link_flags2str (flags_mask, NULL, 0)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + flags_mask, + flags_set); + if (!nlmsg) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return do_change_link (platform, ifindex, nlmsg); } static gboolean @@ -2967,7 +3996,7 @@ link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { NMPlatformError plerr; - plerr = link_change_flags (platform, ifindex, IFF_UP, TRUE); + plerr = link_change_flags (platform, ifindex, IFF_UP, IFF_UP); if (out_no_firmware) *out_no_firmware = plerr == NM_PLATFORM_ERROR_NO_FIRMWARE; return plerr == NM_PLATFORM_ERROR_SUCCESS; @@ -2976,19 +4005,19 @@ link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) static gboolean link_set_down (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_UP, FALSE) == NM_PLATFORM_ERROR_SUCCESS; + return link_change_flags (platform, ifindex, IFF_UP, 0) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_arp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, FALSE) == NM_PLATFORM_ERROR_SUCCESS; + return link_change_flags (platform, ifindex, IFF_NOARP, 0) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_noarp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + return link_change_flags (platform, ifindex, IFF_NOARP, IFF_NOARP) == NM_PLATFORM_ERROR_SUCCESS; } static const char * @@ -3020,19 +4049,30 @@ link_get_udev_device (NMPlatform *platform, int ifindex) static gboolean link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - 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]; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; - rtnl_link_inet6_set_addr_gen_mode (nlo, mode); - debug ("link: change %d: set IPv6 address generation mode to %s", - ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf))); - return do_change_link (platform, nlo, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + if (!_support_user_ipv6ll_get ()) { + _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); + return FALSE; } -#endif - return FALSE; + + _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if ( !nlmsg + || !_nl_msg_new_link_set_afspec (nlmsg, + mode)) + g_return_val_if_reached (FALSE); + + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -3067,16 +4107,30 @@ link_supports_vlans (NMPlatform *platform, int ifindex) static gboolean link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); - auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, length); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; gs_free char *mac = NULL; - rtnl_link_set_addr (change, nladdr); + if (!address || !length) + g_return_val_if_reached (FALSE); - _LOGD ("link: change %d: address %s (%lu bytes)", ifindex, + _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; + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + return FALSE; + + NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); + + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); } static gboolean @@ -3091,12 +4145,24 @@ link_get_permanent_address (NMPlatform *platform, static gboolean link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - rtnl_link_set_mtu (change, mtu); - debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu); + _LOGD ("link: change %d: mtu: %u", ifindex, (unsigned) mtu); - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + return FALSE; + + NLA_PUT_U32 (nlmsg, IFLA_MTU, mtu); + + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); } static char * @@ -3148,649 +4214,680 @@ vlan_add (NMPlatform *platform, int parent, int vlan_id, guint32 vlan_flags, - NMPlatformLink *out_link) + const 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; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - kernel_flags = 0; - if (vlan_flags & NM_VLAN_FLAG_REORDER_HEADERS) - kernel_flags |= VLAN_FLAG_REORDER_HDR; - if (vlan_flags & NM_VLAN_FLAG_GVRP) - kernel_flags |= VLAN_FLAG_GVRP; - if (vlan_flags & NM_VLAN_FLAG_LOOSE_BINDING) - kernel_flags |= VLAN_FLAG_LOOSE_BINDING; + G_STATIC_ASSERT (NM_VLAN_FLAG_REORDER_HEADERS == (guint32) VLAN_FLAG_REORDER_HDR); + G_STATIC_ASSERT (NM_VLAN_FLAG_GVRP == (guint32) VLAN_FLAG_GVRP); + G_STATIC_ASSERT (NM_VLAN_FLAG_LOOSE_BINDING == (guint32) VLAN_FLAG_LOOSE_BINDING); + G_STATIC_ASSERT (NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP); - rtnl_link_set_link (rtnllink, parent); - rtnl_link_vlan_set_id (rtnllink, vlan_id); - rtnl_link_vlan_set_flags (rtnllink, kernel_flags); + vlan_flags &= (guint32) NM_VLAN_FLAGS_ALL; - debug ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)", - name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags); + _LOGD ("link: add vlan '%s', parent %d, vlan id %d, flags %X", + name, parent, vlan_id, (unsigned int) vlan_flags); - 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) -{ - 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 = p; - if (vlan_id) - *vlan_id = v; - return !!obj; -} - -static gboolean -vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) -{ - auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); - - 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 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 = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; - rtnl_link_set_type (change, "vlan"); - rtnl_link_vlan_set_egress_map (change, from, to); + NLA_PUT_U32 (nlmsg, IFLA_LINK, parent); - debug ("link: change %d: vlan egress map %d -> %d", ifindex, from, to); + if (!_nl_msg_new_link_set_linkinfo_vlan (nlmsg, + vlan_id, + NM_VLAN_FLAGS_ALL, + vlan_flags, + NULL, + 0, + NULL, + 0)) + return FALSE; - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + return do_add_link_with_lookup (platform, NM_LINK_TYPE_VLAN, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -static gboolean -link_enslave (NMPlatform *platform, int master, int slave) -{ - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (slave, NULL); +static int +link_gre_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkGre *props, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + char buffer[INET_ADDRSTRLEN]; + + _LOGD (LOG_FMT_IP_TUNNEL, + "gre", + name, + props->parent_ifindex, + nm_utils_inet4_ntop (props->local, NULL), + nm_utils_inet4_ntop (props->remote, buffer)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; - rtnl_link_set_master (change, master); - debug ("link: change %d: enslave to master %d", slave, master); + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; -} + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "gre"); -static gboolean -link_release (NMPlatform *platform, int master, int slave) -{ - return link_enslave (platform, 0, slave); -} + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; -static char * -link_option_path (NMPlatform *platform, int master, const char *category, const char *option) -{ - const char *name = nm_platform_link_get_name (platform, master); + if (props->parent_ifindex) + NLA_PUT_U32 (nlmsg, IFLA_GRE_LINK, props->parent_ifindex); + NLA_PUT_U32 (nlmsg, IFLA_GRE_LOCAL, props->local); + NLA_PUT_U32 (nlmsg, IFLA_GRE_REMOTE, props->remote); + NLA_PUT_U8 (nlmsg, IFLA_GRE_TTL, props->ttl); + NLA_PUT_U8 (nlmsg, IFLA_GRE_TOS, props->tos); + NLA_PUT_U8 (nlmsg, IFLA_GRE_PMTUDISC, !!props->path_mtu_discovery); + NLA_PUT_U32 (nlmsg, IFLA_GRE_IKEY, htonl (props->input_key)); + NLA_PUT_U32 (nlmsg, IFLA_GRE_OKEY, htonl (props->output_key)); + NLA_PUT_U32 (nlmsg, IFLA_GRE_IFLAGS, htons (props->input_flags)); + NLA_PUT_U32 (nlmsg, IFLA_GRE_OFLAGS, htons (props->output_flags)); - if (!name || !category || !option) - return NULL; + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); - return g_strdup_printf ("/sys/class/net/%s/%s/%s", - ASSERT_VALID_PATH_COMPONENT (name), - ASSERT_VALID_PATH_COMPONENT (category), - ASSERT_VALID_PATH_COMPONENT (option)); + return do_add_link_with_lookup (platform, NM_LINK_TYPE_GRE, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -static gboolean -link_set_option (NMPlatform *platform, int master, const char *category, const char *option, const char *value) -{ - gs_free char *path = link_option_path (platform, master, category, option); +static int +link_ip6tnl_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + char buffer[INET_ADDRSTRLEN]; + guint32 flowinfo; + + _LOGD (LOG_FMT_IP_TUNNEL, + "ip6tnl", + name, + props->parent_ifindex, + nm_utils_inet6_ntop (&props->local, NULL), + nm_utils_inet6_ntop (&props->remote, buffer)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; - return path && nm_platform_sysctl_set (platform, path, value); -} + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; -static char * -link_get_option (NMPlatform *platform, int master, const char *category, const char *option) -{ - gs_free char *path = link_option_path (platform, master, category, option); + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "ip6tnl"); - return path ? nm_platform_sysctl_get (platform, path) : NULL; -} + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; -static const char * -master_category (NMPlatform *platform, int master) -{ - switch (nm_platform_link_get_type (platform, master)) { - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bonding"; - default: - return NULL; - } -} + if (props->parent_ifindex) + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_LINK, props->parent_ifindex); -static const char * -slave_category (NMPlatform *platform, int slave) -{ - int master = nm_platform_link_get_master (platform, slave); + if (memcmp (&props->local, &in6addr_any, sizeof (in6addr_any))) + NLA_PUT (nlmsg, IFLA_IPTUN_LOCAL, sizeof (props->local), &props->local); + if (memcmp (&props->remote, &in6addr_any, sizeof (in6addr_any))) + NLA_PUT (nlmsg, IFLA_IPTUN_REMOTE, sizeof (props->remote), &props->remote); - if (master <= 0) - return NULL; + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_TTL, props->ttl); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_ENCAP_LIMIT, props->encap_limit); - switch (nm_platform_link_get_type (platform, master)) { - case NM_LINK_TYPE_BRIDGE: - return "brport"; - default: - return NULL; - } -} + flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; + flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) + & IP6_FLOWINFO_TCLASS_MASK; + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_FLOWINFO, htonl (flowinfo)); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_PROTO, props->proto); -static gboolean -master_set_option (NMPlatform *platform, int master, const char *option, const char *value) -{ - return link_set_option (platform, master, master_category (platform, master), option, value); -} + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); -static char * -master_get_option (NMPlatform *platform, int master, const char *option) -{ - return link_get_option (platform, master, master_category (platform, master), option); + return do_add_link_with_lookup (platform, NM_LINK_TYPE_IP6TNL, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -static gboolean -slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value) -{ - return link_set_option (platform, slave, slave_category (platform, slave), option, value); -} +static int +link_ipip_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkIpIp *props, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + char buffer[INET_ADDRSTRLEN]; + + _LOGD (LOG_FMT_IP_TUNNEL, + "ipip", + name, + props->parent_ifindex, + nm_utils_inet4_ntop (props->local, NULL), + nm_utils_inet4_ntop (props->remote, buffer)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; -static char * -slave_get_option (NMPlatform *platform, int slave, const char *option) -{ - return link_get_option (platform, slave, slave_category (platform, slave), option); -} + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; -static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatformLink *out_link) -{ - 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; + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "ipip"); - obj_parent = nmp_cache_lookup_link (priv->cache, parent); - if (!obj_parent || !obj_parent->link.name[0]) - g_return_val_if_reached (FALSE); + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; - ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key); + if (props->parent_ifindex) + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_LINK, props->parent_ifindex); + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_LOCAL, props->local); + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_REMOTE, props->remote); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_TTL, props->ttl); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_TOS, props->tos); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); - 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); - if (!nm_platform_sysctl_set (platform, path, id)) - return FALSE; + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); - do_request_link (platform, 0, ifname, TRUE); - - 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; + return do_add_link_with_lookup (platform, NM_LINK_TYPE_IPIP, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -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 +static int +link_macvlan_add (NMPlatform *platform, + const char *name, + int parent, + const NMPlatformLnkMacvlan *props, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + + _LOGD ("adding %s '%s' parent %u mode %u", + props->tap ? "macvtap" : "macvlan", + name, + parent, + props->mode); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; -#define IPOIB_MODE_DATAGRAM 0 /* using unreliable datagram QPs */ -#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ + NLA_PUT_U32 (nlmsg, IFLA_LINK, parent); -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 }, -}; + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; -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; + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, props->tap ? "macvtap" : "macvlan"); - 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; + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; - info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); + NLA_PUT_U32 (nlmsg, IFLA_MACVLAN_MODE, props->mode); + NLA_PUT_U16 (nlmsg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0); - 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; - } + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); - return 0; + return do_add_link_with_lookup (platform, + props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN, + name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -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) +static int +link_sit_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkSit *props, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + char buffer[INET_ADDRSTRLEN]; + + _LOGD (LOG_FMT_IP_TUNNEL, + "sit", + name, + props->parent_ifindex, + nm_utils_inet4_ntop (props->local, NULL), + nm_utils_inet4_ntop (props->remote, buffer)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) 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 (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; - if (strstr (contents, "datagram")) - info.mode = "datagram"; - else if (strstr (contents, "connected")) - info.mode = "connected"; - g_free (contents); + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "sit"); - 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; + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; - info.p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1); - g_free (contents); + if (props->parent_ifindex) + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_LINK, props->parent_ifindex); + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_LOCAL, props->local); + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_REMOTE, props->remote); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_TTL, props->ttl); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_TOS, props->tos); + NLA_PUT_U8 (nlmsg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); - if (info.p_key < 0) - return FALSE; - } + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); - if (p_key) - *p_key = info.p_key; - if (mode) - *mode = info.mode; - return TRUE; + return do_add_link_with_lookup (platform, NM_LINK_TYPE_SIT, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } static gboolean -veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) +link_vxlan_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkVxlan *props, + const NMPlatformLink **out_link) { - const char *ifname; - int peer_ifindex; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + struct nm_ifla_vxlan_port_range port_range; - ifname = nm_platform_link_get_name (platform, ifindex); - if (!ifname) - return FALSE; + g_return_val_if_fail (props, FALSE); + + _LOGD ("link: add vxlan '%s', parent %d, vxlan id %d", + name, props->parent_ifindex, props->id); - peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (ifname); - if (peer_ifindex <= 0) + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0, + 0); + if (!nlmsg) return FALSE; - props->peer = peer_ifindex; - return TRUE; -} + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; -static gboolean -tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props) -{ - char *path, *val; - gboolean success = TRUE; + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "vxlan"); - g_return_val_if_fail (props, FALSE); + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; - memset (props, 0, sizeof (*props)); - props->owner = -1; - props->group = -1; + NLA_PUT_U32 (nlmsg, IFLA_VXLAN_ID, props->id); - if (!ifname || !nm_utils_iface_valid_name (ifname)) - return FALSE; - ifname = ASSERT_VALID_PATH_COMPONENT (ifname); + if (props->group) + NLA_PUT (nlmsg, IFLA_VXLAN_GROUP, sizeof (props->group), &props->group); + else if (memcmp (&props->group6, &in6addr_any, sizeof (in6addr_any))) + NLA_PUT (nlmsg, IFLA_VXLAN_GROUP6, sizeof (props->group6), &props->group6); - path = g_strdup_printf ("/sys/class/net/%s/owner", ifname); - 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); - if (errno) - success = FALSE; - g_free (val); - } else - success = FALSE; + if (props->local) + NLA_PUT (nlmsg, IFLA_VXLAN_LOCAL, sizeof (props->local), &props->local); + else if (memcmp (&props->local6, &in6addr_any, sizeof (in6addr_any))) + NLA_PUT (nlmsg, IFLA_VXLAN_LOCAL6, sizeof (props->local6), &props->local6); - path = g_strdup_printf ("/sys/class/net/%s/group", ifname); - 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); - if (errno) - success = FALSE; - g_free (val); - } else - success = FALSE; + if (props->parent_ifindex >= 0) + NLA_PUT_U32 (nlmsg, IFLA_VXLAN_LINK, props->parent_ifindex); - path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname); - val = nm_platform_sysctl_get (platform, path); - g_free (path); - if (val) { - gint64 flags; + if (props->src_port_min || props->src_port_max) { + port_range.low = htons (props->src_port_min); + port_range.high = htons (props->src_port_max); + NLA_PUT (nlmsg, IFLA_VXLAN_PORT_RANGE, sizeof (port_range), &port_range); + } - 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; -#endif - props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap"; - props->no_pi = !!(flags & IFF_NO_PI); - props->vnet_hdr = !!(flags & IFF_VNET_HDR); - props->multi_queue = !!(flags & IFF_MULTI_QUEUE); - } else - success = FALSE; - g_free (val); - } else - success = FALSE; + NLA_PUT_U16 (nlmsg, IFLA_VXLAN_PORT, htons (props->dst_port)); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_TOS, props->tos); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_TTL, props->ttl); + NLA_PUT_U32 (nlmsg, IFLA_VXLAN_AGEING, props->ageing); + NLA_PUT_U32 (nlmsg, IFLA_VXLAN_LIMIT, props->limit); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_LEARNING, !!props->learning); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_PROXY, !!props->proxy); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_RSC, !!props->rsc); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_L2MISS, !!props->l2miss); + NLA_PUT_U8 (nlmsg, IFLA_VXLAN_L3MISS, !!props->l3miss); - return success; -} + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); -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); + return do_add_link_with_lookup (platform, NM_LINK_TYPE_VXLAN, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -static const struct nla_policy macvlan_info_policy[IFLA_MACVLAN_MAX + 1] = { - [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, -#ifdef MACVLAN_FLAG_NOPROMISC - [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, -#endif -}; - -static int -macvlan_info_data_parser (struct nlattr *info_data, gpointer parser_data) -{ - NMPlatformMacvlanProperties *props = parser_data; - struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; - int err; +static void +_vlan_change_vlan_qos_mapping_create (gboolean is_ingress_map, + gboolean reset_all, + const NMVlanQosMapping *current_map, + guint current_n_map, + const NMVlanQosMapping *set_map, + guint set_n_map, + NMVlanQosMapping **out_map, + guint *out_n_map) +{ + NMVlanQosMapping *map; + guint i, j, len; + const guint INGRESS_RANGE_LEN = 8; + + nm_assert (out_map && !*out_map); + nm_assert (out_n_map && !*out_n_map); + + if (!reset_all) + current_n_map = 0; + else if (is_ingress_map) + current_n_map = INGRESS_RANGE_LEN; + + len = current_n_map + set_n_map; + + if (len == 0) + return; - err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data, - (struct nla_policy *) macvlan_info_policy); - if (err < 0) - return err; + map = g_new (NMVlanQosMapping, len); - switch (nla_get_u32 (tb[IFLA_MACVLAN_MODE])) { - case MACVLAN_MODE_PRIVATE: - props->mode = "private"; - break; - case MACVLAN_MODE_VEPA: - props->mode = "vepa"; - break; - case MACVLAN_MODE_BRIDGE: - props->mode = "bridge"; - break; - case MACVLAN_MODE_PASSTHRU: - props->mode = "passthru"; - break; - default: - return -NLE_PARSE_ERR; + if (current_n_map) { + if (is_ingress_map) { + /* For the ingress-map, there are only 8 entries (0 to 7). + * When the user requests to reset all entires, we don't actually + * need the cached entries, we can just explicitly clear all possible + * ones. + * + * That makes only a real difference in case our cache is out-of-date. + * + * For the egress map we cannot do that, because there are far too + * many. There we can only clear the entries that we know about. */ + for (i = 0; i < INGRESS_RANGE_LEN; i++) { + map[i].from = i; + map[i].to = 0; + } + } else { + for (i = 0; i < current_n_map; i++) { + map[i].from = current_map[i].from; + map[i].to = 0; + } + } + } + if (set_n_map) + memcpy (&map[current_n_map], set_map, sizeof (*set_map) * set_n_map); + + g_qsort_with_data (map, + len, + sizeof (*map), + _vlan_qos_mapping_cmp_from, + NULL); + + for (i = 0, j = 0; i < len; i++) { + if ( ( is_ingress_map && !VLAN_XGRESS_PRIO_VALID (map[i].from)) + || (!is_ingress_map && !VLAN_XGRESS_PRIO_VALID (map[i].to))) + continue; + if ( j > 0 + && map[j - 1].from == map[i].from) + map[j - 1] = map[i]; + else + map[j++] = map[i]; } -#ifdef MACVLAN_FLAG_NOPROMISC - props->no_promisc = !!(nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]) & MACVLAN_FLAG_NOPROMISC); -#else - props->no_promisc = FALSE; -#endif - - return 0; + *out_map = map; + *out_n_map = j; } static gboolean -macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props) +link_vlan_change (NMPlatform *platform, + int ifindex, + NMVlanFlags flags_mask, + NMVlanFlags flags_set, + gboolean ingress_reset_all, + const NMVlanQosMapping *ingress_map, + gsize n_ingress_map, + gboolean egress_reset_all, + const NMVlanQosMapping *egress_map, + gsize n_egress_map) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int err; - const NMPObject *obj; - - obj = cache_lookup_link (platform, ifindex); - if (!obj) + const NMPObject *obj_cache; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + unsigned flags; + const NMPObjectLnkVlan *lnk; + guint new_n_ingress_map = 0; + guint new_n_egress_map = 0; + gs_free NMVlanQosMapping *new_ingress_map = NULL; + gs_free NMVlanQosMapping *new_egress_map = NULL; + char s_flags[64]; + char s_ingress[256]; + char s_egress[256]; + + obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + if ( !obj_cache + || !obj_cache->_link.netlink.is_in_netlink) { + _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); return FALSE; - - props->parent_ifindex = obj->link.parent; - - err = _nl_link_parse_info_data (priv->nlh, ifindex, - macvlan_info_data_parser, props); - if (err != 0) { - warning ("(%s) could not read properties: %s", - obj->link.name, nl_geterror (err)); } - return (err == 0); -} - -/* The installed kernel headers might not have VXLAN stuff at all, or - * they might have the original properties, but not PORT, GROUP6, or LOCAL6. - * So until we depend on kernel >= 3.11, we just ignore the actual enum - * in if_link.h and define the values ourselves. - */ -#define IFLA_VXLAN_UNSPEC 0 -#define IFLA_VXLAN_ID 1 -#define IFLA_VXLAN_GROUP 2 -#define IFLA_VXLAN_LINK 3 -#define IFLA_VXLAN_LOCAL 4 -#define IFLA_VXLAN_TTL 5 -#define IFLA_VXLAN_TOS 6 -#define IFLA_VXLAN_LEARNING 7 -#define IFLA_VXLAN_AGEING 8 -#define IFLA_VXLAN_LIMIT 9 -#define IFLA_VXLAN_PORT_RANGE 10 -#define IFLA_VXLAN_PROXY 11 -#define IFLA_VXLAN_RSC 12 -#define IFLA_VXLAN_L2MISS 13 -#define IFLA_VXLAN_L3MISS 14 -#define IFLA_VXLAN_PORT 15 -#define IFLA_VXLAN_GROUP6 16 -#define IFLA_VXLAN_LOCAL6 17 -#undef IFLA_VXLAN_MAX -#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6 -/* older kernel header might not contain 'struct ifla_vxlan_port_range'. - * Redefine it. */ -struct nm_ifla_vxlan_port_range { - guint16 low; - guint16 high; -}; + lnk = obj_cache->_link.netlink.lnk ? &obj_cache->_link.netlink.lnk->_lnk_vlan : NULL; + flags = obj_cache->link.flags; + + flags_set &= flags_mask; + + _vlan_change_vlan_qos_mapping_create (TRUE, + ingress_reset_all, + lnk ? lnk->ingress_qos_map : NULL, + lnk ? lnk->n_ingress_qos_map : 0, + ingress_map, + n_ingress_map, + &new_ingress_map, + &new_n_ingress_map); + + _vlan_change_vlan_qos_mapping_create (FALSE, + egress_reset_all, + lnk ? lnk->egress_qos_map : NULL, + lnk ? lnk->n_egress_qos_map : 0, + egress_map, + n_egress_map, + &new_egress_map, + &new_n_egress_map); + + _LOGD ("link: change %d: vlan:%s%s%s", + ifindex, + flags_mask + ? nm_sprintf_buf (s_flags, " flags 0x%x/0x%x", (unsigned) flags_set, (unsigned) flags_mask) + : "", + new_n_ingress_map + ? nm_platform_vlan_qos_mapping_to_string (" ingress-qos-map", + new_ingress_map, + new_n_ingress_map, + s_ingress, + sizeof (s_ingress)) + : "", + new_n_egress_map + ? nm_platform_vlan_qos_mapping_to_string (" egress-qos-map", + new_egress_map, + new_n_egress_map, + s_egress, + sizeof (s_egress)) + : ""); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if ( !nlmsg + || !_nl_msg_new_link_set_linkinfo_vlan (nlmsg, + -1, + flags_mask, + flags_set, + new_ingress_map, + new_n_ingress_map, + new_egress_map, + new_n_egress_map)) + g_return_val_if_reached (FALSE); -static const struct nla_policy vxlan_info_policy[IFLA_VXLAN_MAX + 1] = { - [IFLA_VXLAN_ID] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr) }, - [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL6] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr) }, - [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, - [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, - [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, - [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, - [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, - [IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct nm_ifla_vxlan_port_range) }, - [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, - [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, - [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, - [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, - [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, -}; + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +} static int -vxlan_info_data_parser (struct nlattr *info_data, gpointer parser_data) +tun_add (NMPlatform *platform, const char *name, gboolean tap, + gint64 owner, gint64 group, gboolean pi, gboolean vnet_hdr, + gboolean multi_queue, const NMPlatformLink **out_link) { - NMPlatformVxlanProperties *props = parser_data; - struct nlattr *tb[IFLA_VXLAN_MAX + 1]; - struct nm_ifla_vxlan_port_range *range; - int err; + const NMPObject *obj; + struct ifreq ifr = { }; + int fd; - err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data, - (struct nla_policy *) vxlan_info_policy); - if (err < 0) - return err; + _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, + tap ? "tap" : "tun", name, owner, group); - memset (props, 0, sizeof (*props)); + fd = open ("/dev/net/tun", O_RDWR); + if (fd < 0) + return FALSE; - if (tb[IFLA_VXLAN_LINK]) - props->parent_ifindex = nla_get_u32 (tb[IFLA_VXLAN_LINK]); - if (tb[IFLA_VXLAN_ID]) - props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]); - if (tb[IFLA_VXLAN_GROUP]) - props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]); - if (tb[IFLA_VXLAN_LOCAL]) - props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]); - if (tb[IFLA_VXLAN_GROUP6]) - memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6)); - if (tb[IFLA_VXLAN_LOCAL6]) - memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6)); + strncpy (ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_flags = tap ? IFF_TAP : IFF_TUN; - if (tb[IFLA_VXLAN_AGEING]) - props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]); - if (tb[IFLA_VXLAN_LIMIT]) - props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]); - if (tb[IFLA_VXLAN_TOS]) - props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]); - if (tb[IFLA_VXLAN_TTL]) - props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]); + if (!pi) + ifr.ifr_flags |= IFF_NO_PI; + if (vnet_hdr) + ifr.ifr_flags |= IFF_VNET_HDR; + if (multi_queue) + ifr.ifr_flags |= NM_IFF_MULTI_QUEUE; - if (tb[IFLA_VXLAN_PORT]) - props->dst_port = nla_get_u16 (tb[IFLA_VXLAN_PORT]); + if (ioctl (fd, TUNSETIFF, &ifr)) { + close (fd); + return FALSE; + } - if (tb[IFLA_VXLAN_PORT_RANGE]) { - range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]); - props->src_port_min = range->low; - props->src_port_max = range->high; + if (owner >= 0 && owner < G_MAXINT32) { + if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) { + close (fd); + return FALSE; + } } - if (tb[IFLA_VXLAN_LEARNING]) - props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]); - if (tb[IFLA_VXLAN_PROXY]) - props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]); - if (tb[IFLA_VXLAN_RSC]) - props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]); - if (tb[IFLA_VXLAN_L2MISS]) - props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]); - if (tb[IFLA_VXLAN_L3MISS]) - props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]); + if (group >= 0 && group < G_MAXINT32) { + if (ioctl (fd, TUNSETGROUP, (gid_t) group)) { + close (fd); + return FALSE; + } + } - return 0; + if (ioctl (fd, TUNSETPERSIST, 1)) { + close (fd); + return FALSE; + } + do_request_link (platform, 0, name); + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, name, FALSE, + tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, + NULL, NULL); + if (out_link) + *out_link = obj ? &obj->link : NULL; + + return !!obj; } static gboolean -vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props) +link_enslave (NMPlatform *platform, int master, int slave) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int err; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int ifindex = slave; - err = _nl_link_parse_info_data (priv->nlh, ifindex, - vxlan_info_data_parser, props); - if (err != 0) { - warning ("(%s) could not read vxlan properties: %s", - nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); - } - return (err == 0); -} - -static const struct nla_policy gre_info_policy[IFLA_GRE_MAX + 1] = { - [IFLA_GRE_LINK] = { .type = NLA_U32 }, - [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_IKEY] = { .type = NLA_U32 }, - [IFLA_GRE_OKEY] = { .type = NLA_U32 }, - [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, - [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, - [IFLA_GRE_TTL] = { .type = NLA_U8 }, - [IFLA_GRE_TOS] = { .type = NLA_U8 }, - [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, -}; + _LOGD ("link: change %d: enslave: master %d", slave, master); -static int -gre_info_data_parser (struct nlattr *info_data, gpointer parser_data) -{ - NMPlatformGreProperties *props = parser_data; - struct nlattr *tb[IFLA_GRE_MAX + 1]; - int err; + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + return FALSE; - err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, - (struct nla_policy *) gre_info_policy); - if (err < 0) - return err; + NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); - props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0; - props->input_flags = nla_get_u16 (tb[IFLA_GRE_IFLAGS]); - props->output_flags = nla_get_u16 (tb[IFLA_GRE_OFLAGS]); - props->input_key = (props->input_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_IKEY]) : 0; - props->output_key = (props->output_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_OKEY]) : 0; - props->local = nla_get_u32 (tb[IFLA_GRE_LOCAL]); - props->remote = nla_get_u32 (tb[IFLA_GRE_REMOTE]); - props->tos = nla_get_u8 (tb[IFLA_GRE_TOS]); - props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]); - props->path_mtu_discovery = !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]); + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); +} - return 0; +static gboolean +link_release (NMPlatform *platform, int master, int slave) +{ + return link_enslave (platform, 0, slave); } +/******************************************************************/ + static gboolean -gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props) +infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int err; + const NMPObject *obj_parent; + const NMPObject *obj; + gs_free char *path = NULL; + gs_free char *id = NULL; + gs_free char *ifname = NULL; - err = _nl_link_parse_info_data (priv->nlh, ifindex, - gre_info_data_parser, props); - if (err != 0) { - warning ("(%s) could not read gre properties: %s", - nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); - } - return (err == 0); + obj_parent = nmp_cache_lookup_link (priv->cache, parent); + if (!obj_parent || !obj_parent->link.name[0]) + g_return_val_if_reached (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 (obj_parent->link.name)); + id = g_strdup_printf ("0x%04x", p_key); + if (!nm_platform_sysctl_set (platform, path, id)) + return FALSE; + + do_request_link (platform, 0, ifname); + + 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) + *out_link = obj ? &obj->link : NULL; + return !!obj; } +/******************************************************************/ + static WifiData * wifi_get_wifi_data (NMPlatform *platform, int ifindex) { @@ -3846,16 +4943,6 @@ wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid) return wifi_utils_get_bssid (wifi_data, bssid); } -static GByteArray * -wifi_get_ssid (NMPlatform *platform, int ifindex) -{ - WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); - - if (!wifi_data) - return NULL; - return wifi_utils_get_ssid (wifi_data); -} - static guint32 wifi_get_frequency (NMPlatform *platform, int ifindex) { @@ -3906,6 +4993,15 @@ wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode) wifi_utils_set_mode (wifi_data, mode); } +static void +wifi_set_powersave (NMPlatform *platform, int ifindex, guint32 powersave) +{ + WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); + + if (wifi_data) + wifi_utils_set_powersave (wifi_data, powersave); +} + static guint32 wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs) { @@ -3926,6 +5022,7 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru wifi_utils_indicate_addressing_running (wifi_data, running); } +/******************************************************************/ static guint32 mesh_get_channel (NMPlatform *platform, int ifindex) @@ -3960,6 +5057,8 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) return wifi_utils_set_mesh_ssid (wifi_data, ssid, len); } +/******************************************************************/ + static gboolean link_get_wake_on_lan (NMPlatform *platform, int ifindex) { @@ -4019,208 +5118,124 @@ ip6_address_get_all (NMPlatform *platform, int ifindex) return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); } -#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) -#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) - -static gboolean -ip4_is_link_local (const struct in_addr *src) -{ - return (src->s_addr & IPV4LL_NETMASK) == IPV4LL_NETWORK; -} - -static struct nl_object * -build_rtnl_addr (NMPlatform *platform, - int family, - int ifindex, - gconstpointer addr, - gconstpointer peer_addr, - int plen, - guint32 lifetime, - guint32 preferred, - guint flags, - const char *label) -{ - 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 = _nl_addr_build (family, addr, addrlen); - int nle; - - /* IP address */ - nle = rtnl_addr_set_local (rtnladdr, nladdr); - if (nle) { - error ("build_rtnl_addr(): rtnl_addr_set_local failed with %s (%d)", nl_geterror (nle), nle); - return NULL; - } - - /* Tighten scope (IPv4 only) */ - if (family == AF_INET && ip4_is_link_local (addr)) - rtnl_addr_set_scope (rtnladdr, RT_SCOPE_LINK); - - /* IPv4 Broadcast address */ - if (family == AF_INET) { - in_addr_t bcast; - auto_nl_addr struct nl_addr *bcaddr = NULL; - - bcast = *((in_addr_t *) addr) | ~nm_utils_ip4_prefix_to_netmask (plen); - 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 = _nl_addr_build (family, peer_addr, addrlen); - - nle = rtnl_addr_set_peer (rtnladdr, nlpeer); - if (nle && nle != -NLE_AF_NOSUPPORT) { - /* IPv6 doesn't support peer addresses yet */ - error ("build_rtnl_addr(): rtnl_addr_set_peer failed with %s (%d)", nl_geterror (nle), nle); - return NULL; - } - } - - _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) && !_support_kernel_extended_ifa_flags_get ()) { - /* Older kernels don't accept unknown netlink attributes. - * - * With commit libnl commit 5206c050504f8676a24854519b9c351470fb7cc6, libnl will only set - * the extended address flags attribute IFA_FLAGS when necessary (> 8 bit). But it's up to - * us not to shove those extended flags on to older kernels. - * - * Just silently clear them. The kernel should ignore those unknown flags anyway. */ - flags &= 0xFF; - } - rtnl_addr_set_flags (rtnladdr, flags); - } - if (label && *label) - rtnl_addr_set_label (rtnladdr, label); - - rtnladdr_copy = rtnladdr; - rtnladdr = NULL; - 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, in_addr_t addr, - in_addr_t peer_addr, int plen, + in_addr_t peer_addr, guint32 lifetime, guint32 preferred, const char *label) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = 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); + nlmsg = _nl_msg_new_address (RTM_NEWADDR, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET, + ifindex, + &addr, + plen, + &peer_addr, + 0, + ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, + lifetime, + preferred, + label); + + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean ip6_address_add (NMPlatform *platform, int ifindex, struct in6_addr addr, - struct in6_addr peer_addr, int plen, + struct in6_addr peer_addr, guint32 lifetime, guint32 preferred, guint flags) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_address (RTM_NEWADDR, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET6, + ifindex, + &addr, + plen, + &peer_addr, + flags, + RT_SCOPE_UNIVERSE, + lifetime, + preferred, + 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); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; + + nlmsg = _nl_msg_new_address (RTM_DELADDR, + 0, + AF_INET, + ifindex, + &addr, + plen, + &peer_address, + 0, + RT_SCOPE_NOWHERE, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + NULL); + if (!nlmsg) + g_return_val_if_reached (FALSE); - 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); + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); + return do_delete_object (platform, &obj_id, nlmsg); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; + + nlmsg = _nl_msg_new_address (RTM_DELADDR, + 0, + AF_INET6, + ifindex, + &addr, + plen, + NULL, + 0, + RT_SCOPE_NOWHERE, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + NULL); + if (!nlmsg) + g_return_val_if_reached (FALSE); - nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); - return do_delete_object (platform, &obj_needle, NULL); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + return do_delete_object (platform, &obj_id, nlmsg); } static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; - 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); + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip4_address; return NULL; @@ -4229,11 +5244,11 @@ ip4_address_get (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) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; - 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); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip6_address; return NULL; @@ -4292,113 +5307,29 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags fl return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); } -static void -clear_host_address (int family, const void *network, int plen, void *dst) -{ - g_return_if_fail (plen == (guint8)plen); - g_return_if_fail (network); - - switch (family) { - case AF_INET: - *((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen); - break; - case AF_INET6: - nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen); - break; - default: - g_assert_not_reached (); - } -} - -static struct nl_object * -build_rtnl_route (int family, int ifindex, NMIPConfigSource source, - gconstpointer network, int plen, gconstpointer gateway, - gconstpointer pref_src, - guint32 metric, guint32 mss) -{ - guint32 network_clean[4]; - struct rtnl_route *rtnlroute; - struct rtnl_nexthop *nexthop; - 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 ? _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 = _nl_addr_build (family, network_clean, plen ? addrlen : 0); - nl_addr_set_prefixlen (dst, plen); - - 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, _nm_ip_config_source_to_rtprot (source)); - - 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); - if (pref_src_nl) - rtnl_route_set_pref_src (rtnlroute, pref_src_nl); - rtnl_route_add_nexthop (rtnlroute, nexthop); - - if (mss > 0) - rtnl_route_set_metric (rtnlroute, RTAX_ADVMSS, mss); - - return (struct nl_object *) rtnlroute; -} - -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformIP4Route *obj = (const NMPlatformIP4Route *) _obj; - - return build_rtnl_route (AF_INET, - obj->ifindex, - obj->source, - &obj->network, - obj->plen, - &obj->gateway, - obj->pref_src ? &obj->pref_src : NULL, - obj->metric, - obj->mss); -} - -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformIP6Route *obj = (const NMPlatformIP6Route *) _obj; - - return build_rtnl_route (AF_INET6, - obj->ifindex, - obj->source, - &obj->network, - obj->plen, - &obj->gateway, - NULL, - obj->metric, - obj->mss); -} - static gboolean ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, - guint32 pref_src, guint32 metric, guint32 mss) + in_addr_t pref_src, guint32 metric, guint32 mss) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = 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); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET, + ifindex, + source, + gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + &network, + plen, + &gateway, + metric, + mss, + pref_src ? &pref_src : NULL); + + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean @@ -4406,30 +5337,34 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - metric = nm_utils_ip6_route_metric_normalize (metric); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET6, + ifindex, + source, + !IN6_IS_ADDR_UNSPECIFIED (&gateway) ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + &network, + plen, + &gateway, + metric, + mss, + NULL); - 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); + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + return do_add_addrroute (platform, &obj_id, nlmsg); } 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; - 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; - const NMPObject *obj; - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; - g_return_val_if_fail (nlo, FALSE); - - nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); if (metric == 0) { /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. @@ -4439,94 +5374,81 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * 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); - } - - 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 - * searches for a matching scope, unless the RTM_DELROUTE message - * specifies RT_SCOPE_NOWHERE (see fib_table_delete()). - * - * However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or - * leave it unset), rtnl_route_build_msg() will reset the scope to - * rtnl_route_guess_scope() -- which probably guesses wrong. - * - * As a workaround, we look at the cached route and use that scope. - * - * Newer versions of libnl, no longer reset the scope if explicitly set to RT_SCOPE_NOWHERE. - * So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE. - **/ - if (obj) - scope = nm_platform_route_scope_inv (obj->ip4_route.scope_inv); + if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) { + /* hmm... we are about to delete an IP4 route with metric 0. We must only + * send the delete request if such a route really exists. Above we refreshed + * the platform cache, still no such route exists. + * + * Be extra careful and reload the routes. We must be sure that such a + * route doesn't exists, because when we add an IPv4 address, we immediately + * afterwards try to delete the kernel-added device route with metric 0. + * It might be, that we didn't yet get the notification about that route. + * + * FIXME: once our ip4_address_add() is sure that upon return we have + * the latest state from in the platform cache, we might save this + * additional expensive cache-resync. */ + do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE); - if (scope == RT_SCOPE_NOWHERE) { - /* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope. - * But probably it will guess 'link' because we set the next hop of the route - * to zero (0.0.0.0). A better guess is 'global'. */ - scope = RT_SCOPE_UNIVERSE; + if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) + return TRUE; } } - 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. */ - /* The following fields are also relevant when comparing the route, but the default values - * are already as we want them: - * - * type: RTN_UNICAST (setting to zero would ignore the type, but we only want to delete RTN_UNICAST) - * pref_src: NULL - */ + nlmsg = _nl_msg_new_route (RTM_DELROUTE, + 0, + AF_INET, + ifindex, + NM_IP_CONFIG_SOURCE_UNKNOWN, + RT_SCOPE_NOWHERE, + &network, + plen, + NULL, + metric, + 0, + NULL); + if (!nlmsg) + return FALSE; - return do_delete_object (platform, &obj_needle, nlo); + return do_delete_object (platform, &obj_id, nlmsg); } 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; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; metric = nm_utils_ip6_route_metric_normalize (metric); - nlo = build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0); + nlmsg = _nl_msg_new_route (RTM_DELROUTE, + 0, + AF_INET6, + ifindex, + NM_IP_CONFIG_SOURCE_UNKNOWN, + RT_SCOPE_NOWHERE, + &network, + plen, + NULL, + metric, + 0, + NULL); + if (!nlmsg) + return FALSE; - nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - return do_delete_object (platform, &obj_needle, nlo); + return do_delete_object (platform, &obj_id, nlmsg); } static const NMPlatformIP4Route * ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; - 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); + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip4_route; return NULL; @@ -4535,13 +5457,13 @@ ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, g static const NMPlatformIP6Route * ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; metric = nm_utils_ip6_route_metric_normalize (metric); - 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); + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip6_route; return NULL; @@ -4553,22 +5475,6 @@ ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int p #define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) -static int -verify_source (struct nl_msg *msg, gpointer user_data) -{ - struct ucred *creds = nlmsg_get_creds (msg); - - if (!creds || creds->pid) { - if (creds) - warning ("netlink: received non-kernel message (pid %d)", creds->pid); - else - warning ("netlink: received message without credentials"); - return NL_STOP; - } - - return NL_OK; -} - static gboolean event_handler (GIOChannel *channel, GIOCondition io_condition, @@ -4578,148 +5484,288 @@ event_handler (GIOChannel *channel, return TRUE; } -static gboolean -event_handler_read_netlink_one (NMPlatform *platform) +/*****************************************************************************/ + +/* copied from libnl3's recvmsgs() */ +static int +event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; + struct nl_sock *sk = priv->nlh; + int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; + unsigned char *buf = NULL; + struct nlmsghdr *hdr; + WaitForNlResponseResult seq_result; + + /* + nla is passed on to not only to nl_recv() but may also be passed + to a function pointer provided by the caller which may or may not + initialize the variable. Thomas Graf. + */ + struct sockaddr_nl nla = {0}; + struct nl_msg *msg = NULL; + struct ucred *creds = NULL; +continue_reading: errno = 0; - nle = nl_recvmsgs_default (priv->nlh_event); + n = nl_recv (sk, &nla, &buf, &creds); /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ - if (nle == 0 && errno == EAGAIN) { + if (n == 0 && errno == EAGAIN) { /* EAGAIN is equal to EWOULDBLOCK. If it would not be, we'd have to * workaround libnl3 mapping EWOULDBLOCK to -NLE_FAILURE. */ G_STATIC_ASSERT (EAGAIN == EWOULDBLOCK); - nle = -NLE_AGAIN; + n = -NLE_AGAIN; } - if (nle < 0) - switch (nle) { - case -NLE_AGAIN: - return FALSE; - case -NLE_DUMP_INTR: - debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); - break; - case -NLE_NOMEM: - info ("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_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); - break; + if (n <= 0) + return n; + + if (!handle_events) { + /* we read until failure or there is nothing to read (EAGAIN). */ + goto continue_reading; } - return TRUE; + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok (hdr, n)) { + gboolean abort_parsing = FALSE; + + nlmsg_free (msg); + msg = nlmsg_convert (hdr); + if (!msg) { + err = -NLE_NOMEM; + goto out; + } + + nlmsg_set_proto (msg, NETLINK_ROUTE); + nlmsg_set_src (msg, &nla); + nrecv++; + + if (!creds || creds->pid) { + if (creds) + _LOGD ("netlink: recvmsg: received non-kernel message (pid %d)", creds->pid); + else + _LOGD ("netlink: recvmsg: received message without credentials"); + goto stop; + } + + _LOGt ("netlink: recvmsg: new message type %d, seq %u", + hdr->nlmsg_type, hdr->nlmsg_seq); + + if (creds) + nlmsg_set_creds (msg, creds); + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + + /* Other side wishes to see an ack for this message */ + if (hdr->nlmsg_flags & NLM_F_ACK) { + /* FIXME: implement */ + } + + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + + if (hdr->nlmsg_type == NLMSG_DONE) { + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + multipart = 0; + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + } else if (hdr->nlmsg_type == NLMSG_NOOP) { + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + } else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by retuning + * NL_SKIP or NL_PROCEED (dangerous) */ + err = -NLE_MSG_OVERFLOW; + abort_parsing = TRUE; + } else if (hdr->nlmsg_type == NLMSG_ERROR) { + /* Message carries a nlmsgerr */ + struct nlmsgerr *e = nlmsg_data (hdr); + + if (hdr->nlmsg_len < nlmsg_size (sizeof (*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + err = -NLE_MSG_TRUNC; + abort_parsing = TRUE; + } else if (e->error) { + int errsv = e->error > 0 ? e->error : -e->error; + + /* Error message reported back from kernel. */ + _LOGD ("netlink: recvmsg: error message from kernel: %s (%d) for request %d", + strerror (errsv), + errsv, + nlmsg_hdr (msg)->nlmsg_seq); + seq_result = -errsv; + } else + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + event_valid_msg (platform, msg); + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + } + + event_seq_check (platform, msg, seq_result); + err = 0; + hdr = nlmsg_next (hdr, &n); + + if (abort_parsing) + goto out; + } + + nlmsg_free (msg); + free (buf); + free (creds); + buf = NULL; + msg = NULL; + creds = NULL; + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + goto continue_reading; + } +stop: + err = 0; +out: + nlmsg_free (msg); + free (buf); + free (creds); + + if (interrupted) + err = -NLE_DUMP_INTR; + + if (!err) + err = nrecv; + + return err; } +/*****************************************************************************/ + static gboolean -event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks) +event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int r; + int r, nle; struct pollfd pfd; gboolean any = FALSE; - gint64 timestamp = 0, now; - const int TIMEOUT = 250; - int timeout = 0; - guint32 wait_for_seq = 0; + gint64 now_ns; + int timeout_ms; + guint i; + struct { + guint32 seq_number; + gint64 timeout_abs_ns; + } data_next; while (TRUE) { - while (event_handler_read_netlink_one (platform)) + + while (TRUE) { + + nle = event_handler_recvmsgs (platform, TRUE); + + if (nle < 0) + switch (nle) { + case -NLE_AGAIN: + goto after_read; + case -NLE_DUMP_INTR: + _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); + break; + case -NLE_NOMEM: + _LOGI ("netlink: read: 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. */ + delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); + event_handler_recvmsgs (platform, FALSE); + 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: + _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); + break; + } 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); +after_read: + + if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) 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; - } + now_ns = 0; + data_next.seq_number = 0; + data_next.timeout_abs_ns = 0; + + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) { + DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if (data->seq_result) + delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result); + else if ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) > data->timeout_abs_ns) + delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); + else { + i++; - /* readjust the wait-time. */ - timeout = TIMEOUT - (now - timestamp); + if ( data_next.seq_number == 0 + || data_next.timeout_abs_ns > data->timeout_abs_ns) + data_next.seq_number = data->seq_number; + data_next.timeout_abs_ns = data->timeout_abs_ns; + } } + if ( !wait_for_acks + || !NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) + return any; + + nm_assert (data_next.seq_number); + nm_assert (data_next.timeout_abs_ns > 0); + nm_assert (now_ns > 0); + + _LOGT ("netlink: read: wait for ACK for sequence number %u...", data_next.seq_number); + + timeout_ms = (data_next.timeout_abs_ns - now_ns) / (NM_UTILS_NS_PER_SECOND / 1000); + memset (&pfd, 0, sizeof (pfd)); - pfd.fd = nl_socket_get_fd (priv->nlh_event); + pfd.fd = nl_socket_get_fd (priv->nlh); pfd.events = POLLIN; - r = poll (&pfd, 1, timeout); + r = poll (&pfd, 1, MAX (1, timeout_ms)); if (r == 0) { - /* timeout. */ - break; + /* timeout and there is nothing to read. */ + goto after_read; } if (r < 0) { int errsv = errno; if (errsv != EINTR) { - _LOGE ("read-netlink-all: poll failed with %s", strerror (errsv)); + _LOGE ("netlink: read: poll failed with %s", strerror (errsv)); + delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL); 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) -{ - struct nl_sock *sock; - int nle; - - sock = nl_socket_alloc (); - g_return_val_if_fail (sock, NULL); - - /* Only ever accept messages from kernel */ - nle = nl_socket_modify_cb (sock, NL_CB_MSG_IN, NL_CB_CUSTOM, verify_source, user_data); - g_assert (!nle); - - /* 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_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); - g_assert (!nle); - nle = nl_socket_set_passcred (sock, 1); - g_assert (!nle); - - /* No blocking for event socket, so that we can drain it safely. */ - if (event) { - nle = nl_socket_set_nonblocking (sock); - g_assert (!nle); - } - - return sock; } /******************************************************************/ @@ -4728,12 +5774,12 @@ 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; + nm_auto_nmpobj 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); + do_emit_signal (platform, obj_cache, cache_op, was_visible); } static void @@ -4745,23 +5791,23 @@ udev_device_added (NMPlatform *platform, ifname = g_udev_device_get_name (udev_device); if (!ifname) { - debug ("udev-add: failed to get device's interface"); + _LOGD ("udev-add: failed to get device's interface"); return; } if (g_udev_device_get_property (udev_device, "IFINDEX")) ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); else { - warning ("(%s): udev-add: failed to get device's ifindex", ifname); + _LOGW ("(%s): udev-add: failed to get device's ifindex", ifname); return; } if (ifindex <= 0) { - warning ("(%s): udev-add: retrieved invalid IFINDEX=%d", ifname, ifindex); + _LOGW ("(%s): udev-add: retrieved invalid IFINDEX=%d", ifname, ifindex); return; } if (!g_udev_device_get_sysfs_path (udev_device)) { - debug ("(%s): udev-add: couldn't determine device path; ignoring...", ifname); + _LOGD ("(%s): udev-add: couldn't determine device path; ignoring...", ifname); return; } @@ -4791,7 +5837,7 @@ udev_device_removed (NMPlatform *platform, ifindex = obj->link.ifindex; } - debug ("udev-remove: IFINDEX=%d", ifindex); + _LOGD ("udev-remove: IFINDEX=%d", ifindex); if (ifindex <= 0) return; @@ -4817,9 +5863,9 @@ handle_udev_event (GUdevClient *client, ifindex = g_udev_device_get_property (udev_device, "IFINDEX"); seqnum = g_udev_device_get_seqnum (udev_device); - debug ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, - action, subsys, g_udev_device_get_name (udev_device), - ifindex ? ifindex : "unknown", seqnum); + _LOGD ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, + action, subsys, g_udev_device_get_name (udev_device), + ifindex ? ifindex : "unknown", seqnum); if (!strcmp (action, "add") || !strcmp (action, "move")) udev_device_added (platform, udev_device); @@ -4836,9 +5882,11 @@ nm_linux_platform_init (NMLinuxPlatform *self) self->priv = priv; + priv->nlh_seq_next = 1; 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->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); } @@ -4856,29 +5904,37 @@ constructed (GObject *_object) _LOGD ("create"); - /* Initialize netlink socket for requests */ - priv->nlh = setup_socket (FALSE, platform); + priv->nlh = nl_socket_alloc (); g_assert (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); - g_assert (priv->nlh_event); + nle = nl_connect (priv->nlh, NETLINK_ROUTE); + g_assert (!nle); + nle = nl_socket_set_passcred (priv->nlh, 1); + g_assert (!nle); + + /* No blocking for event socket, so that we can drain it safely. */ + nle = nl_socket_set_nonblocking (priv->nlh); + g_assert (!nle); + /* The default buffer size wasn't enough for the testsuites. It might just * as well happen with NetworkManager itself. For now let's hope 128KB is * good enough. + * + * FIXME: it's unclear that this is still actually needed. The testsuite + * certainly doesn't fail for me. Maybe it can be removed. */ - nle = nl_socket_set_buffer_size (priv->nlh_event, 131072, 0); + nle = nl_socket_set_buffer_size (priv->nlh, 131072, 0); g_assert (!nle); - nle = nl_socket_add_memberships (priv->nlh_event, + + nle = nl_socket_add_memberships (priv->nlh, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); g_assert (!nle); - 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)); + _LOGD ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), nl_socket_get_fd (priv->nlh)); - priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh_event)); + priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh)); g_io_channel_set_encoding (priv->event_channel, NULL, NULL); g_io_channel_set_close_on_unref (priv->event_channel, TRUE); @@ -4931,12 +5987,12 @@ dispose (GObject *object) _LOGD ("dispose"); + delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); + 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_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); @@ -4951,16 +6007,21 @@ nm_linux_platform_finalize (GObject *object) g_ptr_array_unref (priv->delayed_action.list_master_connected); g_ptr_array_unref (priv->delayed_action.list_refresh_link); + g_array_unref (priv->delayed_action.list_wait_for_nl_response); /* 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); g_object_unref (priv->udev_client); g_hash_table_unref (priv->wifi_data); + if (priv->sysctl_get_prev_values) { + sysctl_clear_cache_list = g_slist_remove (sysctl_clear_cache_list, object); + g_hash_table_destroy (priv->sysctl_get_prev_values); + } + G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); } @@ -4991,6 +6052,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_get_type_name = link_get_type_name; platform_class->link_get_unmanaged = link_get_unmanaged; + platform_class->link_get_lnk = link_get_lnk; + platform_class->link_refresh = link_refresh; platform_class->link_set_up = link_set_up; @@ -5017,33 +6080,23 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; - platform_class->master_set_option = master_set_option; - platform_class->master_get_option = master_get_option; - platform_class->slave_set_option = slave_set_option; - platform_class->slave_get_option = slave_get_option; platform_class->vlan_add = vlan_add; - platform_class->vlan_get_info = vlan_get_info; - platform_class->vlan_set_ingress_map = vlan_set_ingress_map; - platform_class->vlan_set_egress_map = vlan_set_egress_map; + platform_class->link_vlan_change = link_vlan_change; + platform_class->link_vxlan_add = link_vxlan_add; - platform_class->infiniband_partition_add = infiniband_partition_add; - platform_class->infiniband_get_info = infiniband_get_info; + platform_class->tun_add = tun_add; - platform_class->veth_get_properties = veth_get_properties; - platform_class->tun_get_properties = tun_get_properties; - platform_class->macvlan_get_properties = macvlan_get_properties; - platform_class->vxlan_get_properties = vxlan_get_properties; - platform_class->gre_get_properties = gre_get_properties; + platform_class->infiniband_partition_add = infiniband_partition_add; platform_class->wifi_get_capabilities = wifi_get_capabilities; platform_class->wifi_get_bssid = wifi_get_bssid; - platform_class->wifi_get_ssid = wifi_get_ssid; platform_class->wifi_get_frequency = wifi_get_frequency; platform_class->wifi_get_quality = wifi_get_quality; platform_class->wifi_get_rate = wifi_get_rate; platform_class->wifi_get_mode = wifi_get_mode; platform_class->wifi_set_mode = wifi_set_mode; + platform_class->wifi_set_powersave = wifi_set_powersave; platform_class->wifi_find_frequency = wifi_find_frequency; platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; @@ -5051,6 +6104,12 @@ 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->link_gre_add = link_gre_add; + platform_class->link_ip6tnl_add = link_ip6tnl_add; + platform_class->link_macvlan_add = link_macvlan_add; + platform_class->link_ipip_add = link_ipip_add; + platform_class->link_sit_add = link_sit_add; + platform_class->ip4_address_get = ip4_address_get; platform_class->ip6_address_get = ip6_address_get; platform_class->ip4_address_get_all = ip4_address_get_all; diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index a9e2cd82..81f9c738 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -50,4 +50,6 @@ GType nm_linux_platform_get_type (void); void nm_linux_platform_setup (void); +void _nm_linux_platform_sysctl_clear_cache (void); + #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index e9b8d4be..953ac8c5 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -29,10 +29,9 @@ #include <linux/mii.h> #include <linux/version.h> -#include "gsystem-local-alloc.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-setting-wired.h" diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 9948ac8b..3bb44e1a 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -28,20 +28,27 @@ #include <string.h> #include <netlink/route/addr.h> #include <netlink/route/rtnl.h> +#include <linux/ip.h> +#include <linux/if_tun.h> +#include <linux/if_tunnel.h> -#include "gsystem-local-alloc.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-platform.h" #include "nm-platform-utils.h" +#include "nmp-object.h" #include "NetworkManagerUtils.h" -#include "nm-logging.h" +#include "nm-default.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); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network)); #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" @@ -64,21 +71,15 @@ G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWA } \ } G_STMT_END +#define LOG_FMT_IP_TUNNEL "adding %s '%s' parent %u local %s remote %s" + +/*****************************************************************************/ + #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate)) G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) -/* NMPlatform signals */ -enum { - SIGNAL_LINK_CHANGED, - SIGNAL_IP4_ADDRESS_CHANGED, - SIGNAL_IP6_ADDRESS_CHANGED, - SIGNAL_IP4_ROUTE_CHANGED, - SIGNAL_IP6_ROUTE_CHANGED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; +static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 }; enum { PROP_0, @@ -92,10 +93,22 @@ typedef struct { /******************************************************************/ +guint +_nm_platform_signal_id_get (NMPlatformSignalIdType signal_type) +{ + nm_assert ( signal_type > 0 + && signal_type != NM_PLATFORM_SIGNAL_ID_NONE + && signal_type < _NM_PLATFORM_SIGNAL_ID_LAST); + + return signals[signal_type]; +} + +/******************************************************************/ + /* Singleton NMPlatform subclass instance and cached class object */ NM_DEFINE_SINGLETON_INSTANCE (NMPlatform); -NM_DEFINE_SINGLETON_WEAK_REF (NMPlatform); +NM_DEFINE_SINGLETON_REGISTER (NMPlatform); /* Just always initialize a @klass instance. NM_PLATFORM_GET_CLASS() * is only a plain read on the self instance, which the compiler @@ -137,9 +150,9 @@ nm_platform_setup (NMPlatform *instance) singleton_instance = instance; - nm_singleton_instance_weak_ref_register (); + nm_singleton_instance_register (); - nm_log_dbg (LOGD_CORE, "setup NMPlatform singleton (%p, %s)", instance, G_OBJECT_TYPE_NAME (instance)); + nm_log_dbg (LOGD_CORE, "setup %s singleton (%p, %s)", "NMPlatform", singleton_instance, G_OBJECT_TYPE_NAME (instance)); } /** @@ -204,22 +217,6 @@ nm_platform_error_to_string (NMPlatformError error) /******************************************************************/ -#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" -#define IFA_F_NOPREFIXROUTE_STR "noprefixroute" -gboolean -nm_platform_check_support_libnl_extended_ifa_flags () -{ - static int supported = -1; - - /* support for extended ifa-flags was added together - * with the IFA_F_MANAGETEMPADDR flag. So, check if libnl - * is able to parse this flag. */ - if (supported == -1) - supported = rtnl_addr_str2flags (IFA_F_MANAGETEMPADDR_STR) == IFA_F_MANAGETEMPADDR; - - return supported; -} - gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) { @@ -284,7 +281,6 @@ nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value) 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 (self, path, value); } @@ -336,7 +332,6 @@ nm_platform_sysctl_get (NMPlatform *self, const char *path) _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 (self, path); } @@ -416,8 +411,6 @@ nm_platform_link_get_all (NMPlatform *self) _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (klass->link_get_all, NULL); - links = klass->link_get_all (self); if (!links || links->len == 0) @@ -427,7 +420,7 @@ nm_platform_link_get_all (NMPlatform *self) for (i = 0; i < links->len; i++) { item = &g_array_index (links, NMPlatformLink, i); - _LOGt ("link-get: %3d: %s", i, nm_platform_link_to_string (item)); + _LOGt ("link-get: %3d: %s", i, nm_platform_link_to_string (item, NULL, 0)); nm_assert (item->ifindex > 0 && !g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); @@ -450,8 +443,7 @@ nm_platform_link_get_all (NMPlatform *self) if (item->parent != NM_PLATFORM_LINK_OTHER_NETNS) { g_warn_if_fail (item->parent > 0); g_warn_if_fail (item->parent != item->ifindex); - g_warn_if_fail ( !nm_platform_check_support_libnl_link_netnsid () - || g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))); + g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))); } } } @@ -483,7 +475,7 @@ nm_platform_link_get_all (NMPlatform *self) 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)); + _LOGt ("link-get: add %3d -> %3d: %s", i, j, nm_platform_link_to_string (item, NULL, 0)); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; @@ -496,7 +488,7 @@ nm_platform_link_get_all (NMPlatform *self) * 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)); + _LOGt ("link-get: add (loop) %3d -> %3d: %s", first_idx, j, nm_platform_link_to_string (item, NULL, 0)); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; @@ -575,7 +567,7 @@ nm_platform_link_get_by_address (NMPlatform *self, } static NMPlatformError -_link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, NMPlatformLink *out_link) +_link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, const NMPlatformLink **out_link) { const NMPlatformLink *pllink; @@ -590,11 +582,13 @@ _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, N wrong_type ? ", expected " : "", wrong_type ? nm_link_type_to_string (type) : ""); if (out_link) - *out_link = *pllink; + *out_link = pllink; if (wrong_type) return NM_PLATFORM_ERROR_WRONG_TYPE; return NM_PLATFORM_ERROR_EXISTS; } + if (out_link) + *out_link = NULL; return NM_PLATFORM_ERROR_SUCCESS; } @@ -623,14 +617,13 @@ nm_platform_link_add (NMPlatform *self, NMLinkType type, const void *address, size_t address_len, - NMPlatformLink *out_link) + const NMPlatformLink **out_link) { NMPlatformError plerr; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); 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); @@ -644,7 +637,7 @@ nm_platform_link_add (NMPlatform *self, } /** - * nm_platform_dummy_add: + * nm_platform_link_dummy_add: * @self: platform instance * @name: New interface name * @out_link: on success, the link object @@ -652,7 +645,9 @@ nm_platform_link_add (NMPlatform *self, * Create a software ethernet-like interface */ NMPlatformError -nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) +nm_platform_link_dummy_add (NMPlatform *self, + const char *name, + const NMPlatformLink **out_link) { return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, 0, out_link); } @@ -746,8 +741,6 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (klass->link_get_type_name, NULL); - return klass->link_get_type_name (self, ifindex); } @@ -890,22 +883,19 @@ nm_platform_link_uses_arp (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid) { + const NMPlatformLink *pllink; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (iid, FALSE); -#if HAVE_LIBNL_INET6_TOKEN - { - const NMPlatformLink *pllink; - pllink = nm_platform_link_get (self, ifindex); - if (pllink && pllink->inet6_token.is_valid) { - *iid = pllink->inet6_token.iid; - return TRUE; - } + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->inet6_token.is_valid) { + *iid = pllink->inet6_token.iid; + return TRUE; } -#endif return FALSE; } @@ -947,23 +937,18 @@ nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex) { + const NMPlatformLink *pllink; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); -#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 + 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) == NM_IN6_ADDR_GEN_MODE_NONE; return FALSE; } - /** * nm_platform_link_set_user_ip6vll_enabled: * @self: platform instance @@ -981,7 +966,6 @@ nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolea _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 (self, ifindex, enabled); @@ -1004,7 +988,6 @@ nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer addre 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); _LOGD ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (self, ifindex), ifindex); return klass->link_set_address (self, ifindex, address, length); @@ -1083,7 +1066,6 @@ 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 (self, ifindex); } @@ -1094,7 +1076,6 @@ 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 (self, ifindex); } @@ -1113,7 +1094,6 @@ nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmwar _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (klass->link_set_up, FALSE); _LOGD ("link: setting up '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); return klass->link_set_up (self, ifindex, out_no_firmware); @@ -1132,7 +1112,6 @@ nm_platform_link_set_down (NMPlatform *self, int ifindex) _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (klass->link_set_down, FALSE); _LOGD ("link: setting down '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); return klass->link_set_down (self, ifindex); @@ -1151,7 +1130,6 @@ nm_platform_link_set_arp (NMPlatform *self, int ifindex) _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_set_arp, FALSE); _LOGD ("link: setting arp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); return klass->link_set_arp (self, ifindex); @@ -1170,7 +1148,6 @@ nm_platform_link_set_noarp (NMPlatform *self, int ifindex) _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_set_noarp, FALSE); _LOGD ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); return klass->link_set_noarp (self, ifindex); @@ -1191,7 +1168,6 @@ nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) 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); _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); @@ -1306,7 +1282,6 @@ nm_platform_link_get_driver_info (NMPlatform *self, _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_get_driver_info, FALSE); return klass->link_get_driver_info (self, ifindex, @@ -1330,7 +1305,6 @@ nm_platform_link_enslave (NMPlatform *self, int master, int slave) 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); _LOGD ("link: enslaving '%s' (%d) to master '%s' (%d)", nm_platform_link_get_name (self, slave), slave, @@ -1353,7 +1327,6 @@ nm_platform_link_release (NMPlatform *self, int master, int slave) g_return_val_if_fail (master > 0, FALSE); g_return_val_if_fail (slave > 0, FALSE); - g_return_val_if_fail (klass->link_release, FALSE); if (nm_platform_link_get_master (self, slave) != master) return FALSE; @@ -1384,8 +1357,109 @@ nm_platform_link_get_master (NMPlatform *self, int slave) return pllink ? pllink->master : 0; } +/*****************************************************************************/ + /** - * nm_platform_bridge_add: + * nm_platform_link_get_lnk: + * @self: the platform instance + * @ifindex: the link ifindex to lookup + * @link_type: filter by link-type. + * @out_link: (allow-none): returns the platform link instance + * + * If the function returns %NULL, that could mean that no such ifindex + * exists, of that the link has no lnk data. You can find that out + * by checking @out_link. @out_link will always be set if a link + * with @ifindex exists. + * + * If @link_type is %NM_LINK_TYPE_NONE, the function returns the lnk + * object if it is present. If you set link-type, you can be sure + * that only a link type of the matching type is returned (or %NULL). + * + * Returns: the internal link lnk object. The returned object + * is owned by the platform cache and must not be modified. Note + * however, that the object is guaranteed to be immutable, so + * you can savely take a reference and keep it for yourself + * (but don't modify it). + */ +const NMPObject * +nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) +{ + _CHECK_SELF (self, klass, FALSE); + + NM_SET_OUT (out_link, NULL); + + g_return_val_if_fail (ifindex > 0, NULL); + + return klass->link_get_lnk (self, ifindex, link_type, out_link); +} + +static gconstpointer +_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) +{ + const NMPObject *lnk; + + lnk = nm_platform_link_get_lnk (self, ifindex, link_type, out_link); + return lnk ? &lnk->object : NULL; +} + +const NMPlatformLnkGre * +nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_GRE, out_link); +} + +const NMPlatformLnkInfiniband * +nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_INFINIBAND, out_link); +} + +const NMPlatformLnkIp6Tnl * +nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IP6TNL, out_link); +} + +const NMPlatformLnkIpIp * +nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IPIP, out_link); +} + +const NMPlatformLnkMacvlan * +nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACVLAN, out_link); +} + +const NMPlatformLnkMacvtap * +nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACVTAP, out_link); +} + +const NMPlatformLnkSit * +nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_SIT, out_link); +} + +const NMPlatformLnkVlan * +nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_VLAN, out_link); +} + +const NMPlatformLnkVxlan * +nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_VXLAN, out_link); +} + +/*****************************************************************************/ + +/** + * nm_platform_link_bridge_add: * @self: platform instance * @name: New interface name * @address: (allow-none): set the mac address of the new bridge @@ -1395,17 +1469,17 @@ nm_platform_link_get_master (NMPlatform *self, int slave) * Create a software bridge. */ NMPlatformError -nm_platform_bridge_add (NMPlatform *self, - const char *name, - const void *address, - size_t address_len, - NMPlatformLink *out_link) +nm_platform_link_bridge_add (NMPlatform *self, + const char *name, + const void *address, + size_t address_len, + const NMPlatformLink **out_link) { return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, address, address_len, out_link); } /** - * nm_platform_bond_add: + * nm_platform_link_bond_add: * @self: platform instance * @name: New interface name * @out_link: on success, the link object @@ -1413,13 +1487,15 @@ nm_platform_bridge_add (NMPlatform *self, * Create a software bonding device. */ NMPlatformError -nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) +nm_platform_link_bond_add (NMPlatform *self, + const char *name, + const NMPlatformLink **out_link) { return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, 0, out_link); } /** - * nm_platform_team_add: + * nm_platform_link_team_add: * @self: platform instance * @name: New interface name * @out_link: on success, the link object @@ -1427,13 +1503,15 @@ nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_li * Create a software teaming device. */ NMPlatformError -nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) +nm_platform_link_team_add (NMPlatform *self, + const char *name, + const NMPlatformLink **out_link) { return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, 0, out_link); } /** - * nm_platform_vlan_add: + * nm_platform_link_vlan_add: * @self: platform instance * @name: New interface name * @vlanid: VLAN identifier @@ -1443,12 +1521,12 @@ nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_li * Create a software VLAN device. */ NMPlatformError -nm_platform_vlan_add (NMPlatform *self, - const char *name, - int parent, - int vlanid, - guint32 vlanflags, - NMPlatformLink *out_link) +nm_platform_link_vlan_add (NMPlatform *self, + const char *name, + int parent, + int vlanid, + guint32 vlanflags, + const NMPlatformLink **out_link) { NMPlatformError plerr; @@ -1457,7 +1535,6 @@ nm_platform_vlan_add (NMPlatform *self, 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) @@ -1470,98 +1547,321 @@ nm_platform_vlan_add (NMPlatform *self, return NM_PLATFORM_ERROR_SUCCESS; } +/** + * nm_platform_link_vxlan_add: + * @self: platform instance + * @name: New interface name + * @props: properties of the new link + * @out_link: on success, the link object + * + * Create a VXLAN device. + */ +NMPlatformError +nm_platform_link_vxlan_add (NMPlatform *self, + const char *name, + const NMPlatformLnkVxlan *props, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VXLAN, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD ("link: adding vxlan '%s' parent %d id %d", + name, props->parent_ifindex, props->id); + if (!klass->link_vxlan_add (self, name, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + +/** + * nm_platform_link_tun_add: + * @self: platform instance + * @name: new interface name + * @tap: whether the interface is a TAP + * @owner: interface owner or -1 + * @group: interface group or -1 + * @pi: whether to clear the IFF_NO_PI flag + * @vnet_hdr: whether to set the IFF_VNET_HDR flag + * @multi_queue: whether to set the IFF_MULTI_QUEUE flag + * @out_link: on success, the link object + * + * Create a TUN or TAP interface. + */ +NMPlatformError +nm_platform_link_tun_add (NMPlatform *self, + const char *name, + gboolean tap, + gint64 owner, + gint64 group, + gboolean pi, + gboolean vnet_hdr, + gboolean multi_queue, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD ("link: adding %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, + tap ? "tap" : "tun", name, owner, group); + if (!klass->tun_add (self, name, tap, owner, group, pi, vnet_hdr, multi_queue, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + +/*****************************************************************************/ + +static char * +link_option_path (NMPlatform *self, int master, const char *category, const char *option) +{ + const char *name = nm_platform_link_get_name (self, master); + + if (!name || !category || !option) + return NULL; + + return g_strdup_printf ("/sys/class/net/%s/%s/%s", + ASSERT_VALID_PATH_COMPONENT (name), + ASSERT_VALID_PATH_COMPONENT (category), + ASSERT_VALID_PATH_COMPONENT (option)); +} + +static gboolean +link_set_option (NMPlatform *self, int master, const char *category, const char *option, const char *value) +{ + gs_free char *path = link_option_path (self, master, category, option); + + return path && nm_platform_sysctl_set (self, path, value); +} + +static char * +link_get_option (NMPlatform *self, int master, const char *category, const char *option) +{ + gs_free char *path = link_option_path (self, master, category, option); + + return path ? nm_platform_sysctl_get (self, path) : NULL; +} + +static const char * +master_category (NMPlatform *self, int master) +{ + switch (nm_platform_link_get_type (self, master)) { + case NM_LINK_TYPE_BRIDGE: + return "bridge"; + case NM_LINK_TYPE_BOND: + return "bonding"; + default: + return NULL; + } +} + +static const char * +slave_category (NMPlatform *self, int slave) +{ + int master = nm_platform_link_get_master (self, slave); + + if (master <= 0) + return NULL; + + switch (nm_platform_link_get_type (self, master)) { + case NM_LINK_TYPE_BRIDGE: + return "brport"; + default: + return NULL; + } +} + gboolean -nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) +nm_platform_sysctl_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { _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 (self, ifindex, option, value); + return link_set_option (self, ifindex, master_category (self, ifindex), option, value); } char * -nm_platform_master_get_option (NMPlatform *self, int ifindex, const char *option) +nm_platform_sysctl_master_get_option (NMPlatform *self, int ifindex, const char *option) { _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 (self, ifindex, option); + return link_get_option (self, ifindex, master_category (self, ifindex), option); } gboolean -nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) +nm_platform_sysctl_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { _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 (self, ifindex, option, value); + return link_set_option (self, ifindex, slave_category (self, ifindex), option, value); } char * -nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option) +nm_platform_sysctl_slave_get_option (NMPlatform *self, int ifindex, const char *option) { _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 (self, ifindex, option); + return link_get_option (self, ifindex, slave_category (self, ifindex), option); } +/******************************************************************************/ + gboolean -nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid) +nm_platform_link_vlan_change (NMPlatform *self, + int ifindex, + NMVlanFlags flags_mask, + NMVlanFlags flags_set, + gboolean ingress_reset_all, + const NMVlanQosMapping *ingress_map, + gsize n_ingress_map, + gboolean egress_reset_all, + const NMVlanQosMapping *egress_map, + gsize n_egress_map) { _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (klass->vlan_get_info, FALSE); + nm_assert (klass->link_vlan_change); - if (parent) - *parent = 0; - if (vlanid) - *vlanid = 0; + g_return_val_if_fail (!n_ingress_map || ingress_map, FALSE); + g_return_val_if_fail (!n_egress_map || egress_map, FALSE); - if (nm_platform_link_get_type (self, ifindex) != NM_LINK_TYPE_VLAN) - return FALSE; + flags_set &= flags_mask; + + if (_LOGD_ENABLED ()) { + char buf[512]; + char *b = buf; + gsize len, i; + + b[0] = '\0'; + len = sizeof (buf); + + if (flags_mask) + nm_utils_strbuf_append (&b, &len, " flags 0x%x/0x%x", (unsigned) flags_set, (unsigned) flags_mask); + + if (ingress_reset_all || n_ingress_map) { + nm_utils_strbuf_append_str (&b, &len, " ingress-qos-map"); + nm_platform_vlan_qos_mapping_to_string ("", ingress_map, n_ingress_map, b, len); + i = strlen (b); + b += i; + len -= i; + if (ingress_reset_all) + nm_utils_strbuf_append_str (&b, &len, " (reset-all)"); + } + + if (egress_reset_all || n_egress_map) { + nm_utils_strbuf_append_str (&b, &len, " egress-qos-map"); + nm_platform_vlan_qos_mapping_to_string ("", egress_map, n_egress_map, b, len); + i = strlen (b); + b += i; + len -= i; + if (egress_reset_all) + nm_utils_strbuf_append_str (&b, &len, " (reset-all)"); + } - return klass->vlan_get_info (self, ifindex, parent, vlanid); + _LOGD ("link: change vlan %d:%s", ifindex, buf); + } + return klass->link_vlan_change (self, + ifindex, + flags_mask, + flags_set, + ingress_reset_all, + ingress_map, + n_ingress_map, + egress_reset_all, + egress_map, + n_egress_map); } gboolean -nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to) +nm_platform_link_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to) { - _CHECK_SELF (self, klass, FALSE); - - g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE); + NMVlanQosMapping map = { + .from = from, + .to = 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); + return nm_platform_link_vlan_change (self, ifindex, 0, 0, FALSE, &map, 1, FALSE, NULL, 0); } gboolean -nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to) +nm_platform_link_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to) { - _CHECK_SELF (self, klass, FALSE); + NMVlanQosMapping map = { + .from = from, + .to = to, + }; - g_return_val_if_fail (klass->vlan_set_egress_map, FALSE); + return nm_platform_link_vlan_change (self, ifindex, 0, 0, FALSE, NULL, 0, FALSE, &map, 1); +} - _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); +/** + * nm_platform_link_gre_add: + * @self: platform instance + * @name: name of the new interface + * @props: interface properties + * @out_link: on success, the link object + * + * Create a software GRE device. + */ +NMPlatformError +nm_platform_link_gre_add (NMPlatform *self, + const char *name, + const NMPlatformLnkGre *props, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + char buffer[INET_ADDRSTRLEN]; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_GRE, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD (LOG_FMT_IP_TUNNEL, + "gre", + name, + props->parent_ifindex, + nm_utils_inet4_ntop (props->local, NULL), + nm_utils_inet4_ntop (props->remote, buffer)); + + if (!klass->link_gre_add (self, name, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } NMPlatformError -nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link) +nm_platform_link_infiniband_add (NMPlatform *self, + int parent, + int p_key, + const NMPlatformLink **out_link) { gs_free char *parent_name = NULL; gs_free char *name = NULL; @@ -1571,7 +1871,6 @@ nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, N 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); parent_name = g_strdup (nm_platform_link_get_name (self, parent)); if ( !parent_name @@ -1591,73 +1890,330 @@ nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, N } gboolean -nm_platform_infiniband_get_info (NMPlatform *self, - int ifindex, - int *parent, - int *p_key, - const char **mode) -{ +nm_platform_link_infiniband_get_properties (NMPlatform *self, + int ifindex, + int *out_parent, + int *out_p_key, + const char **out_mode) +{ + const NMPlatformLnkInfiniband *plnk; + const NMPlatformLink *plink; + const char *iface; + char *path, *contents; + const char *mode; + int p_key = 0; + _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); + plnk = nm_platform_link_get_lnk_infiniband (self, ifindex, &plink); + + if ( !plink + || plink->type != NM_LINK_TYPE_INFINIBAND) + return FALSE; + + if (plnk) { + NM_SET_OUT (out_parent, plink->parent); + NM_SET_OUT (out_p_key, plnk->p_key); + NM_SET_OUT (out_mode, plnk->mode); + return TRUE; + } + + /* Could not get the link information via netlink. To support older kernels, + * fallback to reading sysfs. */ + + iface = ASSERT_VALID_PATH_COMPONENT (plink->name); + + /* Fall back to reading sysfs */ + path = g_strdup_printf ("/sys/class/net/%s/mode", iface); + contents = nm_platform_sysctl_get (self, path); + g_free (path); + if (!contents) + return FALSE; + + if (strstr (contents, "datagram")) + mode = "datagram"; + else if (strstr (contents, "connected")) + mode = "connected"; + else + mode = NULL; + g_free (contents); + + path = g_strdup_printf ("/sys/class/net/%s/pkey", iface); + contents = nm_platform_sysctl_get (self, path); + g_free (path); + if (!contents) + return FALSE; + p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1); + g_free (contents); + if (p_key < 0) + return FALSE; + + NM_SET_OUT (out_parent, plink->parent); + NM_SET_OUT (out_p_key, p_key); + NM_SET_OUT (out_mode, mode); + return TRUE; } -gboolean -nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *props) +/** + * nm_platform_ip6tnl_add: + * @self: platform instance + * @name: name of the new interface + * @props: interface properties + * @out_link: on success, the link object + * + * Create an IPv6 tunnel. + */ +NMPlatformError +nm_platform_link_ip6tnl_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link) { - _CHECK_SELF (self, klass, FALSE); + NMPlatformError plerr; + char buffer[INET6_ADDRSTRLEN]; - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, FALSE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; - return klass->veth_get_properties (self, ifindex, props); + _LOGD (LOG_FMT_IP_TUNNEL, + "ip6tnl", + name, + props->parent_ifindex, + nm_utils_inet6_ntop (&props->local, NULL), + nm_utils_inet6_ntop (&props->remote, buffer)); + + if (!klass->link_ip6tnl_add (self, name, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } -gboolean -nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) +/** + * nm_platform_ipip_add: + * @self: platform instance + * @name: name of the new interface + * @props: interface properties + * @out_link: on success, the link object + * + * Create an IPIP tunnel. + */ +NMPlatformError +nm_platform_link_ipip_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIpIp *props, + const NMPlatformLink **out_link) { - _CHECK_SELF (self, klass, FALSE); + NMPlatformError plerr; + char buffer[INET_ADDRSTRLEN]; - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, FALSE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - return klass->tun_get_properties (self, ifindex, props); + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IPIP, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD (LOG_FMT_IP_TUNNEL, + "ipip", + name, + props->parent_ifindex, + nm_utils_inet4_ntop (props->local, NULL), + nm_utils_inet4_ntop (props->remote, buffer)); + + if (!klass->link_ipip_add (self, name, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + +/** + * nm_platform_macvlan_add: + * @self: platform instance + * @name: name of the new interface + * @props: interface properties + * @out_link: on success, the link object + * + * Create a MACVLAN or MACVTAP device. + */ +NMPlatformError +nm_platform_link_macvlan_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkMacvlan *props, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + NMLinkType type; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + type = props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN; + + plerr = _link_add_check_existing (self, name, type, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD ("adding %s '%s' parent %u mode %u", + props->tap ? "macvtap" : "macvlan", + name, + parent, + props->mode); + + if (!klass->link_macvlan_add (self, name, parent, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + +/** + * nm_platform_sit_add: + * @self: platform instance + * @name: name of the new interface + * @props: interface properties + * @out_link: on success, the link object + * + * Create a software SIT device. + */ +NMPlatformError +nm_platform_link_sit_add (NMPlatform *self, + const char *name, + const NMPlatformLnkSit *props, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + char buffer[INET_ADDRSTRLEN]; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_SIT, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD (LOG_FMT_IP_TUNNEL, + "sit", + name, + props->parent_ifindex, + nm_utils_inet4_ntop (props->local, NULL), + nm_utils_inet4_ntop (props->remote, buffer)); + + if (!klass->link_sit_add (self, name, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } gboolean -nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props) +nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex) { + const NMPlatformLink *plink; + int peer_ifindex; _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, FALSE); + plink = nm_platform_link_get (self, ifindex); + + if (!plink) + return FALSE; + if (plink->type != NM_LINK_TYPE_VETH) + return FALSE; + + if (plink->parent != 0) { + NM_SET_OUT (out_peer_ifindex, plink->parent); + return TRUE; + } + + /* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */ + if (out_peer_ifindex) { + peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->name); + if (peer_ifindex <= 0) + return FALSE; - return klass->macvlan_get_properties (self, ifindex, props); + *out_peer_ifindex = peer_ifindex; + } + return TRUE; } gboolean -nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props) +nm_platform_link_tun_get_properties_ifname (NMPlatform *self, const char *ifname, NMPlatformTunProperties *props) { + char *path, *val; + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, FALSE); + g_return_val_if_fail (props, FALSE); + + memset (props, 0, sizeof (*props)); + props->owner = -1; + props->group = -1; - return klass->vxlan_get_properties (self, ifindex, props); + 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 (self, path); + g_free (path); + if (val) { + props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + if (errno) + success = FALSE; + g_free (val); + } else + success = FALSE; + + path = g_strdup_printf ("/sys/class/net/%s/group", ifname); + val = nm_platform_sysctl_get (self, path); + g_free (path); + if (val) { + props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + if (errno) + success = FALSE; + g_free (val); + } else + success = FALSE; + + path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname); + val = nm_platform_sysctl_get (self, path); + g_free (path); + if (val) { + gint64 flags; + + flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); + if (!errno) { + props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap"; + props->no_pi = !!(flags & IFF_NO_PI); + props->vnet_hdr = !!(flags & IFF_VNET_HDR); + props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE); + } else + success = FALSE; + g_free (val); + } else + success = FALSE; + + return success; } gboolean -nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props) +nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) { _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 (self, ifindex, props); + return nm_platform_link_tun_get_properties_ifname (self, nm_platform_link_get_name (self, ifindex), props); } gboolean @@ -1680,16 +2236,6 @@ nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid) return klass->wifi_get_bssid (self, ifindex, bssid); } -GByteArray * -nm_platform_wifi_get_ssid (NMPlatform *self, int ifindex) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - - return klass->wifi_get_ssid (self, ifindex); -} - guint32 nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex) { @@ -1740,6 +2286,22 @@ nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode) klass->wifi_set_mode (self, ifindex, mode); } +static void +wifi_set_powersave (NMPlatform *p, int ifindex, guint32 powersave) +{ + /* empty */ +} + +void +nm_platform_wifi_set_powersave (NMPlatform *self, int ifindex, guint32 powersave) +{ + _CHECK_SELF_VOID (self, klass); + + g_return_if_fail (ifindex > 0); + + klass->wifi_set_powersave (self, ifindex, powersave); +} + guint32 nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs) { @@ -1818,13 +2380,29 @@ _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) /******************************************************************/ +void +nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, int plen) +{ + addr->address = address; + addr->peer_address = address; + addr->plen = plen; +} + +const struct in6_addr * +nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr) +{ + if ( IN6_IS_ADDR_UNSPECIFIED (&addr->peer_address) + || IN6_ARE_ADDR_EQUAL (&addr->peer_address, &addr->address)) + return &addr->address; + return &addr->peer_address; +} + GArray * nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); - g_return_val_if_fail (klass->ip4_address_get_all, NULL); return klass->ip4_address_get_all (self, ifindex); } @@ -1835,17 +2413,16 @@ nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) _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 (self, ifindex); } gboolean nm_platform_ip4_address_add (NMPlatform *self, - int ifindex, + int ifindex, in_addr_t address, - in_addr_t peer_address, int plen, + in_addr_t peer_address, guint32 lifetime, guint32 preferred, const char *label) @@ -1856,7 +2433,6 @@ nm_platform_ip4_address_add (NMPlatform *self, g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (lifetime > 0, FALSE); g_return_val_if_fail (preferred <= lifetime, FALSE); - g_return_val_if_fail (klass->ip4_address_add, FALSE); g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE); if (_LOGD_ENABLED ()) { @@ -1872,17 +2448,17 @@ nm_platform_ip4_address_add (NMPlatform *self, if (label) g_strlcpy (addr.label, label, sizeof (addr.label)); - _LOGD ("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, NULL, 0)); } - return klass->ip4_address_add (self, ifindex, address, peer_address, plen, lifetime, preferred, label); + return klass->ip4_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, label); } gboolean nm_platform_ip6_address_add (NMPlatform *self, int ifindex, struct in6_addr address, - struct in6_addr peer_address, int plen, + struct in6_addr peer_address, guint32 lifetime, guint32 preferred, guint flags) @@ -1893,7 +2469,6 @@ nm_platform_ip6_address_add (NMPlatform *self, g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (lifetime > 0, FALSE); g_return_val_if_fail (preferred <= lifetime, FALSE); - g_return_val_if_fail (klass->ip6_address_add, FALSE); if (_LOGD_ENABLED ()) { NMPlatformIP6Address addr = { 0 }; @@ -1907,28 +2482,27 @@ nm_platform_ip6_address_add (NMPlatform *self, addr.preferred = preferred; addr.flags = flags; - _LOGD ("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, NULL, 0)); } - return klass->ip6_address_add (self, ifindex, address, peer_address, plen, lifetime, preferred, flags); + return klass->ip6_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, flags); } gboolean 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]; + char str_peer2[NM_UTILS_INET_ADDRSTRLEN]; + char str_peer[100]; _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); - _LOGD ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s", + _LOGD ("address: deleting IPv4 address %s/%d, %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 ? ", " : "", + peer_address != address + ? nm_sprintf_buf (str_peer, "peer %s, ", nm_utils_inet4_ntop (peer_address, str_peer2)) : "", ifindex, _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); return klass->ip4_address_delete (self, ifindex, address, plen, peer_address); @@ -1943,7 +2517,6 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a 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); _LOGD ("address: deleting IPv6 address %s/%d, ifindex %d%s", nm_utils_inet6_ntop (&address, NULL), plen, ifindex, @@ -1952,21 +2525,21 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a } const NMPlatformIP4Address * -nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen) +nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen, guint32 peer_address) { - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (plen > 0, NULL); - return klass->ip4_address_get (self, ifindex, address, plen); + return klass->ip4_address_get (self, ifindex, address, plen, peer_address); } const NMPlatformIP6Address * nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen) { - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (plen > 0, NULL); return klass->ip6_address_get (self, ifindex, address, plen); } @@ -1980,7 +2553,9 @@ 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) { + if ( candidate->address == address->address + && candidate->plen == address->plen + && ((candidate->peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) { guint32 lifetime, preferred; if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, @@ -2064,7 +2639,7 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred)) continue; - if (!nm_platform_ip4_address_add (self, 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->plen, known_address->peer_address, lifetime, preferred, known_address->label)) return FALSE; if (out_added_addresses) { @@ -2125,7 +2700,7 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known continue; if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, - known_address->peer_address, known_address->plen, + known_address->plen, known_address->peer_address, lifetime, preferred, known_address->flags)) return FALSE; } @@ -2164,17 +2739,46 @@ nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRoute return klass->ip6_route_get_all (self, ifindex, flags); } +/** + * nm_platform_ip4_route_add: + * @self: + * @ifindex: + * @source: + * network: + * plen: + * gateway: + * pref_src: + * metric: + * mss: + * + * For kernel, a gateway can be either explicitly set or left + * at zero (0.0.0.0). In addition, there is the scope of the IPv4 + * route. + * When adding a route with + * $ ip route add default dev $IFNAME + * the resulting route will have gateway 0.0.0.0 and scope "link". + * Contrary to + * $ ip route add default via 0.0.0.0 dev $IFNAME + * which adds the route with scope "global". + * + * NetworkManager's Platform can currently only add on-link-routes with scope + * "link" (and gateway 0.0.0.0) or gateway-routes with scope "global" (and + * gateway not 0.0.0.0). + * + * It does not support adding globally scoped routes via 0.0.0.0. + * + * Returns: %TRUE in case of success. + */ 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, + in_addr_t gateway, in_addr_t pref_src, guint32 metric, guint32 mss) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (0 <= plen && plen <= 32, FALSE); - g_return_val_if_fail (klass->ip4_route_add, FALSE); if (_LOGD_ENABLED ()) { NMPlatformIP4Route route = { 0 }; @@ -2188,7 +2792,7 @@ nm_platform_ip4_route_add (NMPlatform *self, route.mss = mss; route.pref_src = pref_src; - _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route)); + _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route, NULL, 0)); } return klass->ip4_route_add (self, ifindex, source, network, plen, gateway, pref_src, metric, mss); } @@ -2202,7 +2806,6 @@ nm_platform_ip6_route_add (NMPlatform *self, _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); if (_LOGD_ENABLED ()) { NMPlatformIP6Route route = { 0 }; @@ -2215,7 +2818,7 @@ nm_platform_ip6_route_add (NMPlatform *self, route.metric = metric; route.mss = mss; - _LOGD ("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, NULL, 0)); } return klass->ip6_route_add (self, ifindex, source, network, plen, gateway, metric, mss); } @@ -2227,8 +2830,6 @@ nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (klass->ip4_route_delete, FALSE); - _LOGD ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", nm_utils_inet4_ntop (network, NULL), plen, metric, ifindex, _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); @@ -2242,8 +2843,6 @@ nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr net _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (klass->ip6_route_delete, FALSE); - _LOGD ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", nm_utils_inet6_ntop (&network, NULL), plen, metric, ifindex, _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); @@ -2268,6 +2867,40 @@ nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr networ /******************************************************************/ +const char * +nm_platform_vlan_qos_mapping_to_string (const char *name, + const NMVlanQosMapping *map, + gsize n_map, + char *buf, + gsize len) +{ + gsize i; + char *b; + + nm_utils_to_string_buffer_init (&buf, &len); + + if (!n_map) { + nm_utils_strbuf_append_str (&buf, &len, ""); + return buf; + } + + if (!map) + g_return_val_if_reached (""); + + b = buf; + + if (name) { + nm_utils_strbuf_append_str (&b, &len, name); + nm_utils_strbuf_append_str (&b, &len, " {"); + } else + nm_utils_strbuf_append_c (&b, &len, '{'); + + for (i = 0; i < n_map; i++) + nm_utils_strbuf_append (&b, &len, " %u:%u", map[i].from, map[i].to); + nm_utils_strbuf_append_str (&b, &len, " }"); + return buf; +} + static const char * source_to_string (NMIPConfigSource source) { @@ -2320,21 +2953,29 @@ _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, g return buf; } -char _nm_platform_to_string_buffer[256]; - +/** + * nm_platform_link_to_string: + * @route: pointer to NMPlatformLink address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. + * + * A method for converting an link struct into a string representation. + * + * Returns: a string representation of the link. + */ const char * -nm_platform_link_to_string (const NMPlatformLink *link) +nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) { char master[20]; char parent[20]; - char str_vlan[16]; GString *str_flags; char str_addrmode[30]; gs_free char *str_addr = NULL; gs_free char *str_inet6_token = NULL; + const char *str_link_type; - if (!link) - return "(unknown link)"; + if (!nm_utils_to_string_buffer_init_null (link, &buf, &len)) + return buf; str_flags = g_string_new (NULL); if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) @@ -2349,7 +2990,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) if (link->flags) { char str_flags_buf[64]; - rtnl_link_flags2str (link->flags, str_flags_buf, sizeof (str_flags_buf)); + nm_platform_link_flags2str (link->flags, str_flags_buf, sizeof (str_flags_buf)); g_string_append_printf (str_flags, ";%s", str_flags_buf); } @@ -2365,11 +3006,6 @@ nm_platform_link_to_string (const NMPlatformLink *link) else parent[0] = 0; - if (link->vlan_id) - g_snprintf (str_vlan, sizeof (str_vlan), " vlan %u", (guint) link->vlan_id); - else - str_vlan[0] = '\0'; - if (link->inet6_addr_gen_mode_inv) { switch (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv)) { case 0: @@ -2390,14 +3026,15 @@ nm_platform_link_to_string (const NMPlatformLink *link) 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), + str_link_type = nm_link_type_to_string (link->type); + + g_snprintf (buf, len, "%d: " /* ifindex */ "%s" /* name */ "%s" /* parent */ " <%s>" /* flags */ " mtu %d" "%s" /* master */ - "%s" /* vlan */ " arp %u" /* arptype */ "%s%s" /* link->type */ "%s%s" /* kind */ @@ -2412,12 +3049,11 @@ nm_platform_link_to_string (const NMPlatformLink *link) 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 : "", + str_link_type ? " " : "", + str_if_set (str_link_type, "???"), + link->kind ? (g_strcmp0 (str_link_type, link->kind) ? "/" : "*") : "", + link->kind && g_strcmp0 (str_link_type, link->kind) ? link->kind : "", link->initialized ? " init" : " not-init", str_addrmode, str_addr ? " addr " : "", @@ -2427,23 +3063,313 @@ nm_platform_link_to_string (const NMPlatformLink *link) link->driver ? " driver " : "", link->driver ? link->driver : ""); g_string_free (str_flags, TRUE); - return _nm_platform_to_string_buffer; + return buf; +} + +const char * +nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len) +{ + char str_local[30]; + char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote[30]; + char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_ttl[30]; + char str_tos[30]; + char str_parent_ifindex[30]; + char str_input_flags[30]; + char str_output_flags[30]; + char str_input_key[30]; + char str_input_key1[NM_UTILS_INET_ADDRSTRLEN]; + char str_output_key[30]; + char str_output_key1[NM_UTILS_INET_ADDRSTRLEN]; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "gre" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ttl */ + "%s" /* tos */ + "%s" /* path_mtu_discovery */ + "%s" /* iflags */ + "%s" /* oflags */ + "%s" /* ikey */ + "%s" /* okey */ + "", + lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "", + lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", + lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", + lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos 0x%x", lnk->tos)) : "", + lnk->path_mtu_discovery ? "" : " nopmtudisc", + lnk->input_flags ? nm_sprintf_buf (str_input_flags, " iflags 0x%x", lnk->input_flags) : "", + lnk->output_flags ? nm_sprintf_buf (str_output_flags, " oflags 0x%x", lnk->output_flags) : "", + NM_FLAGS_HAS (lnk->input_flags, GRE_KEY) || lnk->input_key ? nm_sprintf_buf (str_input_key, " ikey %s", nm_utils_inet4_ntop (lnk->input_key, str_input_key1)) : "", + NM_FLAGS_HAS (lnk->output_flags, GRE_KEY) || lnk->output_key ? nm_sprintf_buf (str_output_key, " okey %s", nm_utils_inet4_ntop (lnk->output_key, str_output_key1)) : ""); + return buf; +} + +const char * +nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len) +{ + char str_p_key[64]; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "infiniband" + "%s" /* p_key */ + "%s%s" /* mode */ + "", + lnk->p_key ? nm_sprintf_buf (str_p_key, " pkey %d", lnk->p_key) : "", + lnk->mode ? " mode " : "", + lnk->mode ?: ""); + return buf; +} + +const char * +nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len) +{ + char str_local[30]; + char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote[30]; + char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_ttl[30]; + char str_tclass[30]; + char str_flow[30]; + char str_encap[30]; + char str_proto[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "ip6tnl" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ttl */ + "%s" /* tclass */ + "%s" /* encap limit */ + "%s" /* flow label */ + "%s" /* proto */ + "", + nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet6_ntop (&lnk->remote, str_remote1)), + nm_sprintf_buf (str_local, " local %s", nm_utils_inet6_ntop (&lnk->local, str_local1)), + lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", + lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", + lnk->tclass == 1 ? " tclass inherit" : nm_sprintf_buf (str_tclass, " tclass 0x%x", lnk->tclass), + nm_sprintf_buf (str_encap, " encap-limit %u", lnk->encap_limit), + nm_sprintf_buf (str_flow, " flow-label 0x05%x", lnk->flow_label), + nm_sprintf_buf (str_proto, " proto %u", lnk->proto)); + return buf; +} + +const char * +nm_platform_lnk_ipip_to_string (const NMPlatformLnkIpIp *lnk, char *buf, gsize len) +{ + char str_local[30]; + char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote[30]; + char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_ttl[30]; + char str_tos[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "ipip" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ttl */ + "%s" /* tos */ + "%s" /* path_mtu_discovery */ + "", + lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "", + lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", + lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", + lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos 0x%x", lnk->tos)) : "", + lnk->path_mtu_discovery ? "" : " nopmtudisc"); + return buf; +} + +const char * +nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, gsize len) +{ + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "macvlan mode %u %s", + lnk->mode, + lnk->no_promisc ? "not-promisc" : "promisc"); + return buf; +} + +const char * +nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len) +{ + char str_local[30]; + char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote[30]; + char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_ttl[30]; + char str_tos[30]; + char str_flags[30]; + char str_proto[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "sit" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ttl */ + "%s" /* tos */ + "%s" /* path_mtu_discovery */ + "%s" /* flags */ + "%s" /* proto */ + "", + lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "", + lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", + lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", + lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos 0x%x", lnk->tos)) : "", + lnk->path_mtu_discovery ? "" : " nopmtudisc", + lnk->flags ? nm_sprintf_buf (str_flags, " flags 0x%x", lnk->flags) : "", + lnk->proto ? nm_sprintf_buf (str_proto, " proto 0x%x", lnk->proto) : ""); + return buf; +} + +const char * +nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len) +{ + char *b; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + b = buf; + + nm_utils_strbuf_append (&b, &len, "vlan %u", lnk->id); + if (lnk->flags) + nm_utils_strbuf_append (&b, &len, " flags 0x%x", lnk->flags); + return buf; +} + +const char * +nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize len) +{ + char str_group[100]; + char str_group6[100]; + char str_local[100]; + char str_local6[100]; + char str_dev[25]; + char str_limit[25]; + char str_src_port[35]; + char str_dst_port[25]; + char str_tos[25]; + char str_ttl[25]; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + if (lnk->group == 0) + str_group[0] = '\0'; + else { + g_snprintf (str_group, sizeof (str_group), + " %s %s", + IN_MULTICAST (ntohl (lnk->group)) ? "group" : "remote", + nm_utils_inet4_ntop (lnk->group, NULL)); + } + if (IN6_IS_ADDR_UNSPECIFIED (&lnk->group6)) + str_group6[0] = '\0'; + else { + g_snprintf (str_group6, sizeof (str_group6), + " %s%s %s", + IN6_IS_ADDR_MULTICAST (&lnk->group6) ? "group" : "remote", + str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ + nm_utils_inet6_ntop (&lnk->group6, NULL)); + } + + if (lnk->local == 0) + str_local[0] = '\0'; + else { + g_snprintf (str_local, sizeof (str_local), + " local %s", + nm_utils_inet4_ntop (lnk->local, NULL)); + } + if (IN6_IS_ADDR_UNSPECIFIED (&lnk->local6)) + str_local6[0] = '\0'; + else { + g_snprintf (str_local6, sizeof (str_local6), + " local%s %s", + str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ + nm_utils_inet6_ntop (&lnk->local6, NULL)); + } + + g_snprintf (buf, len, + "vxlan" + " id %u" /* id */ + "%s%s" /* group/group6 */ + "%s%s" /* local/local6 */ + "%s" /* dev */ + "%s" /* src_port_min/src_port_max */ + "%s" /* dst_port */ + "%s" /* learning */ + "%s" /* proxy */ + "%s" /* rsc */ + "%s" /* l2miss */ + "%s" /* l3miss */ + "%s" /* tos */ + "%s" /* ttl */ + " ageing %u" /* ageing */ + "%s" /* limit */ + "", + (guint) lnk->id, + str_group, str_group6, + str_local, str_local6, + lnk->parent_ifindex ? nm_sprintf_buf (str_dev, " dev %d", lnk->parent_ifindex) : "", + lnk->src_port_min || lnk->src_port_max ? nm_sprintf_buf (str_src_port, " srcport %u %u", lnk->src_port_min, lnk->src_port_max) : "", + lnk->dst_port ? nm_sprintf_buf (str_dst_port, " dstport %u", lnk->dst_port) : "", + !lnk->learning ? " nolearning" : "", + lnk->proxy ? " proxy" : "", + lnk->rsc ? " rsc" : "", + lnk->l2miss ? " l2miss" : "", + lnk->l3miss ? " l3miss" : "", + lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos %#x", lnk->tos), + lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : "", + lnk->ageing, + lnk->limit ? nm_sprintf_buf (str_limit, " maxaddr %u", lnk->limit) : ""); + return buf; } /** * nm_platform_ip4_address_to_string: * @route: pointer to NMPlatformIP4Address address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * * A method for converting an address struct into a string representation. * * Example output: "" * - * Returns: a string representation of the address. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the address. */ const char * -nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) +nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len) { char s_address[INET_ADDRSTRLEN]; char s_peer[INET_ADDRSTRLEN]; @@ -2454,11 +3380,12 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) const char *str_lft_p, *str_pref_p, *str_time_p; gint32 now = nm_utils_get_monotonic_timestamp_s (); - g_return_val_if_fail (address, "(unknown)"); + if (!nm_utils_to_string_buffer_init_null (address, &buf, &len)) + return buf; inet_ntop (AF_INET, &address->address, s_address, sizeof (s_address)); - if (address->peer_address) { + if (address->peer_address != address->address) { inet_ntop (AF_INET, &address->peer_address, s_peer, sizeof (s_peer)); str_peer = g_strconcat (" ptp ", s_peer, NULL); } @@ -2480,66 +3407,92 @@ 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 (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + g_snprintf (buf, len, + "%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 _nm_platform_to_string_buffer; + return buf; } -/** - * nm_platform_addr_flags2str: wrapper for rtnl_addr_flags2str(), - * which might not yet support some recent address flags. - **/ -void -nm_platform_addr_flags2str (int flags, char *buf, size_t size) -{ - rtnl_addr_flags2str(flags, buf, size); - - /* There are two recent flags IFA_F_MANAGETEMPADDR and IFA_F_NOPREFIXROUTE. - * If libnl does not yet support them, add them by hand. - * These two flags were introduced together with the extended ifa_flags, - * so, check for that. - */ - if ((flags & IFA_F_MANAGETEMPADDR) && !nm_platform_check_support_libnl_extended_ifa_flags ()) { - strncat (buf, buf[0] ? "," IFA_F_MANAGETEMPADDR_STR : IFA_F_MANAGETEMPADDR_STR, - size - strlen (buf) - 1); - } - if ((flags & IFA_F_NOPREFIXROUTE) && !nm_platform_check_support_libnl_extended_ifa_flags ()) { - strncat (buf, buf[0] ? "," IFA_F_NOPREFIXROUTE_STR : IFA_F_NOPREFIXROUTE_STR, - size - strlen (buf) - 1); - } -} +NM_UTILS_FLAGS2STR_DEFINE (nm_platform_link_flags2str, unsigned, + NM_UTILS_FLAGS2STR (IFF_LOOPBACK, "loopback"), + NM_UTILS_FLAGS2STR (IFF_BROADCAST, "broadcast"), + NM_UTILS_FLAGS2STR (IFF_POINTOPOINT, "pointopoint"), + NM_UTILS_FLAGS2STR (IFF_MULTICAST, "multicast"), + NM_UTILS_FLAGS2STR (IFF_NOARP, "noarp"), + NM_UTILS_FLAGS2STR (IFF_ALLMULTI, "allmulti"), + NM_UTILS_FLAGS2STR (IFF_PROMISC, "promisc"), + NM_UTILS_FLAGS2STR (IFF_MASTER, "master"), + NM_UTILS_FLAGS2STR (IFF_SLAVE, "slave"), + NM_UTILS_FLAGS2STR (IFF_DEBUG, "debug"), + NM_UTILS_FLAGS2STR (IFF_DYNAMIC, "dynamic"), + NM_UTILS_FLAGS2STR (IFF_AUTOMEDIA, "automedia"), + NM_UTILS_FLAGS2STR (IFF_PORTSEL, "portsel"), + NM_UTILS_FLAGS2STR (IFF_NOTRAILERS, "notrailers"), + NM_UTILS_FLAGS2STR (IFF_UP, "up"), + NM_UTILS_FLAGS2STR (IFF_RUNNING, "running"), + NM_UTILS_FLAGS2STR (IFF_LOWER_UP, "lowerup"), + NM_UTILS_FLAGS2STR (IFF_DORMANT, "dormant"), + NM_UTILS_FLAGS2STR (IFF_ECHO, "echo"), +); + +NM_UTILS_ENUM2STR_DEFINE (nm_platform_link_inet6_addrgenmode2str, guint8, + NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_NONE, "none"), + NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"), + NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"), +); + +NM_UTILS_FLAGS2STR_DEFINE (nm_platform_addr_flags2str, unsigned, + NM_UTILS_FLAGS2STR (IFA_F_SECONDARY, "secondary"), + NM_UTILS_FLAGS2STR (IFA_F_NODAD, "nodad"), + NM_UTILS_FLAGS2STR (IFA_F_OPTIMISTIC, "optimistic"), + NM_UTILS_FLAGS2STR (IFA_F_HOMEADDRESS, "homeaddress"), + NM_UTILS_FLAGS2STR (IFA_F_DEPRECATED, "deprecated"), + NM_UTILS_FLAGS2STR (IFA_F_TENTATIVE, "tentative"), + NM_UTILS_FLAGS2STR (IFA_F_PERMANENT, "permanent"), + NM_UTILS_FLAGS2STR (IFA_F_MANAGETEMPADDR, "mngtmpaddr"), + NM_UTILS_FLAGS2STR (IFA_F_NOPREFIXROUTE, "noprefixroute"), +); + +NM_UTILS_ENUM2STR_DEFINE (nm_platform_route_scope2str, int, + NM_UTILS_ENUM2STR (RT_SCOPE_NOWHERE, "nowhere"), + NM_UTILS_ENUM2STR (RT_SCOPE_HOST, "host"), + NM_UTILS_ENUM2STR (RT_SCOPE_LINK, "link"), + NM_UTILS_ENUM2STR (RT_SCOPE_SITE, "site"), + NM_UTILS_ENUM2STR (RT_SCOPE_UNIVERSE, "global"), +); /** * nm_platform_ip6_address_to_string: * @route: pointer to NMPlatformIP6Address address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * * A method for converting an address struct into a string representation. * * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1" * - * Returns: a string representation of the address. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the address. */ const char * -nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) +nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len) { +#define S_FLAGS_PREFIX " flags " char s_flags[256]; char s_address[INET6_ADDRSTRLEN]; char s_peer[INET6_ADDRSTRLEN]; char str_lft[30], str_pref[30], str_time[50]; char str_dev[TO_STRING_DEV_BUF_SIZE]; - char *str_flags; char *str_peer = NULL; const char *str_lft_p, *str_pref_p, *str_time_p; gint32 now = nm_utils_get_monotonic_timestamp_s (); - g_return_val_if_fail (address, "(unknown)"); + if (!nm_utils_to_string_buffer_init_null (address, &buf, &len)) + return buf; inet_ntop (AF_INET6, &address->address, s_address, sizeof (s_address)); @@ -2550,9 +3503,11 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) _to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev)); - nm_platform_addr_flags2str (address->flags, s_flags, sizeof (s_flags)); - - str_flags = s_flags[0] ? g_strconcat (" flags ", s_flags, NULL) : NULL; + nm_platform_addr_flags2str (address->flags, &s_flags[STRLEN (S_FLAGS_PREFIX)], sizeof (s_flags) - STRLEN (S_FLAGS_PREFIX)); + if (s_flags[STRLEN (S_FLAGS_PREFIX)] == '\0') + s_flags[0] = '\0'; + else + memcpy (s_flags, S_FLAGS_PREFIX, STRLEN (S_FLAGS_PREFIX)); str_lft_p = _lifetime_to_string (address->timestamp, address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT, @@ -2564,45 +3519,46 @@ 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 (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + g_snprintf (buf, len, + "%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_flags ? str_flags : "", + s_flags, source_to_string (address->source)); - g_free (str_flags); g_free (str_peer); - return _nm_platform_to_string_buffer; + return buf; } /** * nm_platform_ip4_route_to_string: * @route: pointer to NMPlatformIP4Route route structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * * A method for converting a route struct into a string representation. * * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0" * - * Returns: a string representation of the route. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the route. */ const char * -nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) +nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len) { 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)"); + if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) + return buf; 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 (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + g_snprintf (buf, len, "%s/%d" " via %s" "%s" @@ -2619,38 +3575,39 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) 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->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "", route->pref_src ? " pref-src " : "", route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : ""); - return _nm_platform_to_string_buffer; + return buf; } /** * nm_platform_ip6_route_to_string: * @route: pointer to NMPlatformIP6Route route structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * * A method for converting a route struct into a string representation. * * Example output: "ff02::fb/128 via :: dev em1 metric 0" * - * Returns: a string representation of the route. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the route. */ const char * -nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) +nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len) { char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; - g_return_val_if_fail (route, "(unknown)"); + if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) + return buf; inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network)); inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway)); _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + g_snprintf (buf, len, "%s/%d" " via %s" "%s" @@ -2664,10 +3621,10 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) route->metric, route->mss, source_to_string (route->source)); - return _nm_platform_to_string_buffer; + return buf; } -#define _CMP_POINTER(a, b) \ +#define _CMP_SELF(a, b) \ G_STMT_START { \ if ((a) == (b)) \ return 0; \ @@ -2677,6 +3634,19 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return 1; \ } G_STMT_END +#define _CMP_DIRECT(a, b) \ + G_STMT_START { \ + if ((a) != (b)) \ + return ((a) < (b)) ? -1 : 1; \ + } G_STMT_END + +#define _CMP_DIRECT_MEMCMP(a, b, size) \ + G_STMT_START { \ + int c = memcmp ((a), (b), (size)); \ + if (c != 0) \ + return c < 0 ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD(a, b, field) \ G_STMT_START { \ if (((a)->field) != ((b)->field)) \ @@ -2732,13 +3702,12 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) { - _CMP_POINTER (a, b); + _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, type); _CMP_FIELD_STR (a, b, name); _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, vlan_id); _CMP_FIELD (a, b, flags); _CMP_FIELD (a, b, connected); _CMP_FIELD (a, b, mtu); @@ -2757,14 +3726,126 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) } int +nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD (a, b, input_flags); + _CMP_FIELD (a, b, output_flags); + _CMP_FIELD (a, b, input_key); + _CMP_FIELD (a, b, output_key); + _CMP_FIELD (a, b, local); + _CMP_FIELD (a, b, remote); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD (a, b, tos); + _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + return 0; +} + +int +nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, p_key); + _CMP_FIELD_STR_INTERNED (a, b, mode); + return 0; +} + +int +nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD_MEMCMP (a, b, local); + _CMP_FIELD_MEMCMP (a, b, remote); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD (a, b, tclass); + _CMP_FIELD (a, b, encap_limit); + _CMP_FIELD (a, b, flow_label); + _CMP_FIELD (a, b, proto); + return 0; +} + +int +nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD (a, b, local); + _CMP_FIELD (a, b, remote); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD (a, b, tos); + _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + return 0; +} + +int +nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, mode); + _CMP_FIELD_BOOL (a, b, no_promisc); + return 0; +} + +int +nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD (a, b, local); + _CMP_FIELD (a, b, remote); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD (a, b, tos); + _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + _CMP_FIELD (a, b, flags); + _CMP_FIELD (a, b, proto); + return 0; +} + +int +nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, id); + _CMP_FIELD (a, b, flags); + return 0; +} + +int +nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD (a, b, id); + _CMP_FIELD (a, b, group); + _CMP_FIELD (a, b, local); + _CMP_FIELD_MEMCMP (a, b, group6); + _CMP_FIELD_MEMCMP (a, b, local6); + _CMP_FIELD (a, b, tos); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD_BOOL (a, b, learning); + _CMP_FIELD (a, b, ageing); + _CMP_FIELD (a, b, limit); + _CMP_FIELD (a, b, dst_port); + _CMP_FIELD (a, b, src_port_min); + _CMP_FIELD (a, b, src_port_max); + _CMP_FIELD_BOOL (a, b, proxy); + _CMP_FIELD_BOOL (a, b, rsc); + _CMP_FIELD_BOOL (a, b, l2miss); + _CMP_FIELD_BOOL (a, b, l3miss); + return 0; +} + +int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { - _CMP_POINTER (a, b); + _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, source); _CMP_FIELD (a, b, address); - _CMP_FIELD (a, b, peer_address); _CMP_FIELD (a, b, plen); + _CMP_FIELD (a, b, peer_address); _CMP_FIELD (a, b, timestamp); _CMP_FIELD (a, b, lifetime); _CMP_FIELD (a, b, preferred); @@ -2775,11 +3856,17 @@ nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4A int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) { - _CMP_POINTER (a, b); + const struct in6_addr *p_a, *p_b; + + _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, source); _CMP_FIELD_MEMCMP (a, b, address); - _CMP_FIELD_MEMCMP (a, b, peer_address); + + p_a = nm_platform_ip6_address_get_peer (a); + p_b = nm_platform_ip6_address_get_peer (b); + _CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a)); + _CMP_FIELD (a, b, plen); _CMP_FIELD (a, b, timestamp); _CMP_FIELD (a, b, lifetime); @@ -2791,7 +3878,7 @@ nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6A int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) { - _CMP_POINTER (a, b); + _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, source); _CMP_FIELD (a, b, network); @@ -2807,7 +3894,7 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) { - _CMP_POINTER (a, b); + _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, source); _CMP_FIELD_MEMCMP (a, b, network); @@ -2818,9 +3905,6 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route return 0; } -#undef _CMP_FIELD -#undef _CMP_FIELD_MEMCMP - /** * nm_platform_ip_address_cmp_expiry: * @a: a NMPlatformIPAddress to compare @@ -2839,7 +3923,7 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor { gint64 ta = 0, tb = 0; - _CMP_POINTER (a, b); + _CMP_SELF (a, b); if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0) ta = G_MAXINT64; @@ -2872,8 +3956,6 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor return ta < tb ? -1 : 1; } -#undef _CMP_POINTER - const char * nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type) { @@ -2893,31 +3975,31 @@ static void log_link (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: link %7s: %s", nm_platform_signal_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, NULL, 0)); } static void log_ip4_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: address 4 %7s: %s", nm_platform_signal_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, NULL, 0)); } static void log_ip6_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: address 6 %7s: %s", nm_platform_signal_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, NULL, 0)); } static void log_ip4_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: route 4 %7s: %s", nm_platform_signal_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, NULL, 0)); } static void log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: route 6 %7s: %s", nm_platform_signal_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, NULL, 0)); } /******************************************************************/ @@ -2994,7 +4076,7 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .addr_family = AF_INET, .sizeof_route = sizeof (NMPlatformIP4Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip4_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip4_route_to_string, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, .route_get_all = nm_platform_ip4_route_get_all, .route_add = _vtr_v4_route_add, .route_delete = _vtr_v4_route_delete, @@ -3007,7 +4089,7 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .addr_family = AF_INET6, .sizeof_route = sizeof (NMPlatformIP6Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip6_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip6_route_to_string, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, .route_get_all = nm_platform_ip6_route_get_all, .route_add = _vtr_v6_route_add, .route_delete = _vtr_v6_route_delete, @@ -3051,14 +4133,6 @@ nm_platform_init (NMPlatform *object) { } -#define SIGNAL(signal_id, method) signals[signal_id] = \ - g_signal_new_class_handler (NM_PLATFORM_ ## signal_id, \ - G_OBJECT_CLASS_TYPE (object_class), \ - G_SIGNAL_RUN_FIRST, \ - G_CALLBACK (method), \ - NULL, NULL, NULL, \ - 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) { @@ -3069,6 +4143,8 @@ nm_platform_class_init (NMPlatformClass *platform_class) object_class->set_property = set_property; object_class->constructed = constructed; + platform_class->wifi_set_powersave = wifi_set_powersave; + g_object_class_install_property (object_class, PROP_REGISTER_SINGLETON, g_param_spec_boolean (NM_PLATFORM_REGISTER_SINGLETON, "", "", @@ -3077,10 +4153,21 @@ nm_platform_class_init (NMPlatformClass *platform_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +#define SIGNAL(signal, signal_id, method) \ + G_STMT_START { \ + signals[signal] = \ + g_signal_new_class_handler (""signal_id"", \ + G_OBJECT_CLASS_TYPE (object_class), \ + G_SIGNAL_RUN_FIRST, \ + G_CALLBACK (method), \ + NULL, NULL, NULL, \ + G_TYPE_NONE, 4, NM_TYPE_POBJECT_TYPE, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE); \ + } G_STMT_END + /* Signals */ - SIGNAL (SIGNAL_LINK_CHANGED, log_link) - SIGNAL (SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address) - SIGNAL (SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address) - SIGNAL (SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route) - SIGNAL (SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route) + SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 3219dd5a..8ac129a0 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -21,15 +21,16 @@ #ifndef __NETWORKMANAGER_PLATFORM_H__ #define __NETWORKMANAGER_PLATFORM_H__ -#include <glib-object.h> -#include "nm-glib-compat.h" #include <netinet/in.h> #include <linux/if.h> #include <linux/if_addr.h> +#include <linux/if_link.h> -#include <nm-dbus-interface.h> -#include "nm-types.h" +#include "nm-dbus-interface.h" +#include "nm-default.h" #include "NetworkManagerUtils.h" +#include "nm-setting-vlan.h" +#include "nm-core-types-internal.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) #define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform)) @@ -54,6 +55,18 @@ typedef struct _NMPlatform NMPlatform; #define IFA_F_NOPREFIXROUTE 0x200 #endif +/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers + * that don't define it. */ +#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ +#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ +#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ +#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ + +#define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */ + +/* Redefine this in host's endianness */ +#define NM_GRE_KEY 0x2000 + typedef enum { /*< skip >*/ /* dummy value, to enforce that the enum type is signed and has a size @@ -74,18 +87,18 @@ typedef enum { /*< skip >*/ NM_PLATFORM_ERROR_NO_FIRMWARE, } NMPlatformError; -typedef enum { - NM_PLATFORM_REASON_NONE, - /* Event was requested by NetworkManager. */ - NM_PLATFORM_REASON_INTERNAL, - /* Event came from the kernel. */ - NM_PLATFORM_REASON_EXTERNAL, - /* Event is a result of cache checking and cleanups. */ - NM_PLATFORM_REASON_CACHE_CHECK, - - /* Internal reason to suppress announcing change events */ - _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL, -} NMPlatformReason; + +typedef struct { + union { + guint8 addr_ptr[1]; + in_addr_t addr4; + struct in6_addr addr6; + }; +} NMIPAddr; + +extern const NMIPAddr nm_ip_addr_zero; + +#define NMIPAddrInit { .addr6 = IN6ADDR_ANY_INIT } #define NM_PLATFORM_LINK_OTHER_NETNS (-1) @@ -133,9 +146,6 @@ struct _NMPlatformLink { * 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; @@ -147,6 +157,18 @@ struct _NMPlatformLink { guint mtu; }; +typedef enum { /*< skip >*/ + NM_PLATFORM_SIGNAL_ID_NONE, + NM_PLATFORM_SIGNAL_ID_LINK, + NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, + NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, + NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, + NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, + _NM_PLATFORM_SIGNAL_ID_LAST, +} NMPlatformSignalIdType; + +guint _nm_platform_signal_id_get (NMPlatformSignalIdType signal_type); + typedef enum { NM_PLATFORM_SIGNAL_NONE, NM_PLATFORM_SIGNAL_ADDED, @@ -220,11 +242,23 @@ typedef struct { **/ struct _NMPlatformIP4Address { __NMPlatformIPAddress_COMMON; + + /* The local address IFA_LOCAL. */ in_addr_t address; + + /* The IFA_ADDRESS PTP peer address. This field is rather important, because + * it constitutes the identifier for the IPv4 address (e.g. you can add two + * addresses that only differ by their peer's network-part. + * + * Beware that for most cases, NetworkManager doesn't want to set an explicit + * peer-address. Hoever, that corresponds to setting the peer address to @address + * itself. Leaving peer-address unset/zero, means explicitly setting the peer + * address to 0.0.0.0, which you probably don't want. + * */ in_addr_t peer_address; /* PTP peer address */ + char label[IFNAMSIZ]; }; -G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address)); /** * NMPlatformIP6Address: @@ -236,7 +270,6 @@ struct _NMPlatformIP6Address { struct in6_addr peer_address; guint flags; /* ifa_flags from <linux/if_addr.h>, field type "unsigned int" is as used in rtnl_addr_get_flags. */ }; -G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address)); typedef union { NMPlatformIPAddress ax; @@ -291,16 +324,14 @@ struct _NMPlatformIP4Route { /* RTA_PREFSRC/rtnl_route_get_pref_src(). A value of zero means that * no pref-src is set. */ - guint32 pref_src; + in_addr_t pref_src; }; -G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); struct _NMPlatformIP6Route { __NMPlatformIPRoute_COMMON; struct in6_addr network; struct in6_addr gateway; }; -G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network)); typedef union { NMPlatformIPRoute rx; @@ -319,7 +350,7 @@ typedef struct { int addr_family; gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b); - const char *(*route_to_string) (const NMPlatformIPXRoute *route); + const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); @@ -330,26 +361,68 @@ typedef struct { extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; -extern char _nm_platform_to_string_buffer[256]; - typedef struct { - int peer; -} NMPlatformVethProperties; + int parent_ifindex; + guint16 input_flags; + guint16 output_flags; + guint32 input_key; + guint32 output_key; + in_addr_t local; + in_addr_t remote; + guint8 ttl; + guint8 tos; + gboolean path_mtu_discovery; +} NMPlatformLnkGre; typedef struct { - gint64 owner; - gint64 group; + int p_key; const char *mode; - gboolean no_pi; - gboolean vnet_hdr; - gboolean multi_queue; -} NMPlatformTunProperties; +} NMPlatformLnkInfiniband; typedef struct { int parent_ifindex; - const char *mode; + struct in6_addr local; + struct in6_addr remote; + guint8 ttl; + guint8 tclass; + guint8 encap_limit; + guint flow_label; + guint8 proto; +} NMPlatformLnkIp6Tnl; + +typedef struct { + int parent_ifindex; + in_addr_t local; + in_addr_t remote; + guint8 ttl; + guint8 tos; + gboolean path_mtu_discovery; +} NMPlatformLnkIpIp; + +typedef struct { + guint mode; gboolean no_promisc; -} NMPlatformMacvlanProperties; + gboolean tap; +} NMPlatformLnkMacvlan; + +typedef NMPlatformLnkMacvlan NMPlatformLnkMacvtap; + +typedef struct { + int parent_ifindex; + in_addr_t local; + in_addr_t remote; + guint8 ttl; + guint8 tos; + gboolean path_mtu_discovery; + guint16 flags; + guint8 proto; +} NMPlatformLnkSit; + +typedef struct { + /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ + guint16 id; + NMVlanFlags flags; +} NMPlatformLnkVlan; typedef struct { int parent_ifindex; @@ -370,51 +443,19 @@ typedef struct { gboolean rsc; gboolean l2miss; gboolean l3miss; -} NMPlatformVxlanProperties; +} NMPlatformLnkVxlan; typedef struct { - int parent_ifindex; - guint16 input_flags; - guint16 output_flags; - guint32 input_key; - guint32 output_key; - in_addr_t local; - in_addr_t remote; - guint8 ttl; - guint8 tos; - gboolean path_mtu_discovery; -} NMPlatformGreProperties; + gint64 owner; + gint64 group; + const char *mode; + gboolean no_pi; + gboolean vnet_hdr; + gboolean multi_queue; +} NMPlatformTunProperties; /******************************************************************/ -/* NMPlatform abstract class and its implementations provide a layer between - * networkmanager's device management classes and the operating system kernel. - * - * How it works, is best seen in tests/nm-platform-test.c source file. - * - * NMPlatform provides interface to configure kernel interfaces and receive - * notifications about both internal and external configuration changes. It - * respects the following rules: - * - * 1) Every change made through NMPlatform is readily available and the respective - * signals are called synchronously. - * - * 2) State of an object retrieved from NMPlatform (through functions or events) - * is at least as recent than the state retrieved before. - * - * Any failure of the above rules should be fixed in NMPlatform implementations - * and tested in nm-platform-test. Synchronization hacks should never be put - * to any other code. That's why NMPlatform was created and that's why the - * testing code was written for it. - * - * In future, parts of linux platform implementation may be moved to the libnl - * library. - * - * If you have any problems related to NMPlatform on your system, you should - * always first run tests/nm-linux-platform-test as root and with all - * network configuration daemons stopped. Look at the code first. - */ - struct _NMPlatform { GObject parent; }; @@ -429,13 +470,15 @@ typedef struct { const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); + const NMPObject *(*link_get_lnk) (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); + GArray *(*link_get_all) (NMPlatform *); gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, const void *address, size_t address_len, - NMPlatformLink *out_link); + const NMPlatformLink **out_link); gboolean (*link_delete) (NMPlatform *, int ifindex); const char *(*link_get_type_name) (NMPlatform *, int ifindex); gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged); @@ -474,28 +517,48 @@ typedef struct { gboolean (*link_enslave) (NMPlatform *, int master, int slave); gboolean (*link_release) (NMPlatform *, int master, 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, 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, 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); - gboolean (*macvlan_get_properties) (NMPlatform *, int ifindex, NMPlatformMacvlanProperties *props); - gboolean (*vxlan_get_properties) (NMPlatform *, int ifindex, NMPlatformVxlanProperties *props); - gboolean (*gre_get_properties) (NMPlatform *, int ifindex, NMPlatformGreProperties *props); + + gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link); + gboolean (*link_vlan_change) (NMPlatform *self, + int ifindex, + NMVlanFlags flags_mask, + NMVlanFlags flags_set, + gboolean ingress_reset_all, + const NMVlanQosMapping *ingress_map, + gsize n_ingress_map, + gboolean egress_reset_all, + const NMVlanQosMapping *egress_map, + gsize n_egress_map); + gboolean (*link_vxlan_add) (NMPlatform *, + const char *name, + const NMPlatformLnkVxlan *props, + const NMPlatformLink **out_link); + gboolean (*link_gre_add) (NMPlatform *, + const char *name, + const NMPlatformLnkGre *props, + const NMPlatformLink **out_link); + gboolean (*link_ip6tnl_add) (NMPlatform *, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link); + gboolean (*link_ipip_add) (NMPlatform *, + const char *name, + const NMPlatformLnkIpIp *props, + const NMPlatformLink **out_link); + gboolean (*link_macvlan_add) (NMPlatform *, + const char *name, + int parent, + const NMPlatformLnkMacvlan *props, + const NMPlatformLink **out_link); + gboolean (*link_sit_add) (NMPlatform *, + const char *name, + const NMPlatformLnkSit *props, + const NMPlatformLink **out_link); + + gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link); + + gboolean (*tun_add) (NMPlatform *platform, const char *name, gboolean tap, gint64 owner, gint64 group, gboolean pi, + gboolean vnet_hdr, gboolean multi_queue, const NMPlatformLink **out_link); gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); @@ -505,6 +568,7 @@ typedef struct { guint32 (*wifi_get_rate) (NMPlatform *, int ifindex); NM80211Mode (*wifi_get_mode) (NMPlatform *, int ifindex); void (*wifi_set_mode) (NMPlatform *, int ifindex, NM80211Mode mode); + void (*wifi_set_powersave) (NMPlatform *, int ifindex, guint32 powersave); guint32 (*wifi_find_frequency) (NMPlatform *, int ifindex, const guint32 *freqs); void (*wifi_indicate_addressing_running) (NMPlatform *, int ifindex, gboolean running); @@ -514,23 +578,32 @@ typedef struct { GArray * (*ip4_address_get_all) (NMPlatform *, int ifindex); GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex); - gboolean (*ip4_address_add) (NMPlatform *, int ifindex, - in_addr_t address, in_addr_t peer_address, int plen, - guint32 lifetime, guint32 preferred_lft, + gboolean (*ip4_address_add) (NMPlatform *, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address, + guint32 lifetime, + guint32 preferred_lft, const char *label); - gboolean (*ip6_address_add) (NMPlatform *, int ifindex, - struct in6_addr address, struct in6_addr peer_address, int plen, - guint32 lifetime, guint32 preferred_lft, guint flags); + gboolean (*ip6_address_add) (NMPlatform *, + int ifindex, + struct in6_addr address, + int plen, + struct in6_addr peer_address, + 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); - const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, int plen); + const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address, int plen); GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, - guint32 pref_src, guint32 metric, guint32 mss); + in_addr_t pref_src, guint32 metric, guint32 mss); gboolean (*ip6_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss); @@ -607,10 +680,10 @@ const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const ch 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); +NMPlatformError nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); /* convienience methods to lookup the link and access fields of NMPlatformLink. */ @@ -663,33 +736,77 @@ 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_sysctl_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); +char *nm_platform_sysctl_master_get_option (NMPlatform *self, int ifindex, const char *option); +gboolean nm_platform_sysctl_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); +char *nm_platform_sysctl_slave_get_option (NMPlatform *self, int ifindex, const char *option); + +const NMPObject *nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); +const NMPlatformLnkGre *nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkIp6Tnl *nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkInfiniband *nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkMacvlan *nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkMacvtap *nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkSit *nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); + +NMPlatformError nm_platform_link_vlan_add (NMPlatform *self, + const char *name, + int parent, + int vlanid, + guint32 vlanflags, + const NMPlatformLink **out_link); +gboolean nm_platform_link_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to); +gboolean nm_platform_link_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to); +gboolean nm_platform_link_vlan_change (NMPlatform *self, + int ifindex, + NMVlanFlags flags_mask, + NMVlanFlags flags_set, + gboolean ingress_reset_all, + const NMVlanQosMapping *ingress_map, + gsize n_ingress_map, + gboolean egress_reset_all, + const NMVlanQosMapping *egress_map, + gsize n_egress_map); + +NMPlatformError nm_platform_link_vxlan_add (NMPlatform *self, + const char *name, + const NMPlatformLnkVxlan *props, + const NMPlatformLink **out_link); + +NMPlatformError nm_platform_link_tun_add (NMPlatform *self, + const char *name, + gboolean tap, + gint64 owner, + gint64 group, + gboolean pi, + gboolean vnet_hdr, + gboolean multi_queue, + const NMPlatformLink **out_link); + +NMPlatformError nm_platform_link_infiniband_add (NMPlatform *self, + int parent, + int p_key, + const NMPlatformLink **out_link); +gboolean nm_platform_link_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); + +gboolean nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex); +gboolean nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *properties); + +gboolean nm_platform_link_tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *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); +void nm_platform_wifi_set_powersave (NMPlatform *self, int ifindex, guint32 powersave); 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); @@ -697,17 +814,52 @@ 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); +void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, int plen); +const struct in6_addr *nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr); + +const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); + +NMPlatformError nm_platform_link_gre_add (NMPlatform *self, + const char *name, + const NMPlatformLnkGre *props, + const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_ip6tnl_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_ipip_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIpIp *props, + const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_macvlan_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkMacvlan *props, + const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_sit_add (NMPlatform *self, + const char *name, + const NMPlatformLnkSit *props, + const NMPlatformLink **out_link); + 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, +gboolean nm_platform_ip4_address_add (NMPlatform *self, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address, + guint32 lifetime, + guint32 preferred_lft, const char *label); -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_ip6_address_add (NMPlatform *self, + int ifindex, + struct in6_addr address, + int plen, + struct in6_addr peer_address, + guint32 lifetime, + guint32 preferred_lft, + guint flags); 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); @@ -720,31 +872,54 @@ GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatform 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); + in_addr_t pref_src, guint32 metric, guint32 mss); 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 (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); -const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address); -const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route); -const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route); +const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); +const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); +const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len); +const char *nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len); +const char *nm_platform_lnk_ipip_to_string (const NMPlatformLnkIpIp *lnk, char *buf, gsize len); +const char *nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, gsize len); +const char *nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize len); +const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len); +const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len); +const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len); +const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len); + +const char *nm_platform_vlan_qos_mapping_to_string (const char *name, + const NMVlanQosMapping *map, + gsize n_map, + char *buf, + gsize len); int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b); +int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b); +int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b); +int nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b); +int nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b); +int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b); +int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b); +int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b); +int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b); 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_libnl_link_netnsid (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); +const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); +const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); +const char *nm_platform_addr_flags2str (unsigned flags, char *buf, gsize len); +const char *nm_platform_route_scope2str (int scope, char *buf, gsize len); int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index eb6bfc3a..892fd9dd 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -22,11 +22,11 @@ #include <unistd.h> +#include "nm-default.h" #include "nmp-object.h" #include "nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" -#include "nm-logging.h" /*********************************************************************************************/ @@ -82,6 +82,42 @@ _id_hash_ip6_addr (const struct in6_addr *addr) return hash; } +static int +_vlan_xgress_qos_mappings_cmp (guint n_map, + const NMVlanQosMapping *map1, + const NMVlanQosMapping *map2) +{ + guint i; + + for (i = 0; i < n_map; i++) { + if (map1[i].from != map2[i].from) + return map1[i].from < map2[i].from ? -1 : 1; + if (map1[i].to != map2[i].to) + return map1[i].to < map2[i].to ? -1 : 1; + } + return 0; +} + +static void +_vlan_xgress_qos_mappings_cpy (guint *dst_n_map, + const NMVlanQosMapping **dst_map, + guint src_n_map, + const NMVlanQosMapping *src_map) +{ + if (src_n_map == 0) { + g_clear_pointer (dst_map, g_free); + *dst_n_map = 0; + } else if ( src_n_map != *dst_n_map + || _vlan_xgress_qos_mappings_cmp (src_n_map, *dst_map, src_map) != 0) { + g_clear_pointer (dst_map, g_free); + *dst_n_map = src_n_map; + if (src_n_map > 0) + *dst_map = g_memdup (src_map, sizeof (*src_map) * src_n_map); + } +} + +/******************************************************************/ + static const char * _link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname) { @@ -207,6 +243,14 @@ static void _vt_cmd_obj_dispose_link (NMPObject *obj) { g_clear_object (&obj->_link.udev.device); + nmp_object_unref (obj->_link.netlink.lnk); +} + +static void +_vt_cmd_obj_dispose_lnk_vlan (NMPObject *obj) +{ + g_free ((gpointer) obj->_lnk_vlan.ingress_qos_map); + g_free ((gpointer) obj->_lnk_vlan.egress_qos_map); } static NMPObject * @@ -274,10 +318,16 @@ nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformOb const NMPObject * nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) { + const NMPClass *klass; + nm_assert (NMP_OBJECT_IS_VALID (src)); nm_assert (obj); - NMP_OBJECT_GET_CLASS (src)->cmd_obj_stackinit_id (obj, src); + klass = NMP_OBJECT_GET_CLASS (src); + if (!klass->cmd_obj_stackinit_id) + nmp_object_stackinit (obj, klass->obj_type, NULL); + else + klass->cmd_obj_stackinit_id (obj, src); return obj; } @@ -296,19 +346,20 @@ _vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src) } const NMPObject * -nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen) +nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen, guint32 peer_address) { 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; + obj->ip4_address.peer_address = peer_address; 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); + nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen, src->ip4_address.peer_address); } const NMPObject * @@ -369,50 +420,131 @@ 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); - } + char buf2[sizeof (_nm_utils_to_string_buffer)]; - if (!obj) { - g_strlcpy (buf, "NULL", buf_size); + if (!nm_utils_to_string_buffer_init_null (obj, &buf, &buf_size)) return buf; - } g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); klass = NMP_OBJECT_GET_CLASS (obj); + if (klass->cmd_obj_to_string) + return klass->cmd_obj_to_string (obj, to_string_mode, buf, buf_size); + switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: + if (!klass->cmd_plobj_to_string_id) { + g_snprintf (buf, buf_size, "%p", obj); + return buf; + } 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)); + g_snprintf (buf, buf_size, + "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->_ref_count, + obj->is_cached ? '+' : '-', + nmp_object_is_alive (obj) ? '+' : '-', + nmp_object_is_visible (obj) ? '+' : '-', + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2))); + return buf; + case NMP_OBJECT_TO_STRING_PUBLIC: + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + return buf; + default: + g_return_val_if_reached ("ERROR"); + } +} - 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); +static const char * +_vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + char buf2[sizeof (_nm_utils_to_string_buffer)]; + char buf3[sizeof (_nm_utils_to_string_buffer)]; + + 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_snprintf (buf, buf_size, + "[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", + klass->obj_type_name, obj, obj->_ref_count, + obj->is_cached ? '+' : '-', + nmp_object_is_alive (obj) ? '+' : '-', + nmp_object_is_visible (obj) ? '+' : '-', + obj->_link.netlink.is_in_netlink ? '+' : '-', + obj->_link.udev.device, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); + return buf; + case NMP_OBJECT_TO_STRING_PUBLIC: + if (obj->_link.netlink.lnk) { + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2)); + nmp_object_to_string (obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, buf3, sizeof (buf3)); + g_snprintf (buf, buf_size, + "%s; %s", + buf2, buf3); } else - buf3[0] = '\0'; + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + return buf; + default: + g_return_val_if_reached ("ERROR"); + } +} + +static const char * +_vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + char buf2[sizeof (_nm_utils_to_string_buffer)]; + char *b; + gsize l; + + klass = NMP_OBJECT_GET_CLASS (obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_ID: + g_snprintf (buf, buf_size, "%p", obj); + return buf; + case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible%s; %s]", + "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", klass->obj_type_name, obj, obj->_ref_count, obj->is_cached ? '+' : '-', nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', - buf3, buf2); + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (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); + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + + b = buf; + l = strlen (b); + b += l; + buf_size -= l; + + if (obj->_lnk_vlan.n_ingress_qos_map) { + nm_platform_vlan_qos_mapping_to_string (" ingress-qos-map", + obj->_lnk_vlan.ingress_qos_map, + obj->_lnk_vlan.n_ingress_qos_map, + b, + buf_size); + l = strlen (b); + b += l; + buf_size -= l; + } + if (obj->_lnk_vlan.n_egress_qos_map) { + nm_platform_vlan_qos_mapping_to_string (" egress-qos-map", + obj->_lnk_vlan.egress_qos_map, + obj->_lnk_vlan.n_egress_qos_map, + b, + buf_size); + l = strlen (b); + b += l; + buf_size -= l; + } + return buf; default: g_return_val_if_reached ("ERROR"); @@ -425,21 +557,27 @@ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsiz { \ plat_type *const obj = (plat_type *) _obj; \ char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ + char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ \ (void) buf1; \ + (void) buf2; \ 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); +_vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); +_vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen, + obj->peer_address != obj->address ? "," : "", + obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : ""); +_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1)); +_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); +_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) { + const NMPClass *klass1, *klass2; + if (obj1 == obj2) return 0; if (!obj1) @@ -450,49 +588,72 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) 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; + klass1 = NMP_OBJECT_GET_CLASS (obj1); + klass2 = NMP_OBJECT_GET_CLASS (obj2); + + if (klass1 != klass2) + return klass1->obj_type < klass2->obj_type ? -1 : 1; - return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object); + if (klass1->cmd_obj_cmp) + return klass1->cmd_obj_cmp (obj1, obj2); + return klass1->cmd_plobj_cmp (&obj1->object, &obj2->object); } -gboolean -nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) +static int +_vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2) { - const NMPClass *klass; + int i; - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); + i = nm_platform_link_cmp (&obj1->link, &obj2->link); + if (i) + return i; + if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink) + return obj1->_link.netlink.is_in_netlink ? -1 : 1; + i = nmp_object_cmp (obj1->_link.netlink.lnk, obj2->_link.netlink.lnk); + if (i) + return i; + if (obj1->_link.udev.device != obj2->_link.udev.device) { + if (!obj1->_link.udev.device) + return -1; + if (!obj2->_link.udev.device) + return 1; + + /* Only compare based on pointer values. That is ugly because it's not a + * stable sort order, but probably udev gives us always the same GUdevDevice + * instance. + * + * Have this check as very last. */ + return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1; + } + return 0; +} - if (obj1 == obj2) - return TRUE; +static int +_vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2) +{ + int c; - klass = NMP_OBJECT_GET_CLASS (obj1); + c = nm_platform_lnk_vlan_cmp (&obj1->lnk_vlan, &obj2->lnk_vlan); + if (c) + return c; - if (klass != NMP_OBJECT_GET_CLASS (obj2)) - return FALSE; + if (obj1->_lnk_vlan.n_ingress_qos_map != obj2->_lnk_vlan.n_ingress_qos_map) + return obj1->_lnk_vlan.n_ingress_qos_map < obj2->_lnk_vlan.n_ingress_qos_map ? -1 : 1; + if (obj1->_lnk_vlan.n_egress_qos_map != obj2->_lnk_vlan.n_egress_qos_map) + return obj1->_lnk_vlan.n_egress_qos_map < obj2->_lnk_vlan.n_egress_qos_map ? -1 : 1; - return klass->cmd_obj_equal (obj1, obj2); -} + c = _vlan_xgress_qos_mappings_cmp (obj1->_lnk_vlan.n_ingress_qos_map, obj1->_lnk_vlan.ingress_qos_map, obj2->_lnk_vlan.ingress_qos_map); + if (c) + return c; + c = _vlan_xgress_qos_mappings_cmp (obj1->_lnk_vlan.n_egress_qos_map, obj1->_lnk_vlan.egress_qos_map, obj2->_lnk_vlan.egress_qos_map); -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; + return c; } -static gboolean -_vt_cmd_obj_equal_link (const NMPObject *obj1, const NMPObject *obj2) +gboolean +nmp_object_equal (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; + return nmp_object_cmp (obj1, obj2) == 0; } /* @src is a const object, which is not entirely correct for link types, where @@ -511,11 +672,46 @@ nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only) g_return_if_fail (klass == NMP_OBJECT_GET_CLASS (src)); - if (id_only) - klass->cmd_plobj_id_copy (&dst->object, &src->object); - else + if (id_only) { + if (klass->cmd_plobj_id_copy) + klass->cmd_plobj_id_copy (&dst->object, &src->object); + } else if (klass->cmd_obj_copy) klass->cmd_obj_copy (dst, src); + else + memcpy (&dst->object, &src->object, klass->sizeof_data); + } +} + +static void +_vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) +{ + if (dst->_link.udev.device != src->_link.udev.device) { + if (src->_link.udev.device) + g_object_ref (src->_link.udev.device); + if (dst->_link.udev.device) + g_object_unref (dst->_link.udev.device); } + if (dst->_link.netlink.lnk != src->_link.netlink.lnk) { + if (src->_link.netlink.lnk) + nmp_object_ref (src->_link.netlink.lnk); + if (dst->_link.netlink.lnk) + nmp_object_unref (dst->_link.netlink.lnk); + } + dst->_link = src->_link; +} + +static void +_vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src) +{ + dst->lnk_vlan = src->lnk_vlan; + _vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_ingress_qos_map, + &dst->_lnk_vlan.ingress_qos_map, + src->_lnk_vlan.n_ingress_qos_map, + src->_lnk_vlan.ingress_qos_map); + _vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_egress_qos_map, + &dst->_lnk_vlan.egress_qos_map, + src->_lnk_vlan.n_egress_qos_map, + src->_lnk_vlan.egress_qos_map); } #define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \ @@ -533,10 +729,10 @@ _vt_cmd_plobj_id_copy (ip4_address, NMPlatformIP4Address, { dst->ifindex = src->ifindex; dst->plen = src->plen; dst->address = src->address; + dst->peer_address = src->peer_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, { @@ -552,24 +748,6 @@ _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { 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. * */ @@ -603,6 +781,7 @@ nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) klass = NMP_OBJECT_GET_CLASS (obj1); return klass == NMP_OBJECT_GET_CLASS (obj2) + && klass->cmd_plobj_id_equal && klass->cmd_plobj_id_equal (&obj1->object, &obj2->object); } @@ -619,10 +798,13 @@ _vt_cmd_plobj_id_equal (link, NMPlatformLink, _vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address, obj1->ifindex == obj2->ifindex && obj1->plen == obj2->plen - && obj1->address == obj2->address); + && obj1->address == obj2->address + /* for IPv4 addresses, you can add the same local address with differing peer-adddress + * (IFA_ADDRESS), provided that their net-part differs. */ + && ((obj1->peer_address ^ obj2->peer_address) & nm_utils_ip4_prefix_to_netmask (obj1->plen)) == 0); _vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ && IN6_ARE_ADDR_EQUAL (&obj1->address, &obj2->address)); _vt_cmd_plobj_id_equal (ip4_route, NMPlatformIP4Route, obj1->ifindex == obj2->ifindex @@ -638,11 +820,20 @@ _vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route, guint nmp_object_id_hash (const NMPObject *obj) { + const NMPClass *klass; + 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); + + klass = NMP_OBJECT_GET_CLASS (obj); + + if (klass->cmd_plobj_id_hash) + return klass->cmd_plobj_id_hash (&obj->object); + + /* unhashable objects implement pointer equality. */ + return g_direct_hash (obj); } #define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \ @@ -655,34 +846,25 @@ _vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ 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); + + /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ + hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen))); }) _vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { hash = (guint) 2907861637u; hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ hash = hash * 33 + _id_hash_ip6_addr (&obj->address); }) _vt_cmd_plobj_id_hash (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); @@ -700,11 +882,15 @@ _vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { gboolean nmp_object_is_alive (const NMPObject *obj) { + const NMPClass *klass; + /* for convenience, allow NULL. */ if (!obj) return FALSE; - return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_alive (obj); + klass = NMP_OBJECT_GET_CLASS (obj); + return !klass->cmd_obj_is_alive + || klass->cmd_obj_is_alive (obj); } static gboolean @@ -742,75 +928,29 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) gboolean nmp_object_is_visible (const NMPObject *obj) - { + const NMPClass *klass; + /* for convenience, allow NULL. */ if (!obj) return FALSE; - return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_visible (obj); -} + klass = NMP_OBJECT_GET_CLASS (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]; -} + /* a dead object is never visible. */ + if ( klass->cmd_obj_is_alive + && !klass->cmd_obj_is_alive (obj)) + return FALSE; -static gboolean -_vt_cmd_obj_is_visible_ipx_address (const NMPObject *obj) -{ - return obj->object.ifindex > 0; + return !klass->cmd_obj_is_visible + || klass->cmd_obj_is_visible (obj); } 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) +_vt_cmd_obj_is_visible_link (const NMPObject *obj) { - return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_nl (platform, &obj->object, id_only); + return obj->_link.netlink.is_in_netlink + && obj->link.name[0]; } /******************************************************************/ @@ -922,6 +1062,24 @@ nmp_cache_id_init_routes_visible (NMPCacheId *id, return id; } +NMPCacheId * +nmp_cache_id_init_link_by_ifname (NMPCacheId *id, + const char *ifname) +{ + gsize l; + + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME); + + if ( !ifname + || (l = strlen (ifname)) > sizeof (id->link_by_ifname.ifname_short)) + g_return_val_if_reached (id); + + /* the trailing NUL is dropped!! */ + memcpy (id->link_by_ifname.ifname_short, ifname, l); + + return id; +} + /******************************************************************/ static gboolean @@ -940,14 +1098,26 @@ _nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCach *out_id = NULL; return TRUE; default: - return klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); + return klass->cmd_obj_init_cache_id + && 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; + switch (id_type) { + case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: + if (obj->link.name[0]) { + *out_id = nmp_cache_id_init_link_by_ifname (id, obj->link.name); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; } static gboolean @@ -955,7 +1125,7 @@ _vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_t { switch (id_type) { case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (_vt_cmd_obj_is_visible_ipx_address (obj)) { + if (nmp_object_is_visible (obj)) { nm_assert (obj->object.ifindex > 0); *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); return TRUE; @@ -973,14 +1143,14 @@ _vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_typ { switch (id_type) { case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (_vt_cmd_obj_is_visible_ipx_route (obj)) { + if (nmp_object_is_visible (obj)) { nm_assert (obj->object.ifindex > 0); *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); return TRUE; } break; case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: - if ( _vt_cmd_obj_is_visible_ipx_route (obj) + if ( nmp_object_is_visible (obj) && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { nm_assert (obj->object.ifindex > 0); *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0); @@ -988,7 +1158,7 @@ _vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_typ } break; case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: - if ( _vt_cmd_obj_is_visible_ipx_route (obj) + if ( nmp_object_is_visible (obj) && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { nm_assert (obj->object.ifindex > 0); *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0); @@ -996,7 +1166,7 @@ _vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_typ } break; case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT: - if ( _vt_cmd_obj_is_visible_ipx_route (obj) + if ( nmp_object_is_visible (obj) && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { nm_assert (obj->object.ifindex > 0); *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex); @@ -1004,7 +1174,7 @@ _vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_typ } break; case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT: - if ( _vt_cmd_obj_is_visible_ipx_route (obj) + if ( nmp_object_is_visible (obj) && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { nm_assert (obj->object.ifindex > 0); *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex); @@ -1211,7 +1381,7 @@ nmp_cache_lookup_link_full (const NMPCache *cache, const NMPObject *obj; const NMPlatformObject *const *list; guint i, len; - NMPCacheId cache_id; + NMPCacheId cache_id, *p_cache_id; if (ifindex > 0) { obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); @@ -1226,7 +1396,14 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } 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); + if ( ifname + && strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) { + p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname); + ifname = NULL; + } else + p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only); + + list = nmp_cache_lookup_multi (cache, p_cache_id, &len); for (i = 0; i < len; i++) { obj = NMP_OBJECT_UP_CAST (list[i]); @@ -1303,12 +1480,8 @@ _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)) + if (!nm_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); } @@ -1385,7 +1558,7 @@ nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObjec { if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { NMPObject *old; - auto_nmp_obj NMPObject *obj = NULL; + nm_auto_nmpobj 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 @@ -1510,8 +1683,8 @@ nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, * 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(). + * Instead we just don't support it, instead we expect the user to + * create a new instance from netlink. * * TL;DR: a cached object must never be modified. */ @@ -1580,7 +1753,7 @@ 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; + nm_auto_nmpobj NMPObject *obj = NULL; old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); @@ -1647,7 +1820,7 @@ 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; + nm_auto_nmpobj NMPObject *obj = NULL; old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); @@ -1727,7 +1900,7 @@ nmp_cache_free (NMPCache *cache) void ASSERT_nmp_cache_is_consistent (const NMPCache *cache) { -#ifdef NM_MORE_ASSERTS +#if NM_MORE_ASSERTS NMMultiIndexIter iter_multi; GHashTableIter iter_hash; guint i, len; @@ -1788,24 +1961,23 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .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_id = NM_PLATFORM_SIGNAL_ID_LINK, .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_cmp = _vt_cmd_obj_cmp_link, .cmd_obj_copy = _vt_cmd_obj_copy_link, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link, .cmd_obj_dispose = _vt_cmd_obj_dispose_link, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, - .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_link, - .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_link, + .cmd_obj_to_string = _vt_cmd_obj_to_string_link, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link, .cmd_plobj_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_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) 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] = { @@ -1813,23 +1985,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .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_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, .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_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) 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] = { @@ -1837,23 +2004,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .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_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, .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_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) 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] = { @@ -1861,23 +2023,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .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_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, .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_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { @@ -1885,24 +2042,104 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .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_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, .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_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, }, + [NMP_OBJECT_TYPE_LNK_GRE - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_GRE, + .sizeof_data = sizeof (NMPObjectLnkGre), + .sizeof_public = sizeof (NMPlatformLnkGre), + .obj_type_name = "gre", + .lnk_link_type = NM_LINK_TYPE_GRE, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp, + }, + [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, + .sizeof_data = sizeof (NMPObjectLnkInfiniband), + .sizeof_public = sizeof (NMPlatformLnkInfiniband), + .obj_type_name = "infiniband", + .lnk_link_type = NM_LINK_TYPE_INFINIBAND, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_infiniband_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_infiniband_cmp, + }, + [NMP_OBJECT_TYPE_LNK_IP6TNL - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_IP6TNL, + .sizeof_data = sizeof (NMPObjectLnkIp6Tnl), + .sizeof_public = sizeof (NMPlatformLnkIp6Tnl), + .obj_type_name = "ip6tnl", + .lnk_link_type = NM_LINK_TYPE_IP6TNL, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp, + }, + [NMP_OBJECT_TYPE_LNK_IPIP - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_IPIP, + .sizeof_data = sizeof (NMPObjectLnkIpIp), + .sizeof_public = sizeof (NMPlatformLnkIpIp), + .obj_type_name = "ipip", + .lnk_link_type = NM_LINK_TYPE_IPIP, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ipip_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ipip_cmp, + }, + [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN, + .sizeof_data = sizeof (NMPObjectLnkMacvlan), + .sizeof_public = sizeof (NMPlatformLnkMacvlan), + .obj_type_name = "macvlan", + .lnk_link_type = NM_LINK_TYPE_MACVLAN, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, + }, + [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP, + .sizeof_data = sizeof (NMPObjectLnkMacvtap), + .sizeof_public = sizeof (NMPlatformLnkMacvtap), + .obj_type_name = "macvtap", + .lnk_link_type = NM_LINK_TYPE_MACVTAP, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, + }, + [NMP_OBJECT_TYPE_LNK_SIT - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_SIT, + .sizeof_data = sizeof (NMPObjectLnkSit), + .sizeof_public = sizeof (NMPlatformLnkSit), + .obj_type_name = "sit", + .lnk_link_type = NM_LINK_TYPE_SIT, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_sit_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp, + }, + [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, + .sizeof_data = sizeof (NMPObjectLnkVlan), + .sizeof_public = sizeof (NMPlatformLnkVlan), + .obj_type_name = "vlan", + .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, + .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, + .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, + .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, + }, + [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_VXLAN, + .sizeof_data = sizeof (NMPObjectLnkVxlan), + .sizeof_public = sizeof (NMPlatformLnkVxlan), + .obj_type_name = "vxlan", + .lnk_link_type = NM_LINK_TYPE_VXLAN, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vxlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vxlan_cmp, + }, }; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 5b1887b8..ab1cc2fe 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -64,6 +64,9 @@ typedef enum { /*< skip >*/ /* all the objects of a certain type */ NMP_CACHE_ID_TYPE_OBJECT_TYPE, + /* index for the link objects by ifname. */ + NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, + /* all the visible objects of a certain type */ NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, @@ -104,6 +107,11 @@ typedef struct { guint8 obj_type; /* NMPObjectType as guint8 */ int ifindex; } object_type_by_ifindex; + struct { + /* NMP_CACHE_ID_TYPE_LINK_BY_IFNAME */ + guint8 _id_type; + char ifname_short[IFNAMSIZ - 1]; /* don't include the trailing NUL so the struct fits in 4 bytes. */ + } link_by_ifname; }; } NMPCacheId; @@ -116,29 +124,31 @@ typedef struct { int rtm_gettype; int sizeof_data; int sizeof_public; + NMPlatformSignalIdType signal_type_id; const char *obj_type_name; - const char *nl_type; const char *signal_type; + /* Only for NMPObjectLnk* types. */ + NMLinkType lnk_link_type; + /* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index, * initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */ gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id); - gboolean (*cmd_obj_equal) (const NMPObject *obj1, const NMPObject *obj2); + int (*cmd_obj_cmp) (const NMPObject *obj1, const NMPObject *obj2); void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src); void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src); void (*cmd_obj_dispose) (NMPObject *obj); gboolean (*cmd_obj_is_alive) (const NMPObject *obj); gboolean (*cmd_obj_is_visible) (const NMPObject *obj); + const char *(*cmd_obj_to_string) (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); /* 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); + const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj, char *buf, gsize len); int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; @@ -148,7 +158,10 @@ typedef struct { NMPlatformLink _public; struct { - guint8 is_in_netlink; + bool is_in_netlink; + + /* Additional data that depends on the link-type (IFLA_INFO_DATA) */ + NMPObject *lnk; } netlink; struct { @@ -157,6 +170,45 @@ typedef struct { } NMPObjectLink; typedef struct { + NMPlatformLnkGre _public; +} NMPObjectLnkGre; + +typedef struct { + NMPlatformLnkInfiniband _public; +} NMPObjectLnkInfiniband; + +typedef struct { + NMPlatformLnkIp6Tnl _public; +} NMPObjectLnkIp6Tnl; + +typedef struct { + NMPlatformLnkIpIp _public; +} NMPObjectLnkIpIp; + +typedef struct { + NMPlatformLnkMacvlan _public; +} NMPObjectLnkMacvlan; + +typedef NMPObjectLnkMacvlan NMPObjectLnkMacvtap; + +typedef struct { + NMPlatformLnkSit _public; +} NMPObjectLnkSit; + +typedef struct { + NMPlatformLnkVlan _public; + + guint n_ingress_qos_map; + guint n_egress_qos_map; + const NMVlanQosMapping *ingress_qos_map; + const NMVlanQosMapping *egress_qos_map; +} NMPObjectLnkVlan; + +typedef struct { + NMPlatformLnkVxlan _public; +} NMPObjectLnkVxlan; + +typedef struct { NMPlatformIP4Address _public; } NMPObjectIP4Address; @@ -175,13 +227,37 @@ typedef struct { struct _NMPObject { const NMPClass *_class; int _ref_count; - guint8 is_cached; + bool is_cached; union { NMPlatformObject object; NMPlatformLink link; NMPObjectLink _link; + NMPlatformLnkGre lnk_gre; + NMPObjectLnkGre _lnk_gre; + + NMPlatformLnkInfiniband lnk_infiniband; + NMPObjectLnkInfiniband _lnk_infiniband; + + NMPlatformLnkIpIp lnk_ipip; + NMPObjectLnkIpIp _lnk_ipip; + + NMPlatformLnkIp6Tnl lnk_ip6tnl; + NMPObjectLnkIp6Tnl _lnk_ip6tnl; + + NMPlatformLnkMacvlan lnk_macvlan; + NMPObjectLnkMacvlan _lnk_macvlan; + + NMPlatformLnkSit lnk_sit; + NMPObjectLnkSit _lnk_sit; + + NMPlatformLnkVlan lnk_vlan; + NMPObjectLnkVlan _lnk_vlan; + + NMPlatformLnkVxlan lnk_vxlan; + NMPObjectLnkVxlan _lnk_vxlan; + NMPlatformIPAddress ip_address; NMPlatformIPXAddress ipx_address; NMPlatformIP4Address ip4_address; @@ -269,7 +345,7 @@ 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_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen, guint32 peer_address); 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); @@ -286,9 +362,9 @@ 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))) +#define nm_auto_nmpobj __attribute__((cleanup(_nm_auto_nmpobj_cleanup))) static inline void -_nmp_auto_obj_cleanup (NMPObject **pobj) +_nm_auto_nmpobj_cleanup (NMPObject **pobj) { nmp_object_unref (*pobj); } @@ -306,6 +382,7 @@ 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); +NMPCacheId *nmp_cache_id_init_link_by_ifname (NMPCacheId *id, const char *ifname); 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); @@ -341,21 +418,4 @@ NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifi 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 085f56ca..b097fcf6 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -1,14 +1,14 @@ AM_CPPFLAGS = \ -I${top_srcdir} \ - -I${top_srcdir}/include \ - -I${top_builddir}/include \ + -I${top_srcdir}/shared \ + -I${top_builddir}/shared \ -I${top_srcdir}/src \ -I${top_builddir}/src \ -I${top_srcdir}/libnm-core \ -I${top_builddir}/libnm-core \ -I${srcdir}/.. \ -DG_LOG_DOMAIN=\""NetworkManager"\" \ - -DNETWORKMANAGER_COMPILATION \ + -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ $(GLIB_CFLAGS) \ $(GUDEV_CFLAGS) \ diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in index e21a3bb6..c04d6a1b 100644 --- a/src/platform/tests/Makefile.in +++ b/src/platform/tests/Makefile.in @@ -503,7 +503,6 @@ BLUEZ5_LIBS = @BLUEZ5_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CKDB_PATH = @CKDB_PATH@ CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ @@ -515,8 +514,6 @@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ -DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ -DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SYS_DIR = @DBUS_SYS_DIR@ DEFS = @DEFS@ @@ -526,6 +523,7 @@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ DNSMASQ_PATH = @DNSMASQ_PATH@ +DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -580,16 +578,13 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ -IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ -IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ +LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ +LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ LIBDL = @LIBDL@ -LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ -LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ -LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -626,6 +621,8 @@ NEWT_LIBS = @NEWT_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@ +NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@ NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ NM_MICRO_VERSION = @NM_MICRO_VERSION@ NM_MINOR_VERSION = @NM_MINOR_VERSION@ @@ -654,7 +651,6 @@ POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ PPPD_PATH = @PPPD_PATH@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ -PPPOE_PATH = @PPPOE_PATH@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ @@ -669,6 +665,8 @@ SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@ +SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@ SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ @@ -729,6 +727,7 @@ mkdir_p = @mkdir_p@ nmbinary = @nmbinary@ nmconfdir = @nmconfdir@ nmdatadir = @nmdatadir@ +nmlibdir = @nmlibdir@ nmrundir = @nmrundir@ nmstatedir = @nmstatedir@ oldincludedir = @oldincludedir@ @@ -736,6 +735,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -751,12 +751,12 @@ with_dhcpcd = @with_dhcpcd@ with_netconfig = @with_netconfig@ with_resolvconf = @with_resolvconf@ with_valgrind = @with_valgrind@ -AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/include \ - -I${top_builddir}/include -I${top_srcdir}/src \ +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/shared \ + -I${top_builddir}/shared -I${top_srcdir}/src \ -I${top_builddir}/src -I${top_srcdir}/libnm-core \ -I${top_builddir}/libnm-core -I${srcdir}/.. \ -DG_LOG_DOMAIN=\""NetworkManager"\" \ - -DNETWORKMANAGER_COMPILATION \ + -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(GLIB_CFLAGS) \ $(GUDEV_CFLAGS) $(LIBNL_CFLAGS) $(am__append_1) PLATFORM_SOURCES = \ diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index f1058db8..5cc3114c 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -3,34 +3,66 @@ #include <stdlib.h> #include <syslog.h> +#include "nm-default.h" #include "nm-linux-platform.h" -#include "nm-logging.h" #include "nm-test-utils.h" NMTST_DEFINE (); +static struct { + gboolean persist; +} global_opt = { + .persist = TRUE, +}; + +static gboolean +read_argv (int *argc, char ***argv) +{ + GOptionContext *context; + GOptionEntry options[] = { + { "no-persist", 'P', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &global_opt.persist, "Exit after processing netlink messages", NULL }, + { 0 }, + }; + gs_free_error GError *error = NULL; + + context = g_option_context_new (NULL); + g_option_context_set_summary (context, "Monitor netlink events in NMPlatform."); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, argc, argv, &error)) { + g_warning ("Error parsing command line arguments: %s", error->message); + g_option_context_free (context); + return FALSE; + } + + g_option_context_free (context); + return TRUE; +} + int main (int argc, char **argv) { GMainLoop *loop; -#if !GLIB_CHECK_VERSION (2, 35, 0) - g_type_init (); -#endif - if (!g_getenv ("G_MESSAGES_DEBUG")) g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL"); + if (!read_argv (&argc, &argv)) + return 2; + nm_log_info (LOGD_PLATFORM, "platform monitor start"); loop = g_main_loop_new (NULL, FALSE); nm_linux_platform_setup (); - g_main_loop_run (loop); + if (global_opt.persist) + g_main_loop_run (loop); + + g_main_loop_unref (loop); return EXIT_SUCCESS; } diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index d6f346b3..566bb479 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -4,12 +4,19 @@ #define DEVICE_NAME "nm-test-device" #define IP4_ADDRESS "192.0.2.1" +#define IP4_ADDRESS_PEER "192.0.2.2" +#define IP4_ADDRESS_PEER2 "192.0.3.1" #define IP4_PLEN 24 #define IP6_ADDRESS "2001:db8:a:b:1:2:3:4" #define IP6_PLEN 64 +static int DEVICE_IFINDEX = -1; +static int EX = -1; + +/*****************************************************************************/ + static void -ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, 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, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -25,11 +32,11 @@ ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, 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, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -45,13 +52,15 @@ ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } +/*****************************************************************************/ + static void -test_ip4_address (void) +test_ip4_address_general (void) { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + const int ifindex = DEVICE_IFINDEX; 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); @@ -64,13 +73,13 @@ test_ip4_address (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); /* Add address */ - 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)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, NULL); accept_signals (address_changed, 0, 1); /* Test address listing */ @@ -80,16 +89,17 @@ test_ip4_address (void) address = &g_array_index (addresses, NMPlatformIP4Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); g_assert_cmphex (address->address, ==, addr); + g_assert_cmphex (address->peer_address, ==, addr); g_assert_cmpint (address->plen, ==, IP4_PLEN); g_array_unref (addresses); /* Remove address */ - 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)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); free_signal (address_added); free_signal (address_changed); @@ -97,9 +107,9 @@ test_ip4_address (void) } static void -test_ip6_address (void) +test_ip6_address_general (void) { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + const int ifindex = DEVICE_IFINDEX; 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); @@ -114,12 +124,12 @@ test_ip6_address (void) /* Add address */ 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)); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, 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 (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); accept_signals (address_changed, 0, 1); /* Test address listing */ @@ -133,12 +143,15 @@ test_ip6_address (void) g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + nmtstp_ip6_address_del (EX, 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 (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + nmtstp_ip6_address_del (EX, ifindex, addr, IP6_PLEN); + + /* ensure not pending signal. */ + accept_signals (address_changed, 0, 1); free_signal (address_added); free_signal (address_changed); @@ -146,11 +159,11 @@ test_ip6_address (void) } static void -test_ip4_address_external (void) +test_ip4_address_general_2 (void) { + const int ifindex = DEVICE_IFINDEX; 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 (NM_PLATFORM_GET, DEVICE_NAME); in_addr_t addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -161,38 +174,31 @@ test_ip4_address_external (void) /* Looks like addresses are not announced by kerenl when the interface * is down. Link-local IPv6 address is automatically added. */ - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, 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_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_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + accept_signal (address_added); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); + accept_signal (address_removed); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); /* 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 (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)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); 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)); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); - accept_signal (address_removed);*/ free_signal (address_added); free_signal (address_removed); } static void -test_ip6_address_external (void) +test_ip6_address_general_2 (void) { + const int ifindex = DEVICE_IFINDEX; 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 (NM_PLATFORM_GET, DEVICE_NAME); struct in6_addr addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -201,51 +207,197 @@ test_ip6_address_external (void) inet_pton (AF_INET6, IP6_ADDRESS, &addr); /* Add/delete notification */ - 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); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); + accept_signal (address_added); g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); - wait_signal (address_removed); + + nmtstp_ip6_address_del (EX, ifindex, addr, IP6_PLEN); + accept_signal (address_removed); 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 (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); + accept_signal (address_added); g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); + ensure_no_signal (address_added); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + + free_signal (address_added); + free_signal (address_removed); +} + +/*****************************************************************************/ + +static void +test_ip4_address_peer (void) +{ + const int ifindex = DEVICE_IFINDEX; + 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); + in_addr_t addr, addr_peer, addr_peer2; + guint32 lifetime = 2000; + guint32 preferred = 1000; + const NMPlatformIP4Address *a; + + inet_pton (AF_INET, IP4_ADDRESS, &addr); + inet_pton (AF_INET, IP4_ADDRESS_PEER, &addr_peer); + inet_pton (AF_INET, IP4_ADDRESS_PEER2, &addr_peer2); + g_assert (ifindex > 0); + + g_assert (addr != addr_peer); + + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + accept_signals (address_removed, 0, G_MAXINT); + accept_signals (address_added, 0, G_MAXINT); + + /* Add/delete notification */ + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer, lifetime, preferred, NULL); accept_signal (address_added); - /*run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); - g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - wait_signal (address_removed);*/ + g_assert ((a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer))); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2)); + + nmtstp_ip_address_assert_lifetime ((NMPlatformIPAddress *) a, -1, lifetime, preferred); + + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer2, lifetime, preferred, NULL); + accept_signal (address_added); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer)); + g_assert ((a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2))); + + nmtstp_ip_address_assert_lifetime ((NMPlatformIPAddress *) a, -1, lifetime, preferred); + + g_assert (addr != addr_peer); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr_peer); + accept_signal (address_removed); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2)); free_signal (address_added); free_signal (address_removed); } +/*****************************************************************************/ + +static void +test_ip4_address_peer_zero (void) +{ + const int ifindex = DEVICE_IFINDEX; + in_addr_t addr, addr_peer; + guint32 lifetime = 2000; + guint32 preferred = 1000; + const int plen = 24; + const char *label = NULL; + in_addr_t peers[3], r_peers[3]; + int i; + GArray *addrs; + + g_assert (ifindex > 0); + + inet_pton (AF_INET, "192.168.5.2", &addr); + inet_pton (AF_INET, "192.168.6.2", &addr_peer); + peers[0] = addr; + peers[1] = addr_peer; + peers[2] = 0; + + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + + nmtst_rand_perm (NULL, r_peers, peers, sizeof (peers[0]), G_N_ELEMENTS (peers)); + for (i = 0; i < G_N_ELEMENTS (peers); i++) { + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); + + nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, label); + + addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + g_assert (addrs); + g_assert_cmpint (addrs->len, ==, i + 1); + g_array_unref (addrs); + } + + if (nmtst_is_debug () && nmtstp_is_root_test ()) + nmtstp_run_command_check ("ip address show dev %s", DEVICE_NAME); + + nmtst_rand_perm (NULL, r_peers, peers, sizeof (peers[0]), G_N_ELEMENTS (peers)); + for (i = 0; i < G_N_ELEMENTS (peers); i++) { + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); + + nmtstp_ip4_address_del (EX, ifindex, addr, plen, r_peers[i]); + + addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + g_assert (addrs); + g_assert_cmpint (addrs->len, ==, G_N_ELEMENTS (peers) - i - 1); + g_array_unref (addrs); + } +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { nmtst_init_with_logging (argc, argv, NULL, "ALL"); } -void -setup_tests (void) +/***************************************************************************** + * SETUP TESTS + *****************************************************************************/ + +typedef struct { + const char *testpath; + GTestFunc test_func; +} TestSetup; + +static void +_g_test_run (gconstpointer user_data) { - SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); + const TestSetup *s = user_data; + int ifindex; + + _LOGT ("TEST: start %s", s->testpath); nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); + g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); - g_test_add_func ("/address/internal/ip4", test_ip4_address); - g_test_add_func ("/address/internal/ip6", test_ip6_address); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + g_assert_cmpint (ifindex, >, 0); + g_assert_cmpint (DEVICE_IFINDEX, ==, -1); - if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) { - g_test_add_func ("/address/external/ip4", test_ip4_address_external); - g_test_add_func ("/address/external/ip6", test_ip6_address_external); - } + DEVICE_IFINDEX = ifindex; + EX = nmtstp_run_command_check_external_global (); + + s->test_func (); + + g_assert_cmpint (DEVICE_IFINDEX, ==, ifindex); + DEVICE_IFINDEX = -1; + + g_assert_cmpint (ifindex, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + _LOGT ("TEST: finished %s", s->testpath); +} + +static void +_g_test_add_func (const char *testpath, + GTestFunc test_func) +{ + TestSetup *s; + + s = g_new0 (TestSetup, 1); + s->testpath = testpath; + s->test_func = test_func; + + g_test_add_data_func_full (testpath, s, _g_test_run, g_free); +} + +void +setup_tests (void) +{ + _g_test_add_func ("/address/ipv4/general", test_ip4_address_general); + _g_test_add_func ("/address/ipv6/general", test_ip6_address_general); + + _g_test_add_func ("/address/ipv4/general-2", test_ip4_address_general_2); + _g_test_add_func ("/address/ipv6/general-2", test_ip6_address_general_2); + + _g_test_add_func ("/address/ipv4/peer", test_ip4_address_peer); + _g_test_add_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); } diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 0b825114..9e624a02 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -35,7 +35,7 @@ test_cleanup_internal (void) inet_pton (AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6); /* Create and set up device */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); @@ -43,8 +43,8 @@ test_cleanup_internal (void) g_assert (ifindex > 0); /* Add routes and addresses */ - 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_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, NULL)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, 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)); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index b0eae63b..09a57f87 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -2,6 +2,8 @@ #include <sys/mount.h> #include <sched.h> +#include <sys/wait.h> +#include <fcntl.h> #include "test-common.h" @@ -10,9 +12,8 @@ #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) +nmtstp_is_root_test (void) { NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") return (SETUP == nm_linux_platform_setup); @@ -20,9 +21,9 @@ nmtst_platform_is_root_test (void) } gboolean -nmtst_platform_is_sysfs_writable (void) +nmtstp_is_sysfs_writable (void) { - return !nmtst_platform_is_root_test () + return !nmtstp_is_root_test () || (access ("/sys/devices", W_OK) == 0); } @@ -34,7 +35,7 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall data->name = name; data->change_type = change_type; data->received_count = 0; - data->handler_id = g_signal_connect (nm_platform_get (), name, callback, data); + data->handler_id = g_signal_connect (NM_PLATFORM_GET, name, callback, data); data->ifindex = ifindex; data->ifname = ifname; @@ -46,7 +47,7 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall void _accept_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("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; @@ -55,7 +56,7 @@ _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) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); + _LOGD ("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; @@ -64,15 +65,28 @@ _accept_signals (const char *file, int line, const char *func, SignalData *data, 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)); + _LOGD ("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 +_accept_or_wait_signal (const char *file, int line, const char *func, SignalData *data) +{ + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): accept-or-wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count == 0) { + 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 (file, line, func, data); +} + +void _wait_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("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)); @@ -86,16 +100,16 @@ _wait_signal (const char *file, int line, const char *func, SignalData *data) void _free_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): free signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("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_signal_handler_disconnect (NM_PLATFORM_GET, data->handler_id); g_free (data); } void -link_callback (NMPlatform *platform, NMPObjectType obj_type, 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, SignalData *data) { GArray *links; NMPlatformLink *cached; @@ -114,12 +128,12 @@ link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlat return; if (data->loop) { - debug ("Quitting main loop."); + _LOGD ("Quitting main loop."); g_main_loop_quit (data->loop); } 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); + _LOGD ("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 (NM_PLATFORM_GET, ifindex)); @@ -253,21 +267,1176 @@ _assert_ip4_route_exists (const char *file, guint line, const char *func, gboole } } -void -run_command (const char *format, ...) +int +nmtstp_run_command (const char *format, ...) { - char *command; + int result; + gs_free char *command = NULL; va_list ap; va_start (ap, format); command = g_strdup_vprintf (format, ap); va_end (ap); - debug ("Running command: %s", command); - g_assert (!system (command)); - debug ("Command finished."); - g_free (command); + + _LOGD ("Running command: %s", command); + result = system (command); + _LOGD ("Command finished: result=%d", result); + + return result; +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop *loop; + gboolean timeout; + guint id; +} WaitForSignalData; + +static void +_wait_for_signal_cb (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + NMPlatformLink *plink, + NMPlatformSignalChangeType change_type, + gpointer user_data) +{ + WaitForSignalData *data = user_data; + + g_main_loop_quit (data->loop); +} + +static gboolean +_wait_for_signal_timeout (gpointer user_data) +{ + WaitForSignalData *data = user_data; + + data->timeout = TRUE; + data->id = 0; + g_main_loop_quit (data->loop); + return G_SOURCE_REMOVE; +} + +gboolean +nmtstp_wait_for_signal (guint timeout_ms) +{ + WaitForSignalData data = { 0 }; + + gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route; + + data.loop = g_main_loop_new (NULL, FALSE); + + id_link = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip4_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip6_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip4_route = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip6_route = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + + if (timeout_ms != 0) + data.id = g_timeout_add (timeout_ms, _wait_for_signal_timeout, &data); + + g_main_loop_run (data.loop); + + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_link)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_address)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_address)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_route)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_route)); + + if (nm_clear_g_source (&data.id)) + g_assert (timeout_ms != 0 && !data.timeout); + + g_clear_pointer (&data.loop, g_main_loop_unref); + + return !data.timeout; +} + +gboolean +nmtstp_wait_for_signal_until (gint64 until_ms) +{ + gint64 now; + + while (TRUE) { + now = nm_utils_get_monotonic_timestamp_ms (); + + if (until_ms < now) + return FALSE; + + if (nmtstp_wait_for_signal (MAX (1, until_ms - now))) + return TRUE; + } +} + +const NMPlatformLink * +nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +{ + return nmtstp_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); +} + +const NMPlatformLink * +nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms) +{ + const NMPlatformLink *plink; + gint64 now; + + while (TRUE) { + now = nm_utils_get_monotonic_timestamp_ms (); + + plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname); + if ( plink + && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type)) + return plink; + + if (until_ms < now) + return NULL; + + nmtstp_wait_for_signal (MAX (1, until_ms - now)); + } +} + +const NMPlatformLink * +nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +{ + return nmtstp_assert_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); +} + +const NMPlatformLink * +nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms) +{ + const NMPlatformLink *plink; + + plink = nmtstp_wait_for_link_until (ifname, expected_link_type, until_ms); + g_assert (plink); + return plink; +} + +int +nmtstp_run_command_check_external_global (void) +{ + if (!nmtstp_is_root_test ()) + return FALSE; + switch (nmtst_get_rand_int () % 3) { + case 0: + return -1; + case 1: + return FALSE; + default: + return TRUE; + } +} + +gboolean +nmtstp_run_command_check_external (int external_command) +{ + if (external_command != -1) { + g_assert (NM_IN_SET (external_command, FALSE, TRUE)); + g_assert (!external_command || nmtstp_is_root_test ()); + return !!external_command; + } + if (!nmtstp_is_root_test ()) + return FALSE; + return (nmtst_get_rand_int () % 2) == 0; +} + +#define CHECK_LIFETIME_MAX_DIFF 2 + +gboolean +nmtstp_ip_address_check_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred) +{ + gint64 offset; + int i; + + g_assert (addr); + + if (now == -1) + now = nm_utils_get_monotonic_timestamp_s (); + g_assert (now > 0); + + g_assert (expected_preferred <= expected_lifetime); + + if ( expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT) { + return addr->timestamp == 0 + && addr->lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && addr->preferred == NM_PLATFORM_LIFETIME_PERMANENT; + } + + if (addr->timestamp == 0) + return FALSE; + + offset = (gint64) now - addr->timestamp; + + for (i = 0; i < 2; i++) { + guint32 lft = i ? expected_lifetime : expected_preferred; + guint32 adr = i ? addr->lifetime : addr->preferred; + + if (lft == NM_PLATFORM_LIFETIME_PERMANENT) { + if (adr != NM_PLATFORM_LIFETIME_PERMANENT) + return FALSE; + } else { + if ( adr - offset <= lft - CHECK_LIFETIME_MAX_DIFF + || adr - offset >= lft + CHECK_LIFETIME_MAX_DIFF) + return FALSE; + } + } + return TRUE; +} + +void +nmtstp_ip_address_assert_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred) +{ + gint64 n = now; + gint64 offset; + int i; + + g_assert (addr); + + if (now == -1) + now = nm_utils_get_monotonic_timestamp_s (); + g_assert (now > 0); + + g_assert (expected_preferred <= expected_lifetime); + + if ( expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT) { + g_assert_cmpint (addr->timestamp, ==, 0); + g_assert_cmpint (addr->lifetime, ==, NM_PLATFORM_LIFETIME_PERMANENT); + g_assert_cmpint (addr->preferred, ==, NM_PLATFORM_LIFETIME_PERMANENT); + return; + } + + g_assert_cmpint (addr->timestamp, >, 0); + g_assert_cmpint (addr->timestamp, <=, now); + + offset = (gint64) now - addr->timestamp; + g_assert_cmpint (offset, >=, 0); + + for (i = 0; i < 2; i++) { + guint32 lft = i ? expected_lifetime : expected_preferred; + guint32 adr = i ? addr->lifetime : addr->preferred; + + if (lft == NM_PLATFORM_LIFETIME_PERMANENT) + g_assert_cmpint (adr, ==, NM_PLATFORM_LIFETIME_PERMANENT); + else { + g_assert_cmpint (adr, <=, lft); + g_assert_cmpint (offset, <=, adr); + g_assert_cmpint (adr - offset, <=, lft + CHECK_LIFETIME_MAX_DIFF); + g_assert_cmpint (adr - offset, >=, lft - CHECK_LIFETIME_MAX_DIFF); + } + } + + g_assert (nmtstp_ip_address_check_lifetime (addr, n, expected_lifetime, expected_preferred)); +} + +static void +_ip_address_add (gboolean external_command, + gboolean is_v4, + int ifindex, + const NMIPAddr *address, + int plen, + const NMIPAddr *peer_address, + guint32 lifetime, + guint32 preferred, + const char *label, + guint flags) +{ + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + gs_free char *s_valid = NULL; + gs_free char *s_preferred = NULL; + gs_free char *s_label = NULL; + char b1[NM_UTILS_INET_ADDRSTRLEN], b2[NM_UTILS_INET_ADDRSTRLEN]; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + s_valid = g_strdup_printf (" valid_lft %d", lifetime); + if (preferred != NM_PLATFORM_LIFETIME_PERMANENT) + s_preferred = g_strdup_printf (" preferred_lft %d", preferred); + if (label) + s_label = g_strdup_printf ("%s:%s", ifname, label); + + if (is_v4) { + char s_peer[100]; + + g_assert (flags == 0); + + if ( peer_address->addr4 != address->addr4 + || nmtst_get_rand_int () % 2) { + /* If the peer is the same as the local address, we can omit it. The result should be identical */ + g_snprintf (s_peer, sizeof (s_peer), " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); + } else + s_peer[0] = '\0'; + + nmtstp_run_command_check ("ip address change %s%s/%d dev %s%s%s%s", + nm_utils_inet4_ntop (address->addr4, b1), + s_peer, + plen, + ifname, + s_valid ?: "", + s_preferred ?: "", + s_label ?: ""); + } else { + g_assert (label == NULL); + + /* flags not implemented (yet) */ + g_assert (flags == 0); + nmtstp_run_command_check ("ip address change %s%s%s/%d dev %s%s%s%s", + nm_utils_inet6_ntop (&address->addr6, b1), + !IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) ? " peer " : "", + !IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) ? nm_utils_inet6_ntop (&peer_address->addr6, b2) : "", + plen, + ifname, + s_valid ?: "", + s_preferred ?: "", + s_label ?: ""); + } + } else { + gboolean success; + + if (is_v4) { + g_assert (flags == 0); + success = nm_platform_ip4_address_add (NM_PLATFORM_GET, + ifindex, + address->addr4, + plen, + peer_address->addr4, + lifetime, + preferred, + label); + } else { + g_assert (label == NULL); + success = nm_platform_ip6_address_add (NM_PLATFORM_GET, + ifindex, + address->addr6, + plen, + peer_address->addr6, + lifetime, + preferred, + flags); + } + g_assert (success); + } + + /* Let's wait until we see the address. */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + if (is_v4) { + const NMPlatformIP4Address *a; + + g_assert (flags == 0); + a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); + if ( a + && a->peer_address == peer_address->addr4 + && nmtstp_ip_address_check_lifetime ((NMPlatformIPAddress*) a, -1, lifetime, preferred) + && strcmp (a->label, label ?: "") == 0) + break; + } else { + const NMPlatformIP6Address *a; + + g_assert (label == NULL); + g_assert (flags == 0); + + a = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, address->addr6, plen); + if ( a + && !memcmp (nm_platform_ip6_address_get_peer (a), + (IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) || IN6_ARE_ADDR_EQUAL (&address->addr6, &peer_address->addr6)) + ? &address->addr6 : &peer_address->addr6, + sizeof (struct in6_addr)) + && nmtstp_ip_address_check_lifetime ((NMPlatformIPAddress*) a, -1, lifetime, preferred)) + break; + } + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + +#define _assert_pllink(success, pllink, name, type) \ + G_STMT_START { \ + const NMPlatformLink *_pllink = (pllink); \ + \ + if ((success)) { \ + g_assert (_pllink); \ + g_assert (_pllink == nmtstp_link_get_typed (_pllink->ifindex, (name), (type))); \ + } else { \ + g_assert (!_pllink); \ + g_assert (!nmtstp_link_get (0, (name))); \ + } \ + } G_STMT_END + +const NMPlatformLink * +nmtstp_link_dummy_add (gboolean external_command, + const char *name) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + success = !nmtstp_run_command ("ip link add %s type dummy", + name); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_DUMMY, 100); + } else + success = nm_platform_link_dummy_add (NM_PLATFORM_GET, name, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + g_assert (success); + _assert_pllink (success, pllink, name, NM_LINK_TYPE_DUMMY); + return pllink; +} + +const NMPlatformLink * +nmtstp_link_gre_add (gboolean external_command, + const char *name, + const NMPlatformLnkGre *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + success = !nmtstp_run_command ("ip tunnel add %s mode gre %s local %s remote %s ttl %u tos %02x %s", + name, + dev ? dev : "", + nm_utils_inet4_ntop (lnk->local, NULL), + nm_utils_inet4_ntop (lnk->remote, buffer), + lnk->ttl, + lnk->tos, + lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_GRE, 100); + } else + success = nm_platform_link_gre_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_GRE); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_ip6tnl_add (gboolean external_command, + const char *name, + const NMPlatformLnkIp6Tnl *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET6_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + const char *mode; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + switch (lnk->proto) { + case IPPROTO_IPIP: + mode = "ipip6"; + break; + case IPPROTO_IPV6: + mode = "ip6ip6"; + break; + default: + g_assert_not_reached (); + } + + success = !nmtstp_run_command ("ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %02x encaplimit %u flowlabel %x", + name, + mode, + dev, + nm_utils_inet6_ntop (&lnk->local, NULL), + nm_utils_inet6_ntop (&lnk->remote, buffer), + lnk->ttl, + lnk->tclass, + lnk->encap_limit, + lnk->flow_label); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IP6TNL, 100); + } else + success = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_IP6TNL); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_ipip_add (gboolean external_command, + const char *name, + const NMPlatformLnkIpIp *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + success = !nmtstp_run_command ("ip tunnel add %s mode ipip %s local %s remote %s ttl %u tos %02x %s", + name, + dev, + nm_utils_inet4_ntop (lnk->local, NULL), + nm_utils_inet4_ntop (lnk->remote, buffer), + lnk->ttl, + lnk->tos, + lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IPIP, 100); + } else + success = nm_platform_link_ipip_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_IPIP); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_macvlan_add (gboolean external_command, + const char *name, + int parent, + const NMPlatformLnkMacvlan *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + NMLinkType link_type; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + link_type = lnk->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN; + + if (external_command) { + const char *dev; + char *modes[] = { + [MACVLAN_MODE_BRIDGE] = "bridge", + [MACVLAN_MODE_VEPA] = "vepa", + [MACVLAN_MODE_PRIVATE] = "private", + [MACVLAN_MODE_PASSTHRU] = "passthru", + }; + + dev = nm_platform_link_get_name (NM_PLATFORM_GET, parent); + g_assert (dev); + g_assert_cmpint (lnk->mode, <, G_N_ELEMENTS (modes)); + + success = !nmtstp_run_command ("ip link add name %s link %s type %s mode %s %s", + name, + dev, + lnk->tap ? "macvtap" : "macvlan", + modes[lnk->mode], + lnk->no_promisc ? "nopromisc" : ""); + if (success) + pllink = nmtstp_assert_wait_for_link (name, link_type, 100); + } else + success = nm_platform_link_macvlan_add (NM_PLATFORM_GET, name, parent, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, link_type); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_sit_add (gboolean external_command, + const char *name, + const NMPlatformLnkSit *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *dev = ""; + + if (lnk->parent_ifindex) { + const char *parent_name; + + parent_name = nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex); + g_assert (parent_name); + dev = nm_sprintf_bufa (100, " dev %s", parent_name); + } + + success = !nmtstp_run_command ("ip tunnel add %s mode sit%s local %s remote %s ttl %u tos %02x %s", + name, + dev, + nm_utils_inet4_ntop (lnk->local, NULL), + nm_utils_inet4_ntop (lnk->remote, buffer), + lnk->ttl, + lnk->tos, + lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_SIT, 100); + } else + success = nm_platform_link_sit_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_SIT); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_vxlan_add (gboolean external_command, + const char *name, + const NMPlatformLnkVxlan *lnk) +{ + const NMPlatformLink *pllink = NULL; + NMPlatformError plerr; + int err; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + gs_free char *local = NULL, *remote = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + if (lnk->local) + local = g_strdup_printf ("%s", nm_utils_inet4_ntop (lnk->local, NULL)); + else if (memcmp (&lnk->local6, &in6addr_any, sizeof (in6addr_any))) + local = g_strdup_printf ("%s", nm_utils_inet6_ntop (&lnk->local6, NULL)); + + if (lnk->group) + remote = g_strdup_printf ("%s", nm_utils_inet4_ntop (lnk->group, NULL)); + else if (memcmp (&lnk->group6, &in6addr_any, sizeof (in6addr_any))) + remote = g_strdup_printf ("%s", nm_utils_inet6_ntop (&lnk->group6, NULL)); + + err = nmtstp_run_command ("ip link add %s type vxlan id %u %s local %s group %s ttl %u tos %02x dstport %u srcport %u %u ageing %u", + name, + lnk->id, + dev ? dev : "", + local, + remote, + lnk->ttl, + lnk->tos, + lnk->dst_port, + lnk->src_port_min, lnk->src_port_max, + lnk->ageing); + /* Older versions of iproute2 don't support adding vxlan devices. + * On failure, fallback to using platform code. */ + if (err == 0) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_VXLAN, 100); + else + _LOGI ("Adding vxlan device via iproute2 failed. Assume iproute2 is not up to the task."); + } + if (!pllink) { + plerr = nm_platform_link_vxlan_add (NM_PLATFORM_GET, name, lnk, &pllink); + g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (pllink); + } + + g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_VXLAN); + g_assert_cmpstr (pllink->name, ==, name); + return pllink; +} + +void +nmtstp_ip4_address_add (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address, + guint32 lifetime, + guint32 preferred, + const char *label) +{ + _ip_address_add (external_command, + TRUE, + ifindex, + (NMIPAddr *) &address, + plen, + (NMIPAddr *) &peer_address, + lifetime, + preferred, + label, + 0); +} + +void +nmtstp_ip6_address_add (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen, + struct in6_addr peer_address, + guint32 lifetime, + guint32 preferred, + guint flags) +{ + _ip_address_add (external_command, + FALSE, + ifindex, + (NMIPAddr *) &address, + plen, + (NMIPAddr *) &peer_address, + lifetime, + preferred, + NULL, + flags); +} + +static void +_ip_address_del (gboolean external_command, + gboolean is_v4, + int ifindex, + const NMIPAddr *address, + int plen, + const NMIPAddr *peer_address) +{ + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + char b1[NM_UTILS_INET_ADDRSTRLEN], b2[NM_UTILS_INET_ADDRSTRLEN]; + int success; + gboolean had_address; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + /* let's wait until we see the address as we added it. */ + if (is_v4) + had_address = !!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); + else + had_address = !!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, address->addr6, plen); + + if (is_v4) { + success = nmtstp_run_command ("ip address delete %s%s%s/%d dev %s", + nm_utils_inet4_ntop (address->addr4, b1), + peer_address->addr4 != address->addr4 ? " peer " : "", + peer_address->addr4 != address->addr4 ? nm_utils_inet4_ntop (peer_address->addr4, b2) : "", + plen, + ifname); + } else { + g_assert (!peer_address); + success = nmtstp_run_command ("ip address delete %s/%d dev %s", + nm_utils_inet6_ntop (&address->addr6, b1), + plen, + ifname); + } + g_assert (success == 0 || !had_address); + } else { + gboolean success; + + if (is_v4) { + success = nm_platform_ip4_address_delete (NM_PLATFORM_GET, + ifindex, + address->addr4, + plen, + peer_address->addr4); + } else { + g_assert (!peer_address); + success = nm_platform_ip6_address_delete (NM_PLATFORM_GET, + ifindex, + address->addr6, + plen); + } + g_assert (success); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + if (is_v4) { + const NMPlatformIP4Address *a; + + a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); + if (!a) + break; + } else { + const NMPlatformIP6Address *a; + + a = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, address->addr6, plen); + if (!a) + break; + } + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + +void +nmtstp_ip4_address_del (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address) +{ + _ip_address_del (external_command, + TRUE, + ifindex, + (NMIPAddr *) &address, + plen, + (NMIPAddr *) &peer_address); +} + +void +nmtstp_ip6_address_del (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen) +{ + _ip_address_del (external_command, + FALSE, + ifindex, + (NMIPAddr *) &address, + plen, + NULL); +} + +const NMPlatformLink * +nmtstp_link_get_typed (int ifindex, + const char *name, + NMLinkType link_type) +{ + const NMPlatformLink *pllink = NULL; + + if (ifindex > 0) { + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + + if (pllink) { + g_assert_cmpint (pllink->ifindex, ==, ifindex); + if (name) + g_assert_cmpstr (name, ==, pllink->name); + } else { + if (name) + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name)); + } + } else { + g_assert (name); + + pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name); + + if (pllink) + g_assert_cmpstr (name, ==, pllink->name); + } + + g_assert (!name || nm_utils_iface_valid_name (name)); + + if (pllink && link_type != NM_LINK_TYPE_NONE) + g_assert_cmpint (pllink->type, ==, link_type); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_get (int ifindex, + const char *name) +{ + return nmtstp_link_get_typed (ifindex, name, NM_LINK_TYPE_NONE); +} + +void +nmtstp_link_del (gboolean external_command, + int ifindex, + const char *name) +{ + gint64 end_time; + const NMPlatformLink *pllink; + gboolean success; + gs_free char *name_copy = NULL; + + pllink = nmtstp_link_get (ifindex, name); + + g_assert (pllink); + + name = name_copy = g_strdup (pllink->name); + ifindex = pllink->ifindex; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + nmtstp_run_command_check ("ip link delete %s", name); + } else { + success = nm_platform_link_delete (NM_PLATFORM_GET, ifindex); + g_assert (success); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + if (!nm_platform_link_get (NM_PLATFORM_GET, ifindex)) { + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name)); + break; + } + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); } +void +nmtstp_link_set_updown (gboolean external_command, + int ifindex, + gboolean up) +{ + const NMPlatformLink *plink; + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + nmtstp_run_command_check ("ip link set %s %s", + ifname, + up ? "up" : "down"); + } else { + if (up) + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + else + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + g_assert (plink); + + if (NM_FLAGS_HAS (plink->flags, IFF_UP) == !!up) + break; + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + +/*****************************************************************************/ + +struct _NMTstpNamespaceHandle { + pid_t pid; + int pipe_fd; +}; + +NMTstpNamespaceHandle * +nmtstp_namespace_create (int unshare_flags, GError **error) +{ + NMTstpNamespaceHandle *ns_handle; + int e; + int errsv; + pid_t pid, pid2; + int pipefd_c2p[2]; + int pipefd_p2c[2]; + ssize_t r; + + e = pipe (pipefd_c2p); + if (e != 0) { + errsv = errno; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "pipe() failed with %d (%s)", errsv, strerror (errsv)); + return FALSE; + } + + e = pipe (pipefd_p2c); + if (e != 0) { + errsv = errno; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "pipe() failed with %d (%s)", errsv, strerror (errsv)); + close (pipefd_c2p[0]); + close (pipefd_c2p[1]); + return FALSE; + } + + pid = fork (); + if (pid < 0) { + errsv = errno; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "fork() failed with %d (%s)", errsv, strerror (errsv)); + close (pipefd_c2p[0]); + close (pipefd_c2p[1]); + close (pipefd_p2c[0]); + close (pipefd_p2c[1]); + return FALSE; + } + + if (pid == 0) { + char read_buf[1]; + + close (pipefd_c2p[0]); /* close read-end */ + close (pipefd_p2c[1]); /* close write-end */ + + if (unshare (unshare_flags) != 0) { + errsv = errno; + if (errsv == 0) + errsv = -1; + } else + errsv = 0; + + /* sync with parent process and send result. */ + do { + r = write (pipefd_c2p[1], &errsv, sizeof (errsv)); + } while (r < 0 && errno == EINTR); + if (r != sizeof (errsv)) { + errsv = errno; + if (errsv == 0) + errsv = -2; + } + close (pipefd_c2p[1]); + + /* wait until parent process terminates (or kills us). */ + if (errsv == 0) { + do { + r = read (pipefd_p2c[0], read_buf, sizeof (read_buf)); + } while (r < 0 && errno == EINTR); + } + close (pipefd_p2c[0]); + _exit (0); + } + + close (pipefd_c2p[1]); /* close write-end */ + close (pipefd_p2c[0]); /* close read-end */ + + /* sync with child process. */ + do { + r = read (pipefd_c2p[0], &errsv, sizeof (errsv)); + } while (r < 0 && errno == EINTR); + + close (pipefd_c2p[0]); + + if ( r != sizeof (errsv) + || errsv != 0) { + int status; + + if (r != sizeof (errsv)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "child process failed for unknown reason"); + } else { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "child process signaled failure %d (%s)", errsv, strerror (errsv)); + } + close (pipefd_p2c[1]); + kill (pid, SIGKILL); + do { + pid2 = waitpid (pid, &status, 0); + } while (pid2 == -1 && errno == EINTR); + return FALSE; + } + + ns_handle = g_new0 (NMTstpNamespaceHandle, 1); + ns_handle->pid = pid; + ns_handle->pipe_fd = pipefd_p2c[1]; + return ns_handle; +} + +pid_t +nmtstp_namespace_handle_get_pid (NMTstpNamespaceHandle *ns_handle) +{ + g_return_val_if_fail (ns_handle, 0); + g_return_val_if_fail (ns_handle->pid > 0, 0); + + return ns_handle->pid; +} + +void +nmtstp_namespace_handle_release (NMTstpNamespaceHandle *ns_handle) +{ + pid_t pid; + int status; + + if (!ns_handle) + return; + + g_return_if_fail (ns_handle->pid > 0); + + close (ns_handle->pipe_fd); + ns_handle->pipe_fd = 0; + + kill (ns_handle->pid, SIGKILL); + + do { + pid = waitpid (ns_handle->pid, &status, 0); + } while (pid == -1 && errno == EINTR); + ns_handle->pid = 0; + + g_free (ns_handle); +} + +int +nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name) +{ + char p[1000]; + + g_return_val_if_fail (pid > 0, 0); + g_return_val_if_fail (ns_name && ns_name[0] && strlen (ns_name) < 50, 0); + + nm_sprintf_buf (p, "/proc/%lu/ns/%s", (long unsigned) pid, ns_name); + + return open(p, O_RDONLY); +} + +/*****************************************************************************/ + NMTST_DEFINE(); static gboolean @@ -318,7 +1487,7 @@ main (int argc, char **argv) init_tests (&argc, &argv); - if ( nmtst_platform_is_root_test () + if ( nmtstp_is_root_test () && (geteuid () != 0 || getegid () != 0)) { if ( g_getenv ("NMTST_FORCE_REAL_ROOT") || !unshare_user ()) { @@ -335,7 +1504,7 @@ main (int argc, char **argv) } } - if (nmtst_platform_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { + if (nmtstp_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { int errsv; if (unshare (CLONE_NEWNET | CLONE_NEWNS) != 0) { @@ -345,10 +1514,7 @@ main (int argc, char **argv) /* 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); - } + mount (NULL, "/sys", "sysfs", MS_SLAVE, NULL); if (mount ("sys", "/sys", "sysfs", MS_RDONLY, NULL) != 0) { errsv = errno; g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); @@ -383,6 +1549,6 @@ main (int argc, char **argv) nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_object_unref (nm_platform_get ()); + 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 3d647543..0281d3c5 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -4,7 +4,7 @@ #include <string.h> #include <arpa/inet.h> -#include "nm-logging.h" +#include "nm-default.h" #include "nm-platform.h" #include "nm-fake-platform.h" #include "nm-linux-platform.h" @@ -13,10 +13,33 @@ #define DEVICE_NAME "nm-test-device" -#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) +/*********************************************************************************************/ + +#define _NMLOG_PREFIX_NAME "platform-test" +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, __VA_ARGS__) + +#define _LOG(level, domain, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + gint64 _ts = nm_utils_get_monotonic_timestamp_ns (); \ + \ + _nm_log (__level, __domain, 0, \ + "%s[%ld.%09ld]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (long) (_ts / NM_UTILS_NS_PER_SECOND), \ + (long) (_ts % NM_UTILS_NS_PER_SECOND) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*********************************************************************************************/ typedef struct { - int handler_id; + gulong handler_id; const char *name; NMPlatformSignalChangeType change_type; gint received_count; @@ -25,8 +48,21 @@ typedef struct { const char *ifname; } SignalData; -gboolean nmtst_platform_is_root_test (void); -gboolean nmtst_platform_is_sysfs_writable (void); +gboolean nmtstp_is_root_test (void); +gboolean nmtstp_is_sysfs_writable (void); + +/******************************************************************************/ + +typedef struct _NMTstpNamespaceHandle NMTstpNamespaceHandle; + +NMTstpNamespaceHandle *nmtstp_namespace_create (int flags, GError **error); + +void nmtstp_namespace_handle_release (NMTstpNamespaceHandle *handle); +pid_t nmtstp_namespace_handle_get_pid (NMTstpNamespaceHandle *handle); + +int nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name); + +/******************************************************************************/ 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) @@ -35,11 +71,13 @@ SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change 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 _accept_or_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 accept_or_wait_signal(data) _accept_or_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) @@ -48,9 +86,88 @@ gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint3 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, NMPObjectType obj_type, 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, SignalData *data); + +int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__printf__, 1, 2))); +#define nmtstp_run_command_check(...) do { g_assert_cmpint (nmtstp_run_command (__VA_ARGS__), ==, 0); } while (0) + +gboolean nmtstp_wait_for_signal (guint timeout_ms); +gboolean nmtstp_wait_for_signal_until (gint64 until_ms); +const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms); + +const NMPlatformLink *nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms); + +int nmtstp_run_command_check_external_global (void); +gboolean nmtstp_run_command_check_external (int external_command); + +gboolean nmtstp_ip_address_check_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred); +void nmtstp_ip_address_assert_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred); +void nmtstp_ip4_address_add (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address, + guint32 lifetime, + guint32 preferred, + const char *label); +void nmtstp_ip6_address_add (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen, + struct in6_addr peer_address, + guint32 lifetime, + guint32 preferred, + guint flags); +void nmtstp_ip4_address_del (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address); +void nmtstp_ip6_address_del (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen); + +const NMPlatformLink *nmtstp_link_get_typed (int ifindex, const char *name, NMLinkType link_type); +const NMPlatformLink *nmtstp_link_get (int ifindex, const char *name); + +void nmtstp_link_set_updown (gboolean external_command, + int ifindex, + gboolean up); + +const NMPlatformLink *nmtstp_link_dummy_add (gboolean external_command, + const char *name); +const NMPlatformLink *nmtstp_link_gre_add (gboolean external_command, + const char *name, + const NMPlatformLnkGre *lnk); +const NMPlatformLink *nmtstp_link_ip6tnl_add (gboolean external_command, + const char *name, + const NMPlatformLnkIp6Tnl *lnk); +const NMPlatformLink *nmtstp_link_ipip_add (gboolean external_command, + const char *name, + const NMPlatformLnkIpIp *lnk); +const NMPlatformLink *nmtstp_link_macvlan_add (gboolean external_command, + const char *name, + int parent, + const NMPlatformLnkMacvlan *lnk); +const NMPlatformLink *nmtstp_link_sit_add (gboolean external_command, + const char *name, + const NMPlatformLnkSit *lnk); +const NMPlatformLink *nmtstp_link_vxlan_add (gboolean external_command, + const char *name, + const NMPlatformLnkVxlan *lnk); -void run_command (const char *format, ...); +void nmtstp_link_del (gboolean external_command, + int ifindex, + const char *name); void init_tests (int *argc, char ***argv); void setup_tests (void); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index 0a397b37..f342e565 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -23,7 +23,7 @@ #include <linux/rtnetlink.h> #include "nm-linux-platform.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-test-utils.h" @@ -65,3 +65,4 @@ main (int argc, char **argv) return g_test_run (); } + diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 47cbcec1..8b496262 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -1,5 +1,9 @@ #include "config.h" +#include <sched.h> + +#include "nmp-object.h" + #include "test-common.h" #include "nm-test-utils.h" @@ -28,10 +32,18 @@ test_bogus(void) 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_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); 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)); @@ -39,15 +51,18 @@ test_bogus(void) g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, &addrlen)); g_assert (!addrlen); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); 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)); + g_assert (!nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + g_assert (!nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); + g_assert (!nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); } static void @@ -68,15 +83,15 @@ software_add (NMLinkType link_type, const char *name) { switch (link_type) { case NM_LINK_TYPE_DUMMY: - return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BRIDGE: - return nm_platform_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BOND: { 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); + plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, name, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) @@ -84,14 +99,14 @@ software_add (NMLinkType link_type, const char *name) return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: - return nm_platform_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_VLAN: { SignalData *parent_added; SignalData *parent_changed; /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); - if (nm_platform_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) + if (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) accept_signal (parent_added); free_signal (parent_added); @@ -108,7 +123,7 @@ software_add (NMLinkType link_type, const char *name) accept_signal (parent_changed); free_signal (parent_changed); - return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } } default: @@ -123,7 +138,6 @@ test_link_changed_signal_cb (NMPlatform *platform, int ifindex, const NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, - NMPlatformReason reason, gboolean *p_test_link_changed_signal_arg) { /* test invocation of platform signals with multiple listeners @@ -146,9 +160,6 @@ test_link_changed_signal_cb (NMPlatform *platform, g_assert_cmpint ((gint64) change_type, !=, (gint64) 0); g_assert_cmpint (change_type, !=, NM_PLATFORM_SIGNAL_NONE); - g_assert_cmpint ((gint64) reason, !=, (gint64) 0); - g_assert_cmpint (reason, !=, NM_PLATFORM_REASON_NONE); - *p_test_link_changed_signal_arg = TRUE; } @@ -262,9 +273,9 @@ test_slave (int master, int type, SignalData *master_changed) /* Set slave option */ switch (type) { case NM_LINK_TYPE_BRIDGE: - if (nmtst_platform_is_sysfs_writable ()) { - g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); - value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); + value = nm_platform_sysctl_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); g_assert_cmpstr (value, ==, "789"); g_free (value); } @@ -296,7 +307,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_added); ensure_no_signal (link_changed); ensure_no_signal (link_removed); - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + nmtstp_link_del (-1, ifindex, NULL); accept_signals (master_changed, 0, 1); accept_signals (link_changed, 0, 1); accept_signal (link_removed); @@ -311,7 +322,7 @@ test_software (NMLinkType link_type, const char *link_typename) { int ifindex; char *value; - int vlan_parent, vlan_id; + int vlan_parent = -1, vlan_id; SignalData *link_added, *link_changed, *link_removed; @@ -327,7 +338,15 @@ test_software (NMLinkType link_type, const char *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 (NM_PLATFORM_GET, ifindex, &vlan_parent, &vlan_id)); + const NMPlatformLink *plink; + const NMPlatformLnkVlan *plnk; + + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink); + g_assert (plnk); + g_assert (plink); + + vlan_parent = plink->parent; + vlan_id = plnk->id; g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); g_assert_cmpint (vlan_id, ==, VLAN_ID); } @@ -347,17 +366,17 @@ test_software (NMLinkType link_type, const char *link_typename) /* Set master option */ switch (link_type) { case NM_LINK_TYPE_BRIDGE: - if (nmtst_platform_is_sysfs_writable ()) { - g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); - value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); + value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); g_assert_cmpstr (value, ==, "789"); g_free (value); } break; case NM_LINK_TYPE_BOND: - if (nmtst_platform_is_sysfs_writable ()) { - g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); - value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); + value = nm_platform_sysctl_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); @@ -382,20 +401,18 @@ test_software (NMLinkType link_type, const char *link_typename) free_signal (link_changed); /* Delete */ - 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)); + nmtstp_link_del (-1, ifindex, DEVICE_NAME); accept_signal (link_removed); /* Delete again */ g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); /* 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 (NM_PLATFORM_GET, vlan_parent)); + nmtstp_link_del (-1, vlan_parent, NULL); accept_signal (link_removed_parent); free_signal (link_removed_parent); } @@ -414,7 +431,7 @@ test_bridge (void) static void test_bond (void) { - if (nmtst_platform_is_root_test () && + if (nmtstp_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"); @@ -436,6 +453,53 @@ test_vlan (void) test_software (NM_LINK_TYPE_VLAN, "vlan"); } +/*****************************************************************************/ + +static void +test_bridge_addr (void) +{ + char addr[ETH_ALEN]; + NMPlatformLink link; + const NMPlatformLink *plink = NULL; + + nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr)); + + g_assert_cmpint (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink), ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (plink); + link = *plink; + g_assert_cmpstr (link.name, ==, DEVICE_NAME); + + g_assert_cmpint (link.addr.len, ==, sizeof (addr)); + g_assert (!memcmp (link.addr.data, addr, sizeof (addr))); + + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + + if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { + g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); + + g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE)); + g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); + + g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE)); + g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); + } + + g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); + g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); + + nmtstp_link_del (-1, link.ifindex, link.name); +} + +/*****************************************************************************/ + static void test_internal (void) { @@ -451,11 +515,11 @@ test_internal (void) g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); /* Add device */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); /* Try to add again */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); /* Check device index, name and type */ ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -506,7 +570,7 @@ test_internal (void) accept_signal (link_changed); /* Delete device */ - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + nmtstp_link_del (-1, ifindex, DEVICE_NAME); accept_signal (link_removed); /* Try to delete again */ @@ -517,15 +581,18 @@ test_internal (void) free_signal (link_removed); } +/*****************************************************************************/ + static void test_external (void) { 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; + SignalData *link_added, *link_changed, *link_removed; int ifindex; - run_command ("ip link add %s type %s", DEVICE_NAME, "dummy"); + link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); + + nmtstp_run_command_check ("ip link add %s type %s", DEVICE_NAME, "dummy"); wait_signal (link_added); g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); @@ -549,24 +616,24 @@ test_external (void) 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); + nmtstp_run_command_check ("ip link set %s up", DEVICE_NAME); wait_signal (link_changed); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s down", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s down", DEVICE_NAME); wait_signal (link_changed); g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s arp on", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s arp on", DEVICE_NAME); wait_signal (link_changed); g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s arp off", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s arp off", DEVICE_NAME); wait_signal (link_changed); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); - run_command ("ip link del %s", DEVICE_NAME); + nmtstp_run_command_check ("ip link del %s", DEVICE_NAME); wait_signal (link_removed); accept_signals (link_changed, 0, 1); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); @@ -576,6 +643,1183 @@ test_external (void) free_signal (link_removed); } +/*****************************************************************************/ + +typedef struct { + NMLinkType link_type; + int test_mode; + gboolean external_command; +} TestAddSoftwareDetectData; + +static void +test_software_detect (gconstpointer user_data) +{ + const TestAddSoftwareDetectData *test_data = user_data; + int ifindex, ifindex_parent; + const NMPlatformLink *plink; + const NMPObject *lnk; + guint i_step; + const gboolean ext = test_data->external_command; + + nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); + ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + switch (test_data->link_type) { + case NM_LINK_TYPE_GRE: { + NMPlatformLnkGre lnk_gre = { }; + gboolean gracefully_skip = FALSE; + + lnk_gre.local = nmtst_inet4_from_string ("192.168.233.204"); + lnk_gre.remote = nmtst_inet4_from_string ("172.168.10.25"); + lnk_gre.parent_ifindex = ifindex_parent; + lnk_gre.ttl = 174; + lnk_gre.tos = 37; + lnk_gre.path_mtu_discovery = TRUE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "gre0")) { + /* Seems that the ip_gre module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip_gre", NULL) != 0; + } + + if (!nmtstp_link_gre_add (ext, DEVICE_NAME, &lnk_gre)) { + if (gracefully_skip) { + g_test_skip ("Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)"); + goto out_delete_parent; + } + g_error ("Failed adding GRE tunnel"); + } + break; + } + case NM_LINK_TYPE_IPIP: { + NMPlatformLnkIpIp lnk_ipip = { }; + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "tunl0")) { + /* Seems that the ipip module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ipip", NULL) != 0; + } + + lnk_ipip.local = nmtst_inet4_from_string ("1.2.3.4"); + lnk_ipip.remote = nmtst_inet4_from_string ("5.6.7.8"); + lnk_ipip.parent_ifindex = ifindex_parent; + lnk_ipip.tos = 32; + lnk_ipip.path_mtu_discovery = FALSE; + + if (!nmtstp_link_ipip_add (ext, DEVICE_NAME, &lnk_ipip)) { + if (gracefully_skip) { + g_test_skip ("Cannot create ipip tunnel because of missing ipip module (modprobe ipip)"); + goto out_delete_parent; + } + g_error ("Failed adding IPIP tunnel"); + } + break; + } + case NM_LINK_TYPE_IP6TNL: { + NMPlatformLnkIp6Tnl lnk_ip6tnl = { }; + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6tnl0")) { + /* Seems that the ip6_tunnel module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_tunnel", NULL) != 0; + } + + lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15"); + lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16"); + lnk_ip6tnl.parent_ifindex = ifindex_parent; + lnk_ip6tnl.tclass = 20; + lnk_ip6tnl.encap_limit = 6; + lnk_ip6tnl.flow_label = 1337; + lnk_ip6tnl.proto = IPPROTO_IPV6; + + if (!nmtstp_link_ip6tnl_add (ext, DEVICE_NAME, &lnk_ip6tnl)) { + if (gracefully_skip) { + g_test_skip ("Cannot create ip6tnl tunnel because of missing ip6_tunnel module (modprobe ip6_tunnel)"); + goto out_delete_parent; + } + g_error ("Failed adding IP6TNL tunnel"); + } + break; + } + case NM_LINK_TYPE_MACVLAN: { + NMPlatformLnkMacvlan lnk_macvlan = { }; + + lnk_macvlan.mode = MACVLAN_MODE_BRIDGE; + lnk_macvlan.no_promisc = FALSE; + lnk_macvlan.tap = FALSE; + + if (!nmtstp_link_macvlan_add (ext, DEVICE_NAME, ifindex_parent, &lnk_macvlan)) + g_error ("Failed adding MACVLAN interface"); + break; + } + case NM_LINK_TYPE_MACVTAP: { + NMPlatformLnkMacvtap lnk_macvtap = { }; + + lnk_macvtap.mode = MACVLAN_MODE_PRIVATE; + lnk_macvtap.no_promisc = FALSE; + lnk_macvtap.tap = TRUE; + + if (!nmtstp_link_macvlan_add (ext, DEVICE_NAME, ifindex_parent, &lnk_macvtap)) + g_error ("Failed adding MACVTAP interface"); + break; + } + case NM_LINK_TYPE_SIT: { + NMPlatformLnkSit lnk_sit = { }; + gboolean gracefully_skip = FALSE; + + lnk_sit.local = nmtst_inet4_from_string ("192.168.200.1"); + lnk_sit.remote = nmtst_inet4_from_string ("172.25.100.14"); + lnk_sit.parent_ifindex = ifindex_parent; + lnk_sit.ttl = 0; + lnk_sit.tos = 31; + lnk_sit.path_mtu_discovery = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "sit0")) { + /* Seems that the sit module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "sit", NULL) != 0; + } + + if (!nmtstp_link_sit_add (ext, DEVICE_NAME, &lnk_sit)) { + if (gracefully_skip) { + g_test_skip ("Cannot create sit tunnel because of missing sit module (modprobe sit)"); + goto out_delete_parent; + } + g_error ("Failed adding SIT tunnel"); + } + break; + } + case NM_LINK_TYPE_VLAN: + nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1242", DEVICE_NAME, PARENT_NAME); + break; + case NM_LINK_TYPE_VXLAN: { + NMPlatformLnkVxlan lnk_vxlan = { }; + + switch (test_data->test_mode) { + case 0: + lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + lnk_vxlan.id = 42; + lnk_vxlan.local = nmtst_inet4_from_string ("23.1.2.164"); + lnk_vxlan.group = nmtst_inet4_from_string ("239.1.2.134"); + lnk_vxlan.dst_port = 4789; + lnk_vxlan.learning = TRUE; + lnk_vxlan.ageing = 1245; + break; + case 1: + lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + lnk_vxlan.id = 11214423; + lnk_vxlan.local6 = *nmtst_inet6_from_string ("1:2:3:4:334:23::23"); + lnk_vxlan.group6 = *nmtst_inet6_from_string ("ff0e::115"); + lnk_vxlan.ttl = 32; + lnk_vxlan.dst_port = 57412; + lnk_vxlan.src_port_min = 1000; + lnk_vxlan.src_port_max = 1003; + lnk_vxlan.learning = TRUE; + lnk_vxlan.ageing = 3245; + break; + } + + g_assert (nmtstp_link_vxlan_add (ext, DEVICE_NAME, &lnk_vxlan)); + break; + } + default: + g_assert_not_reached (); + } + + ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, test_data->link_type, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_parent, TRUE); + + for (i_step = 0; i_step < 5; i_step++) { + + _LOGD ("test-software-detect: step %u", i_step); + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip -d link show %s", DEVICE_NAME); + + if (i_step > 0) { + gboolean set_up = (i_step % 2) == 1; + + if ( test_data->link_type == NM_LINK_TYPE_VXLAN + && set_up) { + /* On RHEL-7, we need to add a tiny sleep here, otherwise, + * upping the vxlan device fails with EADDRINUSE. + * https://bugzilla.redhat.com/show_bug.cgi?id=1277131 */ + g_usleep (1); + } + nmtstp_link_set_updown (-1, ifindex, set_up); + } + + lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, test_data->link_type, &plink); + g_assert (plink); + g_assert_cmpint (plink->ifindex, ==, ifindex); + g_assert (lnk); + + switch (test_data->link_type) { + case NM_LINK_TYPE_GRE: { + const NMPlatformLnkGre *plnk = &lnk->lnk_gre; + + g_assert (plnk == nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + g_assert_cmpint (plnk->input_flags, ==, 0); + g_assert_cmpint (plnk->output_flags, ==, 0); + g_assert_cmpint (plnk->input_key, ==, 0); + g_assert_cmpint (plnk->output_key, ==, 0); + nmtst_assert_ip4_address (plnk->local, "192.168.233.204"); + nmtst_assert_ip4_address (plnk->remote, "172.168.10.25"); + g_assert_cmpint (plnk->ttl, ==, 174); + g_assert_cmpint (plnk->tos, ==, 37); + g_assert_cmpint (plnk->path_mtu_discovery, ==, TRUE); + break; + } + case NM_LINK_TYPE_IP6TNL: { + const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; + + g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip6_address (&plnk->local, "fd01::15"); + nmtst_assert_ip6_address (&plnk->remote, "fd01::16"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tclass, ==, 20); + g_assert_cmpint (plnk->encap_limit, ==, 6); + g_assert_cmpint (plnk->flow_label, ==, 1337); + g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6); + break; + } + case NM_LINK_TYPE_IPIP: { + const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip; + + g_assert (plnk == nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip4_address (plnk->local, "1.2.3.4"); + nmtst_assert_ip4_address (plnk->remote, "5.6.7.8"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tos, ==, 32); + g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE); + break; + } + case NM_LINK_TYPE_MACVLAN: { + const NMPlatformLnkMacvlan *plnk = &lnk->lnk_macvlan; + + g_assert (plnk == nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->no_promisc, ==, FALSE); + g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_BRIDGE); + break; + } + case NM_LINK_TYPE_MACVTAP: { + const NMPlatformLnkMacvtap *plnk = &lnk->lnk_macvlan; + + g_assert (plnk == nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->no_promisc, ==, FALSE); + g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_PRIVATE); + break; + } + case NM_LINK_TYPE_SIT: { + const NMPlatformLnkSit *plnk = &lnk->lnk_sit; + + g_assert (plnk == nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip4_address (plnk->local, "192.168.200.1"); + nmtst_assert_ip4_address (plnk->remote, "172.25.100.14"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tos, ==, 31); + g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE); + break; + } + case NM_LINK_TYPE_VLAN: { + const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; + + g_assert (plnk == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->id, ==, 1242); + break; + } + case NM_LINK_TYPE_VXLAN: { + const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan; + + g_assert (plnk == nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, !=, 0); + g_assert_cmpint (plnk->tos, ==, 0); + g_assert_cmpint (plnk->learning, ==, TRUE); + g_assert_cmpint (plnk->limit, ==, 0); + g_assert_cmpint (plnk->proxy, ==, FALSE); + g_assert_cmpint (plnk->rsc, ==, FALSE); + g_assert_cmpint (plnk->l2miss, ==, FALSE); + g_assert_cmpint (plnk->l3miss, ==, FALSE); + + switch (test_data->test_mode) { + case 0: + g_assert_cmpint (plnk->id, ==, 42); + nmtst_assert_ip4_address (plnk->local, "23.1.2.164"); + nmtst_assert_ip4_address (plnk->group, "239.1.2.134"); + nmtst_assert_ip6_address (&plnk->group6, "::"); + nmtst_assert_ip6_address (&plnk->local6, "::"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->ageing, ==, 1245); + g_assert_cmpint (plnk->dst_port, ==, 4789); + if ( plnk->src_port_min != 0 + || plnk->src_port_max != 0) { + /* on some kernels, omiting the port range results in setting + * following default port range. */ + g_assert_cmpint (plnk->src_port_min, ==, 32768); + g_assert_cmpint (plnk->src_port_max, ==, 61000); + } + break; + case 1: + g_assert_cmpint (plnk->id, ==, 11214423); + nmtst_assert_ip4_address (plnk->local, "0.0.0.0"); + nmtst_assert_ip4_address (plnk->group, "0.0.0.0"); + nmtst_assert_ip6_address (&plnk->group6, "ff0e::115"); + nmtst_assert_ip6_address (&plnk->local6, "1:2:3:4:334:23::23"); + g_assert_cmpint (plnk->ageing, ==, 3245); + g_assert_cmpint (plnk->dst_port, ==, 57412); + g_assert_cmpint (plnk->ttl, ==, 32); + g_assert_cmpint (plnk->src_port_min, ==, 1000); + g_assert_cmpint (plnk->src_port_max, ==, 1003); + break; + } + break; + } + default: + g_assert_not_reached (); + } + } + + nmtstp_link_del (-1, ifindex, DEVICE_NAME); +out_delete_parent: + nmtstp_link_del (-1, ifindex_parent, PARENT_NAME); +} + +static void +test_software_detect_add (const char *testpath, + NMLinkType link_type, + int test_mode) +{ + TestAddSoftwareDetectData *test_data; + char *path; + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + test_data->external_command = TRUE; + + path = g_strdup_printf ("%s/external", testpath); + g_test_add_data_func_full (path, test_data, test_software_detect, g_free); + g_free (path); + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + test_data->external_command = FALSE; + + path = g_strdup_printf ("%s/platform", testpath); + g_test_add_data_func_full (path, test_data, test_software_detect, g_free); + g_free (path); + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + test_data->external_command = -1; + + path = g_strdup_printf ("%s/random", testpath); + g_test_add_data_func_full (path, test_data, test_software_detect, g_free); + g_free (path); +} + +/*****************************************************************************/ + +static void +_assert_xgress_qos_mappings_impl (int ifindex, + gboolean is_ingress_map , + int n_entries, + int n, + ...) +{ + const NMPlatformLink *plink; + const NMPObject *lnk; + guint n_map; + const NMVlanQosMapping *map; + va_list ap; + guint i; + + lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink); + + g_assert (plink); + g_assert_cmpint (plink->ifindex, ==, ifindex); + g_assert (lnk); + g_assert (&lnk->lnk_vlan == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); + + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip -d link show %s", plink->name); + + if (is_ingress_map) { + map = lnk->_lnk_vlan.ingress_qos_map; + n_map = lnk->_lnk_vlan.n_ingress_qos_map; + } else { + map = lnk->_lnk_vlan.egress_qos_map; + n_map = lnk->_lnk_vlan.n_egress_qos_map; + } + + if (n_entries != -1) + g_assert_cmpint (n_map, ==, n_entries); + + for (i = 0; i < n_map; i++) { + if (is_ingress_map) { + g_assert_cmpint (map[i].from, >=, 0); + g_assert_cmpint (map[i].from, <=, 7); + } + if (i > 0) + g_assert_cmpint (map[i - 1].from, <, map[i].from); + } + + va_start (ap, n); + for (; n > 0; n--) { + gboolean found = FALSE; + guint from = va_arg (ap, guint); + guint to = va_arg (ap, guint); + + for (i = 0; i < n_map; i++) { + if (map[i].from == from) { + g_assert (!found); + found = TRUE; + + g_assert (map[i].to == to); + } + } + g_assert (found); + } + va_end (ap); +} +#define _assert_xgress_qos_mappings(ifindex, is_ingress_map, n_entries, ...) \ + _assert_xgress_qos_mappings_impl ((ifindex), (is_ingress_map), (n_entries), \ + (G_STATIC_ASSERT_EXPR ((NM_NARG (__VA_ARGS__) % 2) == 0), NM_NARG (__VA_ARGS__) / 2), \ + __VA_ARGS__) +#define _assert_ingress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, TRUE, n_entries, __VA_ARGS__) +#define _assert_egress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, FALSE, n_entries, __VA_ARGS__) + +static void +_assert_vlan_flags (int ifindex, NMVlanFlags flags) +{ + const NMPlatformLnkVlan *plnk; + + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL); + g_assert (plnk); + g_assert_cmpint (plnk->flags, ==, flags); +} + +static void +test_vlan_set_xgress (void) +{ + int ifindex, ifindex_parent; + + nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); + ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1245", DEVICE_NAME, PARENT_NAME); + ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex; + + /* ingress-qos-map */ + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 4, 5)); + _assert_ingress_qos_mappings (ifindex, 1, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 7)); + _assert_ingress_qos_mappings (ifindex, 2, + 3, 7, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 8)); + _assert_ingress_qos_mappings (ifindex, 2, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 4)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 4, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, G_MAXUINT32, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32 - 1)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, G_MAXUINT32 - 1, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + /* Set invalid values: */ + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 8, 3)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 9, 4)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + /* egress-qos-map */ + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 7, 3)); + _assert_egress_qos_mappings (ifindex, 1, + 7, 3); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 4)); + _assert_egress_qos_mappings (ifindex, 2, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 4)); + _assert_egress_qos_mappings (ifindex, 3, + 0, 4, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 4)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 4, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 5)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 5, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 9, 5)); + _assert_egress_qos_mappings (ifindex, 5, + 0, 4, + 1, 5, + 7, 3, + 8, 4, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 5)); + _assert_egress_qos_mappings (ifindex, 5, + 0, 4, + 1, 5, + 7, 3, + 8, 5, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 0)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 0)); + _assert_egress_qos_mappings (ifindex, 3, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 4)); + _assert_egress_qos_mappings (ifindex, 4, + 1, 5, + 7, 3, + 9, 5, + 100, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 4)); + _assert_egress_qos_mappings (ifindex, 5, + 1, 5, + 7, 3, + 9, 5, + 100, 4, + G_MAXUINT32, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 8)); + _assert_egress_qos_mappings (ifindex, 5, + 1, 5, + 7, 3, + 9, 5, + 100, 4, + G_MAXUINT32, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 0)); + _assert_egress_qos_mappings (ifindex, 4, + 1, 5, + 7, 3, + 9, 5, + 100, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 0)); + _assert_egress_qos_mappings (ifindex, 3, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 0)); + _assert_egress_qos_mappings (ifindex, 2, + 7, 3, + 9, 5); + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 5 }, + { .from = 7, .to = 1655 }, + { .from = 7, .to = 17655 }, + { .from = 5, .to = 754 }, + { .from = 4, .to = 12 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 4, + 3, 5, + 4, 12, + 7, 17655, + 5, 754); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 18 }, + { .from = 6, .to = 121 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 5, + 3, 18, + 4, 12, + 6, 121, + 7, 17655, + 5, 754); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 0 }, + { .from = 6, .to = 7 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 6, 7); + } + + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 5, .to = 1 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 5, 1); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 5, .to = 3 }, + { .from = 1655, .to = 5 }, + { .from = 1655, .to = 7 }, + { .from = G_MAXUINT32, .to = 6 }, + { .from = G_MAXUINT32, .to = 8 }, + { .from = 754, .to = 4 }, + { .from = 3, .to = 2 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 5, + 3, 2, + 5, 3, + 754, 4, + 1655, 7, + G_MAXUINT32, 6); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 754, .to = 3 }, + { .from = 755, .to = 8 }, + { .from = 1655, .to = 0 }, + { .from = 6, .to = 1 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + FALSE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 5, + 3, 2, + 5, 3, + 6, 1, + 754, 3, + G_MAXUINT32, 6); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 6, .to = 0 }, + { .from = 3, .to = 4 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 3, 4); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 6, .to = 145 }, + { .from = 4, .to = 1 }, + { .from = 6, .to = 12 }, + }; + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 5 }, + { .from = 3232, .to = 7 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP, + NM_VLAN_FLAG_REORDER_HEADERS, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_ingress_qos_mappings (ifindex, 2, + 4, 1, + 6, 12); + _assert_egress_qos_mappings (ifindex, 2, + 1, 5, + 3232, 7); + _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 6, .to = 145 }, + { .from = 4, .to = 1 }, + { .from = 6, .to = 12 }, + }; + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 7 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 3232, .to = 0 }, + { .from = 64, .to = 4 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + NM_VLAN_FLAG_GVRP, + NM_VLAN_FLAG_GVRP, + FALSE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_ingress_qos_mappings (ifindex, 2, + 4, 1, + 6, 12); + _assert_egress_qos_mappings (ifindex, 2, + 1, 7, + 64, 4); + _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP); + } + + nmtstp_link_del (-1, ifindex, DEVICE_NAME); + nmtstp_link_del (-1, ifindex_parent, PARENT_NAME); +} + +/*****************************************************************************/ + +static void +test_create_many_links_do (guint n_devices) +{ + gint64 time, start_time = nm_utils_get_monotonic_timestamp_ns (); + guint i; + char name[64]; + const NMPlatformLink *pllink; + gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices); + const gint EX = ((int) (nmtst_get_rand_int () % 4)) - 1; + + g_assert (EX >= -1 && EX <= 2); + + _LOGI (">>> create devices (EX=%d)...", EX); + + for (i = 0; i < n_devices; i++) { + nm_sprintf_buf (name, "t-%05u", i); + if (EX == 2) { + /* This mode is different from letting nmtstp_link_dummy_add() + * because in this case we don't process any platform events + * while adding all the links. */ + nmtstp_run_command_check ("ip link add %s type dummy", name); + } else + nmtstp_link_dummy_add (EX, name); + } + + _LOGI (">>> process events after creating devices..."); + + nm_platform_process_events (NM_PLATFORM_GET); + + _LOGI (">>> check devices..."); + + for (i = 0; i < n_devices; i++) { + nm_sprintf_buf (name, "t-%05u", i); + + pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name); + g_assert (pllink); + g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_DUMMY); + g_assert_cmpstr (pllink->name, ==, name); + + g_array_append_val (ifindexes, pllink->ifindex); + } + + _LOGI (">>> delete devices..."); + + g_assert_cmpint (ifindexes->len, ==, n_devices); + for (i = 0; i < n_devices; i++) { + nm_sprintf_buf (name, "t-%05u", i); + + if (EX == 2) + nmtstp_run_command_check ("ip link delete %s", name); + else + nmtstp_link_del (EX, g_array_index (ifindexes, int, i), name); + } + + _LOGI (">>> process events after deleting devices..."); + nm_platform_process_events (NM_PLATFORM_GET); + + time = nm_utils_get_monotonic_timestamp_ns () - start_time; + _LOGI (">>> finished in %ld.%09ld seconds", (long) (time / NM_UTILS_NS_PER_SECOND), (long) (time % NM_UTILS_NS_PER_SECOND)); +} + +static void +test_create_many_links (gconstpointer user_data) +{ + guint n_devices = GPOINTER_TO_UINT (user_data); + + if (n_devices > 100 && nmtst_test_quick ()) { + g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", str_if_set (g_get_prgname (), "test-link-linux")); + g_test_skip ("Skip long running test"); + return; + } + + test_create_many_links_do (n_devices); +} + +/*****************************************************************************/ + +static void +test_nl_bugs_veth (void) +{ + const char *IFACE_VETH0 = "nm-test-veth0"; + const char *IFACE_VETH1 = "nm-test-veth1"; + int ifindex_veth0, ifindex_veth1; + int i; + const NMPlatformLink *pllink_veth0, *pllink_veth1; + gs_free_error GError *error = NULL; + NMTstpNamespaceHandle *ns_handle = NULL; + + /* create veth pair. */ + nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1); + ifindex_veth0 = nmtstp_assert_wait_for_link (IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex; + ifindex_veth1 = nmtstp_assert_wait_for_link (IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex; + + /* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */ + g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i)); + g_assert_cmpint (i, ==, ifindex_veth1); + + g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth1, &i)); + g_assert_cmpint (i, ==, ifindex_veth0); + + /* assert that NMPlatformLink.parent is the peer-ifindex. */ + pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); + g_assert (pllink_veth0); + if (pllink_veth0->parent == 0) { + /* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder + * of the test. */ + goto out; + } + g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1); + + + /* The following tests whether we have a workaround for kernel bug + * https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */ + pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1); + g_assert (pllink_veth1); + g_assert_cmpint (pllink_veth1->parent, ==, ifindex_veth0); + + + /* move one veth peer to another namespace and check that the + * parent/IFLA_LINK of the remaining peer properly updates + * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ + ns_handle = nmtstp_namespace_create (CLONE_NEWNET, &error); + g_assert_no_error (error); + g_assert (ns_handle); + + nmtstp_run_command_check ("ip link set %s netns %ld", IFACE_VETH1, (long) nmtstp_namespace_handle_get_pid (ns_handle)); + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (50); + nm_platform_process_events (NM_PLATFORM_GET); + + pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1); + pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); + if ( !pllink_veth1 + && pllink_veth0 + && pllink_veth0->parent == NM_PLATFORM_LINK_OTHER_NETNS) { + break; + } + }); + +out: + nmtstp_link_del (-1, ifindex_veth0, IFACE_VETH0); + g_assert (!nmtstp_link_get (ifindex_veth0, IFACE_VETH0)); + g_assert (!nmtstp_link_get (ifindex_veth1, IFACE_VETH1)); + nmtstp_namespace_handle_release (ns_handle); +} + +/*****************************************************************************/ + +static void +test_nl_bugs_spuroius_newlink (void) +{ + const char *IFACE_BOND0 = "nm-test-bond0"; + const char *IFACE_DUMMY0 = "nm-test-dummy0"; + int ifindex_bond0, ifindex_dummy0; + const NMPlatformLink *pllink; + gboolean wait_for_settle; + + /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ + + nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0); + ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add %s type bond", IFACE_BOND0); + ifindex_bond0 = nmtstp_assert_wait_for_link (IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_bond0, TRUE); + + nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BOND0); + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (50); + + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); + g_assert (pllink); + if (pllink->master == ifindex_bond0) + break; + }); + + nmtstp_run_command_check ("ip link del %s", IFACE_BOND0); + + wait_for_settle = TRUE; + nmtstp_wait_for_signal (50); +again: + nm_platform_process_events (NM_PLATFORM_GET); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bond0); + g_assert (!pllink); + + if (wait_for_settle) { + wait_for_settle = FALSE; + NMTST_WAIT (300, { nmtstp_wait_for_signal (50); }); + goto again; + } + + g_assert (!nmtstp_link_get (ifindex_bond0, IFACE_BOND0)); + nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0); +} + +/*****************************************************************************/ + +static void +test_nl_bugs_spuroius_dellink (void) +{ + const char *IFACE_BRIDGE0 = "nm-test-bridge0"; + const char *IFACE_DUMMY0 = "nm-test-dummy0"; + int ifindex_bridge0, ifindex_dummy0; + const NMPlatformLink *pllink; + gboolean wait_for_settle; + + /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ + + nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0); + ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add %s type bridge", IFACE_BRIDGE0); + ifindex_bridge0 = nmtstp_assert_wait_for_link (IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_bridge0, TRUE); + + nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BRIDGE0); + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (50); + + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); + g_assert (pllink); + if (pllink->master == ifindex_bridge0) + break; + }); + + nm_platform_process_events (NM_PLATFORM_GET); + + nmtstp_run_command_check ("ip link set %s nomaster", IFACE_DUMMY0); + + wait_for_settle = TRUE; + nmtstp_wait_for_signal (50); +again: + nm_platform_process_events (NM_PLATFORM_GET); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bridge0); + g_assert (pllink); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); + g_assert (pllink); + g_assert_cmpint (pllink->parent, ==, 0); + + if (wait_for_settle) { + wait_for_settle = FALSE; + NMTST_WAIT (300, { nmtstp_wait_for_signal (50); }); + goto again; + } + + nmtstp_link_del (-1, ifindex_bridge0, IFACE_BRIDGE0); + nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0); +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { @@ -599,7 +1843,28 @@ setup_tests (void) g_test_add_func ("/link/software/bond", test_bond); g_test_add_func ("/link/software/team", test_team); g_test_add_func ("/link/software/vlan", test_vlan); + g_test_add_func ("/link/software/bridge/addr", test_bridge_addr); - if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) + if (nmtstp_is_root_test ()) { g_test_add_func ("/link/external", test_external); + + test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); + test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0); + test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0); + test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0); + test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0); + test_software_detect_add ("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0); + test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0); + test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); + test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); + + g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress); + + g_test_add_data_func ("/link/create-many-links/20", GUINT_TO_POINTER (20), test_create_many_links); + g_test_add_data_func ("/link/create-many-links/1000", GUINT_TO_POINTER (1000), test_create_many_links); + + g_test_add_func ("/link/nl-bugs/veth", test_nl_bugs_veth); + g_test_add_func ("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink); + g_test_add_func ("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink); + } } diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 88bb0707..eba2383b 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -20,7 +20,7 @@ #include "nmp-object.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-test-utils.h" @@ -148,8 +148,8 @@ _nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, 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; + nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); + nm_auto_nmpobj NMPObject *new_clone = NULL; const NMPObject *obj_old; _NMPCacheUpdateData data = { .cache = cache, diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index cf322dec..60a0c15c 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -9,7 +9,7 @@ #define DEVICE_NAME "nm-test-device" static void -ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const 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, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -25,11 +25,11 @@ ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, c g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const 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, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -45,7 +45,7 @@ ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, c g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } static void @@ -282,6 +282,31 @@ test_ip6_route (void) free_signal (route_removed); } +/*****************************************************************************/ + +static void +test_ip4_zero_gateway (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + + nmtstp_run_command_check ("ip route add 1.2.3.1/32 via 0.0.0.0 dev %s", DEVICE_NAME); + nmtstp_run_command_check ("ip route add 1.2.3.2/32 dev %s", DEVICE_NAME); + + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (10); + if ( nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0) + && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0)) + break; + }); + + nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); + + nmtstp_wait_for_signal (50); + nm_platform_process_events (NM_PLATFORM_GET); +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { @@ -295,7 +320,7 @@ setup_tests (void) nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); @@ -304,4 +329,7 @@ setup_tests (void) g_test_add_func ("/route/ip4", test_ip4_route); g_test_add_func ("/route/ip6", test_ip6_route); g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0); + + if (nmtstp_is_root_test ()) + g_test_add_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); } diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index b31ffa1c..e05104f3 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -28,21 +28,208 @@ #include <net/ethernet.h> #include <unistd.h> #include <math.h> - -#include <glib.h> - -#include <netlink/genl/genl.h> -#include <netlink/genl/family.h> -#include <netlink/genl/ctrl.h> - +#include <netlink/netlink.h> +#include <netlink/msg.h> #include <linux/nl80211.h> +#include "nm-default.h" #include "wifi-utils-private.h" #include "wifi-utils-nl80211.h" #include "nm-platform.h" -#include "nm-logging.h" #include "nm-utils.h" + +/***************************************************************************** + * Copied from libnl3/genl: + *****************************************************************************/ + +static void * +genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr)); + + return (char *) nlmsg_data (nlh) + GENL_HDRLEN; +} + +static void * +genlmsg_data (const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +static void * +genlmsg_user_hdr (const struct genlmsghdr *gnlh) +{ + return genlmsg_data (gnlh); +} + +static struct genlmsghdr * +genlmsg_hdr (struct nlmsghdr *nlh) +{ + return nlmsg_data (nlh); +} + +static void * +genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen) +{ + return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen); +} + +static struct nlattr * +genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data (gnlh, hdrlen); +} + +static int +genlmsg_len (const struct genlmsghdr *gnlh) +{ + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + +static int +genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen); +} + +static int +genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen) +{ + struct genlmsghdr *ghdr; + + if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN)) + return 0; + + ghdr = nlmsg_data (nlh); + if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen)) + return 0; + + return 1; +} + +static int +genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr (nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data (nlh); + return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen), + genlmsg_attrlen (ghdr, hdrlen), policy); +} + +/***************************************************************************** + * Reimplementation of libnl3/genl functions: + *****************************************************************************/ + +static int +probe_response (struct nl_msg *msg, void *arg) +{ + static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, + .maxlen = GENL_NAMSIZ }, + [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, + [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, + [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, + [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, + [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, + }; + struct nlattr *tb[CTRL_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr (msg); + gint32 *response_data = arg; + + if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + *response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]); + + return NL_STOP; +} + +static int +genl_ctrl_resolve (struct nl_sock *sk, const char *name) +{ + struct nl_msg *msg; + struct nl_cb *cb, *orig; + int rc; + int result = -NLE_OBJ_NOTFOUND; + gint32 response_data = -1; + + if (!(orig = nl_socket_get_cb (sk))) + goto out; + + cb = nl_cb_clone (orig); + nl_cb_put (orig); + if (!cb) + goto out; + + msg = nlmsg_alloc (); + if (!msg) + goto out_cb_free; + + if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1)) + goto out_msg_free; + + if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0) + goto out_msg_free; + + rc = nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, &response_data); + if (rc < 0) + goto out_msg_free; + + rc = nl_send_auto_complete (sk, msg); + if (rc < 0) + goto out_msg_free; + + rc = nl_recvmsgs (sk, cb); + if (rc < 0) + goto out_msg_free; + + /* If search was successful, request may be ACKed after data */ + rc = nl_wait_for_ack (sk); + if (rc < 0) + goto out_msg_free; + + if (response_data > 0) + result = response_data; + +out_msg_free: + nlmsg_free (msg); +out_cb_free: + nl_cb_put (cb); +out: + if (result >= 0) + nm_log_dbg (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result); + else + nm_log_err (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name); + return result; +} + +/***************************************************************************** + * </libn-genl-3> + *****************************************************************************/ + typedef struct { WifiData parent; struct nl_sock *nl_sock; @@ -144,7 +331,7 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, * not warn on DUMP_INTR error for get scan command. */ if (err == -NLE_DUMP_INTR && - genlmsg_hdr(nlmsg_hdr(msg))->cmd == NL80211_CMD_GET_SCAN) + genlmsg_hdr (nlmsg_hdr (msg))->cmd == NL80211_CMD_GET_SCAN) break; nm_log_warn (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s", @@ -265,6 +452,24 @@ wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode) return FALSE; } +static gboolean +wifi_nl80211_set_powersave (WifiData *data, guint32 powersave) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + struct nl_msg *msg; + int err; + + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_POWER_SAVE, 0); + NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE, + powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); + err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); + return err ? FALSE : TRUE; + +nla_put_failure: + nlmsg_free (msg); + return FALSE; +} + /* @divisor: pass what value @xbm should be divided by to get dBm */ static guint32 nl80211_xbm_to_percent (gint32 xbm, guint32 divisor) @@ -273,7 +478,7 @@ nl80211_xbm_to_percent (gint32 xbm, guint32 divisor) #define SIGNAL_MAX_DBM -20 xbm /= divisor; - xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); + xbm = CLAMP (xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); return 100 - 70 * (((float) SIGNAL_MAX_DBM - (float) xbm) / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM)); @@ -353,7 +558,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) if (bss[NL80211_BSS_BSSID] == NULL) return NL_SKIP; - memcpy(info->bssid, nla_data (bss[NL80211_BSS_BSSID]), ETH_ALEN); + memcpy (info->bssid, nla_data (bss[NL80211_BSS_BSSID]), ETH_ALEN); if (bss[NL80211_BSS_FREQUENCY]) info->freq = nla_get_u32 (bss[NL80211_BSS_FREQUENCY]); @@ -370,7 +575,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) guint8 *ssid; guint32 ssid_len; - find_ssid(nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]), + find_ssid (nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len (bss[NL80211_BSS_INFORMATION_ELEMENTS]), &ssid, &ssid_len); if (ssid && ssid_len && ssid_len <= sizeof (info->ssid)) { @@ -390,7 +595,7 @@ nl80211_get_bss_info (WifiDataNl80211 *nl80211, { struct nl_msg *msg; - memset(bss_info, 0, sizeof (*bss_info)); + memset (bss_info, 0, sizeof (*bss_info)); msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_SCAN, NLM_F_DUMP); @@ -424,24 +629,6 @@ wifi_nl80211_find_freq (WifiData *data, const guint32 *freqs) return 0; } -static GByteArray * -wifi_nl80211_get_ssid (WifiData *data) -{ - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; - GByteArray *array = NULL; - struct nl80211_bss_info bss_info; - - nl80211_get_bss_info (nl80211, &bss_info); - - if (bss_info.valid) { - array = g_byte_array_sized_new (bss_info.ssid_len); - g_byte_array_append (array, (const guint8 *) bss_info.ssid, - bss_info.ssid_len); - } - - return array; -} - static gboolean wifi_nl80211_get_bssid (WifiData *data, guint8 *out_bssid) { @@ -451,7 +638,7 @@ wifi_nl80211_get_bssid (WifiData *data, guint8 *out_bssid) nl80211_get_bss_info (nl80211, &bss_info); if (bss_info.valid) - memcpy(out_bssid, bss_info.bssid, ETH_ALEN); + memcpy (out_bssid, bss_info.bssid, ETH_ALEN); return bss_info.valid; } @@ -533,7 +720,7 @@ nl80211_get_ap_info (WifiDataNl80211 *nl80211, struct nl_msg *msg; struct nl80211_bss_info bss_info; - memset(sta_info, 0, sizeof (*sta_info)); + memset (sta_info, 0, sizeof (*sta_info)); nl80211_get_bss_info (nl80211, &bss_info); if (!bss_info.valid) @@ -621,7 +808,7 @@ nl80211_wowlan_handler (struct nl_msg *msg, void *arg) info->enabled = FALSE; - if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; @@ -695,7 +882,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) G_STATIC_ASSERT (NL80211_FREQUENCY_ATTR_PASSIVE_SCAN != NL80211_FREQUENCY_ATTR_NO_IBSS); #endif - if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; @@ -743,13 +930,13 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band, - NULL) < 0) + NULL) < 0) return NL_SKIP; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], - rem_freq) { + nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX, - nl_freq, freq_policy); + nl_freq, freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; @@ -767,7 +954,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) NULL) < 0) return NL_SKIP; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX, nl_freq, freq_policy); @@ -853,9 +1040,9 @@ wifi_nl80211_init (const char *iface, int ifindex) nl80211 = wifi_data_new (iface, ifindex, sizeof (*nl80211)); nl80211->parent.get_mode = wifi_nl80211_get_mode; nl80211->parent.set_mode = wifi_nl80211_set_mode; + nl80211->parent.set_powersave = wifi_nl80211_set_powersave; nl80211->parent.get_freq = wifi_nl80211_get_freq; nl80211->parent.find_freq = wifi_nl80211_find_freq; - nl80211->parent.get_ssid = wifi_nl80211_get_ssid; nl80211->parent.get_bssid = wifi_nl80211_get_bssid; nl80211->parent.get_rate = wifi_nl80211_get_rate; nl80211->parent.get_qual = wifi_nl80211_get_qual; @@ -868,7 +1055,7 @@ wifi_nl80211_init (const char *iface, int ifindex) if (nl80211->nl_sock == NULL) goto error; - if (genl_connect (nl80211->nl_sock)) + if (nl_connect (nl80211->nl_sock, NETLINK_GENERIC)) goto error; nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211"); @@ -886,22 +1073,22 @@ wifi_nl80211_init (const char *iface, int ifindex) if (nl80211_send_and_recv (nl80211, msg, nl80211_wiphy_info_handler, &device_info) < 0) { nm_log_dbg (LOGD_HW | LOGD_WIFI, - "(%s): NL80211_CMD_GET_WIPHY request failed", - nl80211->parent.iface); + "(%s): NL80211_CMD_GET_WIPHY request failed", + nl80211->parent.iface); goto error; } if (!device_info.success) { nm_log_dbg (LOGD_HW | LOGD_WIFI, - "(%s): NL80211_CMD_GET_WIPHY request indicated failure", - nl80211->parent.iface); + "(%s): NL80211_CMD_GET_WIPHY request indicated failure", + nl80211->parent.iface); goto error; } if (!device_info.supported) { nm_log_dbg (LOGD_HW | LOGD_WIFI, - "(%s): driver does not fully support nl80211, falling back to WEXT", - nl80211->parent.iface); + "(%s): driver does not fully support nl80211, falling back to WEXT", + nl80211->parent.iface); goto error; } @@ -914,8 +1101,8 @@ wifi_nl80211_init (const char *iface, int ifindex) if (device_info.num_freqs == 0 || device_info.freqs == NULL) { nm_log_err (LOGD_HW | LOGD_WIFI, - "(%s): driver reports no supported frequencies", - nl80211->parent.iface); + "(%s): driver reports no supported frequencies", + nl80211->parent.iface); goto error; } diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h index aaedd5b7..21a4c57b 100644 --- a/src/platform/wifi/wifi-utils-private.h +++ b/src/platform/wifi/wifi-utils-private.h @@ -21,8 +21,8 @@ #ifndef __WIFI_UTILS_PRIVATE_H__ #define __WIFI_UTILS_PRIVATE_H__ -#include <glib.h> +#include "nm-default.h" #include "nm-dbus-interface.h" #include "wifi-utils.h" @@ -35,15 +35,15 @@ struct WifiData { gboolean (*set_mode) (WifiData *data, const NM80211Mode mode); + /* Set power saving mode on an interface */ + gboolean (*set_powersave) (WifiData *data, guint32 powersave); + /* Return current frequency in MHz (really associated BSS frequency) */ guint32 (*get_freq) (WifiData *data); /* Return first supported frequency in the zero-terminated list */ guint32 (*find_freq) (WifiData *data, const guint32 *freqs); - /* If SSID is empty/blank (zero-length or all \0s) return NULL */ - GByteArray * (*get_ssid) (WifiData *data); - /* Return current bitrate in Kbps */ guint32 (*get_rate) (WifiData *data); diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c index 470b73d1..96bcf53d 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/wifi-utils-wext.c @@ -28,11 +28,10 @@ #include <unistd.h> #include <math.h> -#include <glib.h> +#include "nm-default.h" #include "wifi-utils-private.h" #include "wifi-utils-wext.h" -#include "nm-logging.h" #include "nm-utils.h" #include "nm-platform-utils.h" @@ -45,7 +44,6 @@ #include <sys/socket.h> #include <linux/wireless.h> - typedef struct { WifiData parent; int fd; @@ -166,6 +164,30 @@ wifi_wext_set_mode (WifiData *data, const NM80211Mode mode) return TRUE; } +static gboolean +wifi_wext_set_powersave (WifiData *data, guint32 powersave) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + + memset (&wrq, 0, sizeof (struct iwreq)); + if (powersave == 1) { + wrq.u.power.flags = IW_POWER_ALL_R; + } else + wrq.u.power.disabled = 1; + + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + if (ioctl (wext->fd, SIOCSIWPOWER, &wrq) < 0) { + if (errno != ENODEV) { + nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): error setting powersave %" G_GUINT32_FORMAT, + wext->parent.iface, powersave); + } + return FALSE; + } + + return TRUE; +} + static guint32 wifi_wext_get_freq (WifiData *data) { @@ -200,36 +222,6 @@ wifi_wext_find_freq (WifiData *data, const guint32 *freqs) return 0; } -static GByteArray * -wifi_wext_get_ssid (WifiData *data) -{ - WifiDataWext *wext = (WifiDataWext *) data; - struct iwreq wrq; - char ssid[IW_ESSID_MAX_SIZE + 2]; - guint32 len; - GByteArray *array = NULL; - - memset (ssid, 0, sizeof (ssid)); - wrq.u.essid.pointer = (caddr_t) &ssid; - wrq.u.essid.length = sizeof (ssid); - wrq.u.essid.flags = 0; - strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); - - if (ioctl (wext->fd, SIOCGIWESSID, &wrq) < 0) { - nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): couldn't get SSID: %d", - wext->parent.iface, errno); - return NULL; - } - - len = wrq.u.essid.length; - if (nm_utils_is_empty_ssid ((guint8 *) ssid, len) == FALSE) { - array = g_byte_array_sized_new (len); - g_byte_array_append (array, (const guint8 *) ssid, len); - } - - return array; -} - static gboolean wifi_wext_get_bssid (WifiData *data, guint8 *out_bssid) { @@ -575,9 +567,9 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan) wext = wifi_data_new (iface, ifindex, sizeof (*wext)); wext->parent.get_mode = wifi_wext_get_mode; wext->parent.set_mode = wifi_wext_set_mode; + wext->parent.set_powersave = wifi_wext_set_powersave; wext->parent.get_freq = wifi_wext_get_freq; wext->parent.find_freq = wifi_wext_find_freq; - wext->parent.get_ssid = wifi_wext_get_ssid; wext->parent.get_bssid = wifi_wext_get_bssid; wext->parent.get_rate = wifi_wext_get_rate; wext->parent.get_qual = wifi_wext_get_qual; diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index 068c404e..5f2f61b0 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -24,8 +24,8 @@ #include <sys/stat.h> #include <stdio.h> #include <string.h> -#include <glib.h> +#include "nm-default.h" #include "wifi-utils.h" #include "wifi-utils-private.h" #include "wifi-utils-nl80211.h" @@ -98,6 +98,14 @@ wifi_utils_set_mode (WifiData *data, const NM80211Mode mode) return data->set_mode ? data->set_mode (data, mode) : TRUE; } +gboolean +wifi_utils_set_powersave (WifiData *data, guint32 powersave) +{ + g_return_val_if_fail (data != NULL, FALSE); + + return data->set_powersave ? data->set_powersave (data, powersave) : TRUE; +} + guint32 wifi_utils_get_freq (WifiData *data) { @@ -113,13 +121,6 @@ wifi_utils_find_freq (WifiData *data, const guint32 *freqs) return data->find_freq (data, freqs); } -GByteArray * -wifi_utils_get_ssid (WifiData *data) -{ - g_return_val_if_fail (data != NULL, NULL); - return data->get_ssid (data); -} - gboolean wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid) { diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index 588386ab..21dac9e9 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -23,8 +23,8 @@ #define __WIFI_UTILS_H__ #include <net/ethernet.h> -#include <glib.h> +#include "nm-default.h" #include "nm-dbus-interface.h" typedef struct WifiData WifiData; @@ -48,9 +48,6 @@ guint32 wifi_utils_get_freq (WifiData *data); * Frequencies are specified in MHz. */ guint32 wifi_utils_find_freq (WifiData *data, const guint32 *freqs); -/* Caller must free returned byte array */ -GByteArray *wifi_utils_get_ssid (WifiData *data); - /* out_bssid must be ETH_ALEN bytes */ gboolean wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid); @@ -66,6 +63,8 @@ gboolean wifi_utils_indicate_addressing_running (WifiData *data, gboolean runnin /* Returns true if WoWLAN is enabled on device */ gboolean wifi_utils_get_wowlan (WifiData *data); +gboolean wifi_utils_set_powersave (WifiData *data, guint32 powersave); + /* OLPC Mesh-only functions */ guint32 wifi_utils_get_mesh_channel (WifiData *data); |