diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h index 8260c2b562..9be6b0f321 100644 --- a/include/nm-macros-internal.h +++ b/include/nm-macros-internal.h @@ -189,6 +189,29 @@ nm_clear_g_source (guint *id) #define NM_FLAGS_ANY(flags, check) ( ( ((flags) & (check)) != 0 ) ? TRUE : FALSE ) #define NM_FLAGS_ALL(flags, check) ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE ) +#define NM_FLAGS_SET(flags, val) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + _flags | _val; \ + }) + +#define NM_FLAGS_UNSET(flags, val) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + _flags & (~_val); \ + }) + +#define NM_FLAGS_ASSIGN(flags, val, assign) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + (assign) \ + ? _flags | (_val) \ + : _flags & (~_val); \ + }) + /*****************************************************************************/ #endif /* __NM_MACROS_INTERNAL_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index 90ee90d492..b9809a28a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -220,6 +220,8 @@ nm_sources = \ platform/nm-linux-platform.h \ platform/nm-platform.c \ platform/nm-platform.h \ + platform/nm-platform-utils.c \ + platform/nm-platform-utils.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h \ @@ -477,6 +479,8 @@ libnm_iface_helper_la_SOURCES = \ platform/nm-linux-platform.h \ platform/nm-platform.c \ platform/nm-platform.h \ + platform/nm-platform-utils.c \ + platform/nm-platform-utils.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h \ diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index a989813d47..2225541c8d 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -176,8 +176,19 @@ nm_spawn_process (const char *args, GError **error) return status; } +static const char * +_trunk_first_line (char *str) +{ + char *s; + + s = strchr (str, '\n'); + if (s) + s[0] = '\0'; + return str; +} + int -nm_utils_modprobe (GError **error, const char *arg1, ...) +nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *arg1, ...) { gs_unref_ptrarray GPtrArray *argv = NULL; int exit_status; @@ -185,6 +196,8 @@ nm_utils_modprobe (GError **error, const char *arg1, ...) #define ARGV_TO_STR(argv) (_log_str ? _log_str : (_log_str = g_strjoinv (" ", (char **) argv->pdata))) GError *local = NULL; va_list ap; + NMLogLevel llevel = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR; + gs_free char *std_out = NULL, *std_err = NULL; g_return_val_if_fail (!error || !*error, -1); g_return_val_if_fail (arg1, -1); @@ -202,12 +215,14 @@ nm_utils_modprobe (GError **error, const char *arg1, ...) g_ptr_array_add (argv, NULL); nm_log_dbg (LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR (argv)); - if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, NULL, NULL, &exit_status, &local)) { - nm_log_err (LOGD_CORE, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message); + if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, &std_out, &std_err, &exit_status, &local)) { + nm_log (llevel, LOGD_CORE, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message); g_propagate_error (error, local); return -1; } else if (exit_status != 0) - nm_log_err (LOGD_CORE, "modprobe: '%s' exited with error %d", ARGV_TO_STR (argv), exit_status); + nm_log (llevel, LOGD_CORE, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status, + std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "", + std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : ""); return exit_status; } diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 10f1f12f63..ad584e2297 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -51,7 +51,7 @@ nm_utils_ip6_route_metric_normalize (guint32 metric) int nm_spawn_process (const char *args, GError **error); -int nm_utils_modprobe (GError **error, const char *arg1, ...) G_GNUC_NULL_TERMINATED; +int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED; /** * str_if_set: diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index bc5273f531..274f6b1cc7 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -26,10 +26,6 @@ #include #include #include -#include -#include -#include -#include #include #include @@ -48,6 +44,7 @@ #include "nm-enum-types.h" #include "nm-dbus-manager.h" #include "nm-platform.h" +#include "nm-platform-utils.h" #include "nm-dcb.h" #include "nm-settings-connection.h" #include "nm-config.h" @@ -1508,37 +1505,10 @@ get_link_speed (NMDevice *device) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - struct ifreq ifr; - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; guint32 speed; - int fd; - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - _LOGW (LOGD_HW | LOGD_ETHER, "couldn't open ethtool control socket."); + if (!nmp_utils_ethtool_get_link_speed (nm_device_get_iface (device), &speed)) return; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - strncpy (ifr.ifr_name, nm_device_get_iface (device), IFNAMSIZ); - ifr.ifr_data = (char *) &edata; - - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - close (fd); - return; - } - close (fd); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) - speed = edata.speed; -#else - speed = ethtool_cmd_speed (&edata); -#endif - if (speed == G_MAXUINT16 || speed == G_MAXUINT32) - speed = 0; - if (priv->speed == speed) return; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 3d5cda21b9..625ad3ee33 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1096,7 +1096,14 @@ nm_device_finish_init (NMDevice *self) nm_device_enslave_slave (priv->master, self, NULL); if (priv->ifindex > 0) { - if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) { + if (priv->ifindex == 1) { + /* keep 'lo' as default-unmanaged. */ + + /* FIXME: either find a better way to unmange 'lo' that cannot be changed + * by user configuration (NM_UNMANGED_LOOPBACK?) or fix managing 'lo'. + * Currently it can happen that NM deletes 127.0.0.1 address. */ + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, TRUE); + } else if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) { nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged); nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, platform_unmanaged); } else { @@ -5216,7 +5223,7 @@ share_init (void) } for (iter = modules; *iter; iter++) - nm_utils_modprobe (NULL, *iter, NULL); + nm_utils_modprobe (NULL, FALSE, *iter, NULL); return TRUE; } diff --git a/src/nm-manager.c b/src/nm-manager.c index 4950c98f86..0c1201cee2 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1977,6 +1977,21 @@ platform_link_cb (NMPlatform *platform, } } +static void +platform_query_devices (NMManager *self) +{ + GArray *links_array; + NMPlatformLink *links; + int i; + + links_array = nm_platform_link_get_all (NM_PLATFORM_GET); + links = (NMPlatformLink *) links_array->data; + for (i = 0; i < links_array->len; i++) + platform_link_added (self, links[i].ifindex, &links[i], NM_PLATFORM_REASON_INTERNAL); + + g_array_unref (links_array); +} + static void rfkill_manager_rfkill_changed_cb (NMRfkillManager *rfkill_mgr, RfKillType rtype, @@ -4113,7 +4128,7 @@ nm_manager_start (NMManager *self) /* Start device factories */ nm_device_factory_manager_for_each_factory (start_factory, NULL); - nm_platform_query_devices (NM_PLATFORM_GET); + platform_query_devices (self); /* * Connections added before the manager is started do not emit diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index b97237d96c..88f27aab0f 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -31,10 +31,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -58,6 +54,7 @@ #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" @@ -362,162 +359,45 @@ _support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) #endif } -/****************************************************************** - * ethtool - ******************************************************************/ +/******************************************************************/ -static gboolean -ethtool_get (const char *name, gpointer edata) +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 ifreq ifr; - int fd; + struct nlmsghdr *msg_hdr = nlmsg_hdr (msg); - if (!name || !*name) - return FALSE; + if (!_support_kernel_extended_ifa_flags_still_undecided ()) + return; - memset (&ifr, 0, sizeof (ifr)); - strncpy (ifr.ifr_name, name, IFNAMSIZ); - ifr.ifr_data = edata; + msg_hdr = nlmsg_hdr (msg); + if (msg_hdr->nlmsg_type != RTM_NEWADDR) + return; - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - error ("ethtool: Could not open socket."); - return FALSE; - } + /* the extended address flags are only set for AF_INET6 */ + if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) + return; - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - debug ("ethtool: Request failed: %s", strerror (errno)); - close (fd); - return FALSE; - } - - close (fd); - return TRUE; -} - -static int -ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) -{ - gs_free struct ethtool_sset_info *info = NULL; - gs_free struct ethtool_gstrings *strings = NULL; - guint32 len, i; - - info = g_malloc0 (sizeof (*info) + sizeof (guint32)); - info->cmd = ETHTOOL_GSSET_INFO; - info->reserved = 0; - info->sset_mask = 1ULL << stringset_id; - - if (!ethtool_get (ifname, info)) - return -1; - if (!info->sset_mask) - return -1; - - len = info->data[0]; - - strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = stringset_id; - strings->len = len; - if (!ethtool_get (ifname, strings)) - return -1; - - for (i = 0; i < len; i++) { - if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) - return i; - } - - return -1; + /* 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; } static gboolean -ethtool_get_driver_info (const char *ifname, - char **out_driver_name, - char **out_driver_version, - char **out_fw_version) +_support_kernel_extended_ifa_flags_get () { - struct ethtool_drvinfo drvinfo = { 0 }; - - if (!ifname) - return FALSE; - - drvinfo.cmd = ETHTOOL_GDRVINFO; - if (!ethtool_get (ifname, &drvinfo)) - return FALSE; - - if (out_driver_name) - *out_driver_name = g_strdup (drvinfo.driver); - if (out_driver_version) - *out_driver_version = g_strdup (drvinfo.version); - if (out_fw_version) - *out_fw_version = g_strdup (drvinfo.fw_version); - - return TRUE; -} - -static gboolean -ethtool_get_permanent_address (const char *ifname, - guint8 *buf, - size_t *length) -{ - gs_free struct ethtool_perm_addr *epaddr = NULL; - - if (!ifname) - return FALSE; - - epaddr = g_malloc0 (sizeof (*epaddr) + NM_UTILS_HWADDR_LEN_MAX); - epaddr->cmd = ETHTOOL_GPERMADDR; - epaddr->size = NM_UTILS_HWADDR_LEN_MAX; - - if (!ethtool_get (ifname, epaddr)) - return FALSE; - if (!nm_ethernet_address_is_valid (epaddr->data, epaddr->size)) - return FALSE; - - g_assert (epaddr->size <= NM_UTILS_HWADDR_LEN_MAX); - memcpy (buf, epaddr->data, epaddr->size); - *length = epaddr->size; - return TRUE; -} - -/****************************************************************** - * udev - ******************************************************************/ - -static const char * -udev_get_driver (GUdevDevice *device, int ifindex) -{ - GUdevDevice *parent = NULL, *grandparent = NULL; - const char *driver, *subsys; - - driver = g_udev_device_get_driver (device); - if (driver) - goto out; - - /* Try the parent */ - parent = g_udev_device_get_parent (device); - if (parent) { - driver = g_udev_device_get_driver (parent); - if (!driver) { - /* Try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = g_udev_device_get_subsystem (parent); - if ( (g_strcmp0 (subsys, "ibmebus") == 0) - || (subsys == NULL)) { - grandparent = g_udev_device_get_parent (parent); - if (grandparent) - driver = g_udev_device_get_driver (grandparent); - } - } + 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; } - g_clear_object (&parent); - g_clear_object (&grandparent); - -out: - /* Intern the string so we don't have to worry about memory - * management in NMPlatformLink. */ - return g_intern_string (driver); + return _support_kernel_extended_ifa_flags > 0; } /****************************************************************** @@ -550,8 +430,6 @@ typedef struct { GHashTable *udev_devices; GHashTable *wifi_data; - - int support_kernel_extended_ifa_flags; } NMLinuxPlatformPrivate; #define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate)) @@ -824,42 +702,12 @@ nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, /******************************************************************/ -static void -_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr = nlmsg_hdr (msg); - - g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0); - g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR); - - /* 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). - **/ - priv->support_kernel_extended_ifa_flags = - nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */) - ? 1 : -1; -} - static gboolean check_support_kernel_extended_ifa_flags (NMPlatform *platform) { - NMLinuxPlatformPrivate *priv; - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - if (priv->support_kernel_extended_ifa_flags == 0) { - nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support."); - priv->support_kernel_extended_ifa_flags = -1; - } - - return priv->support_kernel_extended_ifa_flags > 0; + return _support_kernel_extended_ifa_flags_get (); } static gboolean @@ -1033,7 +881,7 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink) } /* Fallback OVS detection for kernel <= 3.16 */ - if (ethtool_get_driver_info (ifname, &driver, NULL, NULL)) { + if (nmp_utils_ethtool_get_driver_info (ifname, &driver, NULL, NULL)) { if (!g_strcmp0 (driver, "openvswitch")) return NM_LINK_TYPE_OPENVSWITCH; } @@ -1102,7 +950,7 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); if (udev_device) { - info->driver = udev_get_driver (udev_device, info->ifindex); + info->driver = nmp_utils_udev_get_driver (udev_device); info->udi = g_udev_device_get_sysfs_path (udev_device); info->initialized = TRUE; } @@ -1110,7 +958,7 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin if (!info->driver) info->driver = info->kind; if (!info->driver) { - if (ethtool_get_driver_info (name, &tmp, NULL, NULL)) { + if (nmp_utils_ethtool_get_driver_info (name, &tmp, NULL, NULL)) { info->driver = g_intern_string (tmp); g_free (tmp); } @@ -2065,12 +1913,8 @@ event_notification (struct nl_msg *msg, gpointer user_data) event = nlmsg_hdr (msg)->nlmsg_type; - if (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) { - /* if kernel support for extended ifa flags is still undecided, use the opportunity - * now and use @msg to decide it. This saves a blocking net link request. - **/ - _check_support_kernel_extended_ifa_flags_init (priv, msg); - } + if (_support_kernel_extended_ifa_flags_still_undecided () && event == RTM_NEWADDR) + _support_kernel_extended_ifa_flags_detect (msg); nl_msg_parse (msg, ref_object, &object); if (!object) @@ -2439,7 +2283,7 @@ link_add (NMPlatform *platform, * bond0 automatically. */ if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS)) - nm_utils_modprobe (NULL, "bonding", "max_bonds=0", NULL); + nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); } debug ("link: add link '%s' of type '%s' (%d)", @@ -2741,58 +2585,6 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable return FALSE; } -static gboolean -supports_ethtool_carrier_detect (const char *ifname) -{ - struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; - - /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we - * assume the device supports carrier-detect, otherwise we assume it - * doesn't. - */ - return ethtool_get (ifname, &edata); -} - -static gboolean -supports_mii_carrier_detect (const char *ifname) -{ - int fd; - struct ifreq ifr; - struct mii_ioctl_data *mii; - gboolean supports_mii = FALSE; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_PLATFORM, "couldn't open control socket."); - return FALSE; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - strncpy (ifr.ifr_name, ifname, IFNAMSIZ); - - errno = 0; - if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIPHY failed: %d", errno); - goto out; - } - - /* If we can read the BMSR register, we assume that the card supports MII link detection */ - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - mii->reg_num = MII_BMSR; - - if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG result 0x%X", mii->val_out); - supports_mii = TRUE; - } else { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG failed: %d", errno); - } - - out: - close (fd); - nm_log_dbg (LOGD_PLATFORM, "MII %s supported", supports_mii ? "is" : "not"); - return supports_mii; -} - static gboolean link_supports_carrier_detect (NMPlatform *platform, int ifindex) { @@ -2805,42 +2597,19 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) * us whether the device actually supports carrier detection in the first * place. We assume any device that does implements one of these two APIs. */ - return supports_ethtool_carrier_detect (name) || supports_mii_carrier_detect (name); + return nmp_utils_ethtool_supports_carrier_detect (name) || nmp_utils_mii_supports_carrier_detect (name); } static gboolean link_supports_vlans (NMPlatform *platform, int ifindex) { auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - const char *name = nm_platform_link_get_name (platform, ifindex); - gs_free struct ethtool_gfeatures *features = NULL; - int idx, block, bit, size; /* Only ARPHRD_ETHER links can possibly support VLANs. */ if (!rtnllink || rtnl_link_get_arptype (rtnllink) != ARPHRD_ETHER) return FALSE; - if (!name) - return FALSE; - - idx = ethtool_get_stringset_index (name, ETH_SS_FEATURES, "vlan-challenged"); - if (idx == -1) { - debug ("vlan-challenged ethtool feature does not exist?"); - return FALSE; - } - - block = idx / 32; - bit = idx % 32; - size = block + 1; - - features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); - features->cmd = ETHTOOL_GFEATURES; - features->size = size; - - if (!ethtool_get (name, features)) - return FALSE; - - return !(features->features[block].active & (1 << bit)); + return nmp_utils_ethtool_supports_vlans (rtnl_link_get_name (rtnllink)); } static gboolean @@ -2891,7 +2660,7 @@ link_get_permanent_address (NMPlatform *platform, guint8 *buf, size_t *length) { - return ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length); + return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length); } static gboolean @@ -3288,26 +3057,17 @@ static gboolean veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) { const char *ifname; - gs_free struct ethtool_stats *stats = NULL; - int peer_ifindex_stat; + int peer_ifindex; ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return FALSE; - peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat == -1) { - debug ("%s: peer_ifindex ethtool stat does not exist?", ifname); - return FALSE; - } - - stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = peer_ifindex_stat + 1; - if (!ethtool_get (ifname, stats)) + peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (ifname); + if (peer_ifindex <= 0) return FALSE; - props->peer = stats->data[peer_ifindex_stat]; + props->peer = peer_ifindex; return TRUE; } @@ -3806,16 +3566,9 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex) { NMLinkType type = link_get_type (platform, ifindex); - if (type == NM_LINK_TYPE_ETHERNET) { - struct ethtool_wolinfo wol; - - memset (&wol, 0, sizeof (wol)); - wol.cmd = ETHTOOL_GWOL; - if (!ethtool_get (link_get_name (platform, ifindex), &wol)) - return FALSE; - - return wol.wolopts != 0; - } else if (type == NM_LINK_TYPE_WIFI) { + if (type == NM_LINK_TYPE_ETHERNET) + return nmp_utils_ethtool_get_wake_on_lan (link_get_name (platform, ifindex)); + else if (type == NM_LINK_TYPE_WIFI) { WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); if (!wifi_data) @@ -3833,10 +3586,10 @@ link_get_driver_info (NMPlatform *platform, char **out_driver_version, char **out_fw_version) { - return ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex), - out_driver_name, - out_driver_version, - out_fw_version); + return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex), + out_driver_name, + out_driver_version, + out_fw_version); } /******************************************************************/ @@ -3963,7 +3716,7 @@ build_rtnl_addr (NMPlatform *platform, rtnl_addr_set_preferred_lifetime (rtnladdr, preferred); } if (flags) { - if ((flags & ~0xFF) && !check_support_kernel_extended_ifa_flags (platform)) { + 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 @@ -4775,6 +4528,8 @@ constructed (GObject *_object) int channel_flags; gboolean status; int nle; + GUdevEnumerator *enumerator; + GList *devices, *iter; /* Initialize netlink socket for requests */ priv->nlh = setup_socket (FALSE, platform); @@ -4839,16 +4594,6 @@ constructed (GObject *_object) priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); -} - -static void -setup_devices (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GUdevEnumerator *enumerator; - GList *devices, *iter; - /* And read initial device list */ enumerator = g_udev_enumerator_new (priv->udev_client); g_udev_enumerator_add_match_subsystem (enumerator, "net"); @@ -4862,6 +4607,8 @@ setup_devices (NMPlatform *platform) } g_list_free (devices); g_object_unref (enumerator); + + G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); } static void @@ -4899,8 +4646,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) object_class->constructed = constructed; object_class->finalize = nm_linux_platform_finalize; - platform_class->setup_devices = setup_devices; - platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c new file mode 100644 index 0000000000..407b91fbcc --- /dev/null +++ b/src/platform/nm-platform-utils.c @@ -0,0 +1,350 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nm-platform-utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gsystem-local-alloc.h" +#include "nm-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + + +/****************************************************************** + * ethtool + ******************************************************************/ + +static gboolean +ethtool_get (const char *name, gpointer edata) +{ + struct ifreq ifr; + int fd; + + if (!name || !*name) + return FALSE; + + memset (&ifr, 0, sizeof (ifr)); + strncpy (ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_data = edata; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_PLATFORM, "ethtool: Could not open socket."); + return FALSE; + } + + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: Request failed: %s", strerror (errno)); + close (fd); + return FALSE; + } + + close (fd); + return TRUE; +} + +static int +ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) +{ + gs_free struct ethtool_sset_info *info = NULL; + gs_free struct ethtool_gstrings *strings = NULL; + guint32 len, i; + + info = g_malloc0 (sizeof (*info) + sizeof (guint32)); + info->cmd = ETHTOOL_GSSET_INFO; + info->reserved = 0; + info->sset_mask = 1ULL << stringset_id; + + if (!ethtool_get (ifname, info)) + return -1; + if (!info->sset_mask) + return -1; + + len = info->data[0]; + + strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = stringset_id; + strings->len = len; + if (!ethtool_get (ifname, strings)) + return -1; + + for (i = 0; i < len; i++) { + if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) + return i; + } + + return -1; +} + +gboolean +nmp_utils_ethtool_get_driver_info (const char *ifname, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) +{ + struct ethtool_drvinfo drvinfo = { 0 }; + + if (!ifname) + return FALSE; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + if (!ethtool_get (ifname, &drvinfo)) + return FALSE; + + if (out_driver_name) + *out_driver_name = g_strdup (drvinfo.driver); + if (out_driver_version) + *out_driver_version = g_strdup (drvinfo.version); + if (out_fw_version) + *out_fw_version = g_strdup (drvinfo.fw_version); + + return TRUE; +} + +gboolean +nmp_utils_ethtool_get_permanent_address (const char *ifname, + guint8 *buf, + size_t *length) +{ + gs_free struct ethtool_perm_addr *epaddr = NULL; + + if (!ifname) + return FALSE; + + epaddr = g_malloc0 (sizeof (*epaddr) + NM_UTILS_HWADDR_LEN_MAX); + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = NM_UTILS_HWADDR_LEN_MAX; + + if (!ethtool_get (ifname, epaddr)) + return FALSE; + if (!nm_ethernet_address_is_valid (epaddr->data, epaddr->size)) + return FALSE; + + g_assert (epaddr->size <= NM_UTILS_HWADDR_LEN_MAX); + memcpy (buf, epaddr->data, epaddr->size); + *length = epaddr->size; + return TRUE; +} + +gboolean +nmp_utils_ethtool_supports_carrier_detect (const char *ifname) +{ + struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; + + /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we + * assume the device supports carrier-detect, otherwise we assume it + * doesn't. + */ + return ethtool_get (ifname, &edata); +} + +gboolean +nmp_utils_ethtool_supports_vlans (const char *ifname) +{ + gs_free struct ethtool_gfeatures *features = NULL; + int idx, block, bit, size; + + if (!ifname) + return FALSE; + + idx = ethtool_get_stringset_index (ifname, ETH_SS_FEATURES, "vlan-challenged"); + if (idx == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %s?", ifname); + return FALSE; + } + + block = idx / 32; + bit = idx % 32; + size = block + 1; + + features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); + features->cmd = ETHTOOL_GFEATURES; + features->size = size; + + if (!ethtool_get (ifname, features)) + return FALSE; + + return !(features->features[block].active & (1 << bit)); +} + +int +nmp_utils_ethtool_get_peer_ifindex (const char *ifname) +{ + gs_free struct ethtool_stats *stats = NULL; + int peer_ifindex_stat; + + if (!ifname) + return 0; + + peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); + if (peer_ifindex_stat == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: peer_ifindex stat for %s does not exist?", ifname); + return FALSE; + } + + stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = peer_ifindex_stat + 1; + if (!ethtool_get (ifname, stats)) + return 0; + + return stats->data[peer_ifindex_stat]; +} + +gboolean +nmp_utils_ethtool_get_wake_on_lan (const char *ifname) +{ + struct ethtool_wolinfo wol; + + if (!ifname) + return FALSE; + + memset (&wol, 0, sizeof (wol)); + wol.cmd = ETHTOOL_GWOL; + if (!ethtool_get (ifname, &wol)) + return FALSE; + + return wol.wolopts != 0; +} + +gboolean +nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed) +{ + struct ethtool_cmd edata = { + .cmd = ETHTOOL_GSET, + }; + guint32 speed; + + if (!ethtool_get (ifname, &edata)) + return FALSE; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + speed = edata.speed; +#else + speed = ethtool_cmd_speed (&edata); +#endif + if (speed == G_MAXUINT16 || speed == G_MAXUINT32) + speed = 0; + + if (out_speed) + *out_speed = speed; + return TRUE; +} + +/****************************************************************** + * mii + ******************************************************************/ + +gboolean +nmp_utils_mii_supports_carrier_detect (const char *ifname) +{ + int fd, errsv; + struct ifreq ifr; + struct mii_ioctl_data *mii; + gboolean supports_mii = FALSE; + + if (!ifname) + return FALSE; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname); + return FALSE; + } + + memset (&ifr, 0, sizeof (struct ifreq)); + strncpy (ifr.ifr_name, ifname, IFNAMSIZ); + + errno = 0; + if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { + errsv = errno; + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIPHY failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); + goto out; + } + + /* If we can read the BMSR register, we assume that the card supports MII link detection */ + mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; + mii->reg_num = MII_BMSR; + + if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) { + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG result 0x%X (%s)", mii->val_out, ifname); + supports_mii = TRUE; + } else { + errsv = errno; + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); + } + +out: + close (fd); + nm_log_dbg (LOGD_PLATFORM, "mii: MII %s supported (%s)", supports_mii ? "is" : "not", ifname); + return supports_mii; +} + +/****************************************************************** + * udev + ******************************************************************/ + +const char * +nmp_utils_udev_get_driver (GUdevDevice *device) +{ + GUdevDevice *parent = NULL, *grandparent = NULL; + const char *driver, *subsys; + + driver = g_udev_device_get_driver (device); + if (driver) + goto out; + + /* Try the parent */ + parent = g_udev_device_get_parent (device); + if (parent) { + driver = g_udev_device_get_driver (parent); + if (!driver) { + /* Try the grandparent if it's an ibmebus device or if the + * subsys is NULL which usually indicates some sort of + * platform device like a 'gadget' net interface. + */ + subsys = g_udev_device_get_subsystem (parent); + if ( (g_strcmp0 (subsys, "ibmebus") == 0) + || (subsys == NULL)) { + grandparent = g_udev_device_get_parent (parent); + if (grandparent) + driver = g_udev_device_get_driver (grandparent); + } + } + } + g_clear_object (&parent); + g_clear_object (&grandparent); + +out: + /* Intern the string so we don't have to worry about memory + * management in NMPlatformLink. */ + return g_intern_string (driver); +} + + diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h new file mode 100644 index 0000000000..d0032f5d69 --- /dev/null +++ b/src/platform/nm-platform-utils.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_PLATFORM_UTILS_H__ +#define __NM_PLATFORM_UTILS_H__ + +#include "config.h" + +#include + +#include "nm-platform.h" + + +const char *nmp_utils_ethtool_get_driver (const char *ifname); +gboolean nmp_utils_ethtool_supports_carrier_detect (const char *ifname); +gboolean nmp_utils_ethtool_supports_vlans (const char *ifname); +int nmp_utils_ethtool_get_peer_ifindex (const char *ifname); +gboolean nmp_utils_ethtool_get_wake_on_lan (const char *ifname); +gboolean nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed); + +gboolean nmp_utils_ethtool_get_driver_info (const char *ifname, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version); + +gboolean nmp_utils_ethtool_get_permanent_address (const char *ifname, + guint8 *buf, + size_t *length); + + +gboolean nmp_utils_mii_supports_carrier_detect (const char *ifname); + + +const char *nmp_utils_udev_get_driver (GUdevDevice *device); + + +#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 876237e9ca..82014faa8b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -383,36 +383,6 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint ba /******************************************************************/ -/** - * nm_platform_query_devices: - * self: platform instance - * - * Emit #NMPlatform:link-changed ADDED signals for all currently-known links. - * Should only be called at startup. - */ -void -nm_platform_query_devices (NMPlatform *self) -{ - GArray *links_array; - NMPlatformLink *links; - int i; - - _CHECK_SELF_VOID (self, klass); - - links_array = nm_platform_link_get_all (self); - links = (NMPlatformLink *) links_array->data; - for (i = 0; i < links_array->len; i++) { - g_signal_emit (self, signals[SIGNAL_LINK_CHANGED], 0, - links[i].ifindex, &links[i], NM_PLATFORM_SIGNAL_ADDED, - NM_PLATFORM_REASON_INTERNAL); - } - g_array_unref (links_array); - - /* Platform specific device setup. */ - if (klass->setup_devices) - klass->setup_devices (self); -} - /** * nm_platform_link_get_all: * self: platform instance @@ -3112,8 +3082,8 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor #undef _CMP_POINTER -static const char * -_change_type_to_string (NMPlatformSignalChangeType change_type) +const char * +nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type) { switch (change_type) { case NM_PLATFORM_SIGNAL_ADDED: @@ -3131,31 +3101,31 @@ static void log_link (NMPlatform *p, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: link %7s: %s", _change_type_to_string (change_type), nm_platform_link_to_string (device)); + debug ("signal: link %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_link_to_string (device)); } static void log_ip4_address (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: address 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_address_to_string (address)); + debug ("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_address_to_string (address)); } static void log_ip6_address (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: address 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_address_to_string (address)); + debug ("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_address_to_string (address)); } static void log_ip4_route (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: route 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_route_to_string (route)); + debug ("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_route_to_string (route)); } static void log_ip6_route (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: route 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_route_to_string (route)); + debug ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route)); } /******************************************************************/ diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index ef1deff9ca..9b267e548c 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -368,8 +368,6 @@ struct _NMPlatform { typedef struct { GObjectClass parent; - void (*setup_devices) (NMPlatform *); - gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *path); @@ -516,6 +514,8 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" +const char *nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type); + /******************************************************************/ GType nm_platform_get_type (void); @@ -534,8 +534,6 @@ void nm_platform_set_error (NMPlatform *self, NMPlatformError error); NMPlatformError nm_platform_get_error (NMPlatform *self); const char *nm_platform_get_error_msg (NMPlatform *self); -void nm_platform_query_devices (NMPlatform *self); - gboolean nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value); char *nm_platform_sysctl_get (NMPlatform *self, const char *path); gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback); diff --git a/src/platform/tests/.gitignore b/src/platform/tests/.gitignore index 5457fbca6c..cdd8a51f83 100644 --- a/src/platform/tests/.gitignore +++ b/src/platform/tests/.gitignore @@ -1,11 +1,12 @@ /dump /monitor /platform -/test-link-fake -/test-link-linux /test-address-fake /test-address-linux +/test-cleanup-fake +/test-cleanup-linux +/test-general +/test-link-fake +/test-link-linux /test-route-fake /test-route-linux -/test-cleanup-fake -/test-cleanup-linux \ No newline at end of file diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 92400f9091..76b40f7ca3 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -37,6 +37,7 @@ noinst_PROGRAMS = \ test-link-linux \ test-address-fake \ test-address-linux \ + test-general \ test-route-fake \ test-route-linux \ test-cleanup-fake \ @@ -109,12 +110,19 @@ test_cleanup_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_cleanup_linux_LDADD = $(PLATFORM_LDADD) +test_general_SOURCES = \ + test-general.c +test_general_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + + @VALGRIND_RULES@ TESTS = \ test-address-fake \ test-address-linux \ test-cleanup-fake \ test-cleanup-linux \ + test-general \ test-link-fake \ test-link-linux \ test-route-fake \ diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 2eda47cc8b..8b4eb0465a 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -8,7 +8,7 @@ #include "nm-test-utils.h" #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" -#define SIGNAL_DATA_ARG(data) (data)->name, _change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count +#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 @@ -19,6 +19,13 @@ nmtst_platform_is_root_test () NM_PRAGMA_WARNING_REENABLE } +gboolean +nmtst_platform_is_sysfs_writable () +{ + return !nmtst_platform_is_root_test () + || (access ("/sys/devices", W_OK) == 0); +} + SignalData * add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname) { @@ -36,21 +43,6 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall return data; } -static const char * -_change_type_to_string (NMPlatformSignalChangeType change_type) -{ - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - return "added"; - case NM_PLATFORM_SIGNAL_CHANGED: - return "changed"; - case NM_PLATFORM_SIGNAL_REMOVED: - return "removed"; - default: - g_return_val_if_reached ("UNKNOWN"); - } -} - void _accept_signal (const char *file, int line, const char *func, SignalData *data) { @@ -127,7 +119,7 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl } data->received_count++; - debug ("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", data->name, _change_type_to_string (data->change_type), ifindex, received->name, data->received_count); + debug ("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", data->name, nm_platform_signal_change_type_to_string (data->change_type), ifindex, received->name, data->received_count); if (change_type == NM_PLATFORM_SIGNAL_REMOVED) g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); @@ -278,6 +270,46 @@ run_command (const char *format, ...) NMTST_DEFINE(); +static gboolean +unshare_user () +{ + FILE *f; + uid_t uid = geteuid (); + gid_t gid = getegid (); + + /* Already a root? */ + if (gid == 0 && uid == 0) + return TRUE; + + /* Become a root in new user NS. */ + if (unshare (CLONE_NEWUSER) != 0) + return FALSE; + + /* Since Linux 3.19 we have to disable setgroups() in order to map users. + * Just proceed if the file is not there. */ + f = fopen ("/proc/self/setgroups", "w"); + if (f) { + fprintf (f, "deny"); + fclose (f); + } + + /* Map current UID to root in NS to be created. */ + f = fopen ("/proc/self/uid_map", "w"); + if (!f) + return FALSE; + fprintf (f, "0 %d 1", uid); + fclose (f); + + /* Map current GID to root in NS to be created. */ + f = fopen ("/proc/self/gid_map", "w"); + if (!f) + return FALSE; + fprintf (f, "0 %d 1", gid); + fclose (f); + + return TRUE; +} + int main (int argc, char **argv) { @@ -286,17 +318,21 @@ main (int argc, char **argv) init_tests (&argc, &argv); - if (nmtst_platform_is_root_test () && getuid() != 0) { - /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ - nmtst_reexec_sudo (); + if ( nmtst_platform_is_root_test () + && (geteuid () != 0 || getegid () != 0)) { + if ( g_getenv ("NMTST_FORCE_REAL_ROOT") + || !unshare_user ()) { + /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ + nmtst_reexec_sudo (); #ifdef REQUIRE_ROOT_TESTS - g_print ("Fail test: requires root privileges (%s)\n", program); - return EXIT_FAILURE; + g_print ("Fail test: requires root privileges (%s)\n", program); + return EXIT_FAILURE; #else - g_print ("Skipping test: requires root privileges (%s)\n", program); - return g_test_run (); + g_print ("Skipping test: requires root privileges (%s)\n", program); + return g_test_run (); #endif + } } if (nmtst_platform_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { @@ -325,12 +361,17 @@ main (int argc, char **argv) g_error ("mount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); } if (mount (NULL, "/sys/devices", "sysfs", MS_REMOUNT, NULL) != 0) { - errsv = errno; - g_error ("remount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); - } - if (mount ("/sys/devices/devices", "/sys/devices", "sysfs", MS_BIND, NULL) != 0) { - errsv = errno; - g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); + /* Read-write remount failed. Never mind, we're probably just a root in + * our user NS. */ + if (umount ("/sys/devices") != 0) { + errsv = errno; + g_error ("umount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); + } + } else { + if (mount ("/sys/devices/devices", "/sys/devices", "sysfs", MS_BIND, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); + } } } diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 56e27f6581..563d9fb4f6 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -29,6 +29,7 @@ typedef struct { } SignalData; gboolean nmtst_platform_is_root_test (void); +gboolean nmtst_platform_is_sysfs_writable (void); SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname); #define add_signal(name, change_type, callback) add_signal_full (name, change_type, (GCallback) callback, 0, NULL) diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c new file mode 100644 index 0000000000..2f29d7de70 --- /dev/null +++ b/src/platform/tests/test-general.c @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nm-platform-utils.h" + +#include "nm-logging.h" + +#include "nm-test-utils.h" + + +/******************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + return g_test_run (); +} diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index bbd42946af..102fe795f7 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -219,12 +219,14 @@ test_slave (int master, int type, SignalData *master_changed) /* Set slave option */ switch (type) { case NM_LINK_TYPE_BRIDGE: - g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); - no_error (); - value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); - no_error (); - g_assert_cmpstr (value, ==, "789"); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); + no_error (); + value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); + no_error (); + g_assert_cmpstr (value, ==, "789"); + g_free (value); + } break; default: break; @@ -294,21 +296,25 @@ test_software (NMLinkType link_type, const char *link_typename) /* Set master option */ switch (link_type) { case NM_LINK_TYPE_BRIDGE: - g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); - no_error (); - value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); - no_error (); - g_assert_cmpstr (value, ==, "789"); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); + no_error (); + value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); + no_error (); + g_assert_cmpstr (value, ==, "789"); + g_free (value); + } break; case NM_LINK_TYPE_BOND: - g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); - no_error (); - value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); - no_error (); - /* When reading back, the output looks slightly different. */ - g_assert (g_str_has_prefix (value, "active-backup")); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); + no_error (); + value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); + no_error (); + /* When reading back, the output looks slightly different. */ + g_assert (g_str_has_prefix (value, "active-backup")); + g_free (value); + } break; default: break; diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c index 791296590b..414b16c91c 100644 --- a/src/ppp-manager/nm-ppp-manager.c +++ b/src/ppp-manager/nm-ppp-manager.c @@ -1106,7 +1106,7 @@ nm_ppp_manager_start (NMPPPManager *manager, /* Make sure /dev/ppp exists (bgo #533064) */ if (stat ("/dev/ppp", &st) || !S_ISCHR (st.st_mode)) - nm_utils_modprobe (NULL, "ppp_generic", NULL); + nm_utils_modprobe (NULL, FALSE, "ppp_generic", NULL); connection = nm_act_request_get_connection (req); g_assert (connection);