diff options
author | Thomas Haller <thaller@redhat.com> | 2016-12-13 12:02:24 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2016-12-13 12:03:46 +0100 |
commit | 0d7bf7dee31175d3eeab2bd5a9eef8e7b6618016 (patch) | |
tree | 06abd60e9ea4484f5efed8daebe51881fd50017d | |
parent | 597f327b200139665a919202d632229ca20df33d (diff) | |
parent | 396d90e744348b9b2c432e9a60eb3f6f245c8c5c (diff) | |
download | NetworkManager-0d7bf7dee31175d3eeab2bd5a9eef8e7b6618016.tar.gz |
core: merge branch 'th/sysctl-ifname-race-bgo775613'
https://bugzilla.gnome.org/show_bug.cgi?id=775613
38 files changed, 1221 insertions, 534 deletions
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index fdb45f9291..24499172ca 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -22,7 +22,9 @@ #ifndef __NM_MACROS_INTERNAL_H__ #define __NM_MACROS_INTERNAL_H__ +#include <stdio.h> #include <stdlib.h> +#include <errno.h> #include "nm-glib.h" @@ -59,6 +61,37 @@ _nm_auto_free_gstring_impl (GString **str) } #define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring_impl) +static inline void +_nm_auto_close_impl (int *pfd) +{ + if (*pfd >= 0) { + int errsv = errno; + + (void) close (*pfd); + errno = errsv; + } +} +#define nm_auto_close nm_auto(_nm_auto_close_impl) + +static inline void +_nm_auto_fclose_impl (FILE **pfd) +{ + if (*pfd) { + int errsv = errno; + + (void) fclose (*pfd); + errno = errsv; + } +} +#define nm_auto_fclose nm_auto(_nm_auto_fclose_impl) + +static inline void +_nm_auto_protect_errno (int *p_saved_errno) +{ + errno = *p_saved_errno; +} +#define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) + /*****************************************************************************/ /* http://stackoverflow.com/a/11172679 */ diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c index 2ff2fc1840..b04e9fe717 100644 --- a/src/devices/adsl/nm-atm-manager.c +++ b/src/devices/adsl/nm-atm-manager.c @@ -137,7 +137,7 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device) atm_index_path = g_strdup_printf ("/sys/class/atm/%s/atmindex", NM_ASSERT_VALID_PATH_COMPONENT (ifname)); atm_index = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, - atm_index_path, + NMP_SYSCTL_PATHID_ABSOLUTE (atm_index_path), 10, 0, G_MAXINT, -1); if (atm_index < 0) { diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index 6cf808e687..9af6e69035 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -158,7 +158,7 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl) g_return_val_if_fail (priv->brfd == -1, FALSE); g_return_val_if_fail (priv->nas_ifname != NULL, FALSE); - priv->brfd = socket (PF_ATMPVC, SOCK_DGRAM, ATM_AAL5); + priv->brfd = socket (PF_ATMPVC, SOCK_DGRAM | SOCK_CLOEXEC, ATM_AAL5); if (priv->brfd < 0) { errsv = errno; _LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv); @@ -344,7 +344,7 @@ br2684_create_iface (NMDeviceAdsl *self, nm_clear_g_source (&priv->nas_update_id); } - fd = socket (PF_ATMPVC, SOCK_DGRAM, ATM_AAL5); + fd = socket (PF_ATMPVC, SOCK_DGRAM | SOCK_CLOEXEC, ATM_AAL5); if (fd < 0) { errsv = errno; _LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv); @@ -547,7 +547,7 @@ carrier_update_cb (gpointer user_data) path = g_strdup_printf ("/sys/class/atm/%s/carrier", NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self)))); - carrier = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, 1, -1); + carrier = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 0, 1, -1); g_free (path); if (carrier != -1) diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c index 4c93feba60..aba3a0dd97 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.c +++ b/src/devices/bluetooth/nm-bluez5-dun.c @@ -64,7 +64,7 @@ dun_connect (NMBluez5DunContext *context) .channel = context->rfcomm_channel }; - context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC, BTPROTO_RFCOMM); if (context->rfcomm_fd < 0) { int errsv = errno; error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, @@ -112,7 +112,7 @@ dun_connect (NMBluez5DunContext *context) context->rfcomm_id = devid; snprintf (tty, ttylen, "/dev/rfcomm%d", devid); - while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY)) < 0 && try--) { + while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0 && try--) { if (try) { g_usleep (100 * 1000); continue; diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 43084d49b9..2f9a092a0f 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -208,7 +208,7 @@ _update_s390_subchannels (NMDeviceEthernet *self) gs_free char *path = NULL, *value = NULL; path = g_strdup_printf ("%s/%s", parent_path, item); - value = nm_platform_sysctl_get (NM_PLATFORM_GET, path); + value = nm_platform_sysctl_get (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (path)); if ( !strcmp (item, "portname") && !g_strcmp0 (value, "no portname required")) { @@ -827,7 +827,7 @@ link_negotiation_set (NMDevice *device) } } - if (!nm_platform_ethtool_get_link_settings (NM_PLATFORM_GET, nm_device_get_iface (device), + if (!nm_platform_ethtool_get_link_settings (NM_PLATFORM_GET, nm_device_get_ifindex (device), &link_autoneg, &link_speed, &link_duplex)) { _LOGW (LOGD_DEVICE, "set-link: unable to retrieve link negotiation"); return; @@ -852,7 +852,7 @@ link_negotiation_set (NMDevice *device) } if (!nm_platform_ethtool_set_link_settings (NM_PLATFORM_GET, - nm_device_get_iface (device), + nm_device_get_ifindex (device), autoneg, speed, duplex)) { @@ -1243,7 +1243,7 @@ wake_on_lan_enable (NMDevice *device) } wol = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; found: - return nm_platform_ethtool_set_wake_on_lan (NM_PLATFORM_GET, nm_device_get_iface (device), wol, password); + return nm_platform_ethtool_set_wake_on_lan (NM_PLATFORM_GET, nm_device_get_ifindex (device), wol, password); } /*****************************************************************************/ @@ -1609,7 +1609,7 @@ get_link_speed (NMDevice *device) NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); guint32 speed; - if (!nm_platform_ethtool_get_link_settings (NM_PLATFORM_GET, nm_device_get_iface (device), NULL, &speed, NULL)) + if (!nm_platform_ethtool_get_link_settings (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL, &speed, NULL)) return; if (priv->speed == speed) return; diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 47c2a01656..b0871b0a95 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -77,10 +77,11 @@ get_generic_capabilities (NMDevice *device) static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { + nm_auto_close int dirfd = -1; NMActStageReturn ret; NMSettingInfiniband *s_infiniband; + char ifname_verified[IFNAMSIZ]; const char *transport_mode; - char *mode_path; gboolean ok, no_firmware = FALSE; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -94,11 +95,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband); - mode_path = g_strdup_printf ("/sys/class/net/%s/mode", - NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (dev))); - if (!g_file_test (mode_path, G_FILE_TEST_EXISTS)) { - g_free (mode_path); - + dirfd = nm_platform_sysctl_open_netdir (NM_PLATFORM_GET, nm_device_get_ifindex (dev), ifname_verified); + if (dirfd < 0) { if (!strcmp (transport_mode, "datagram")) return NM_ACT_STAGE_RETURN_SUCCESS; else { @@ -109,8 +107,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) /* With some drivers the interface must be down to set transport mode */ nm_device_take_down (dev, TRUE); - ok = nm_platform_sysctl_set (NM_PLATFORM_GET, mode_path, transport_mode); - g_free (mode_path); + ok = nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"), transport_mode); nm_device_bring_up (dev, TRUE, &no_firmware); if (!ok) { diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 15bcacb0bc..ea70f1a539 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -80,7 +80,7 @@ reload_tun_properties (NMDeviceTun *self) ifindex = nm_device_get_ifindex (NM_DEVICE (self)); if (ifindex > 0) { - if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) { + if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, ifindex, NULL, &props)) { _LOGD (LOGD_DEVICE, "tun-properties: cannot loading tun properties from platform for ifindex %d", ifindex); ifindex = 0; } else if (g_strcmp0 (priv->mode, props.mode) != 0) { @@ -181,7 +181,7 @@ update_connection (NMDevice *device, NMConnection *connection) nm_connection_add_setting (connection, (NMSetting *) s_tun); } - if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { + if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL, &props)) { _LOGW (LOGD_PLATFORM, "failed to get TUN interface info while updating connection."); return; } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2785a1a890..af201d8ce9 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -629,13 +629,13 @@ init_ip6_config_dns_priority (NMDevice *self, NMIP6Config *config) gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { - return nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value); + return nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), value); } static guint32 nm_device_ipv6_sysctl_get_int32 (NMDevice *self, const char *property, gint32 fallback) { - return nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), fallback); + return nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), fallback); } gboolean @@ -6771,7 +6771,7 @@ save_ip6_properties (NMDevice *self) g_hash_table_remove_all (priv->ip6_saved_properties); for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - value = nm_platform_sysctl_get (NM_PLATFORM_GET, nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])); + value = nm_platform_sysctl_get (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]))); if (value) { g_hash_table_insert (priv->ip6_saved_properties, (char *) ip6_properties_to_save[i], @@ -6832,7 +6832,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) if (enable) { /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ value = nm_platform_sysctl_get (NM_PLATFORM_GET, - nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6")); + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6"))); if (g_strcmp0 (value, "0") == 0) nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); g_free (value); @@ -6898,7 +6898,7 @@ _ip6_privacy_get (NMDevice *self) * Instead of reading static config files in /etc, just read the current sysctl value. * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves * the "default" entry untouched. */ - ip6_privacy = nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, "/proc/sys/net/ipv6/conf/default/use_tempaddr", NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + ip6_privacy = nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); return _ip6_privacy_clamp (ip6_privacy); } @@ -7406,14 +7406,14 @@ share_init (void) char **iter; int errsv; - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv4/ip_forward", "1")) { + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_forward"), "1")) { errsv = errno; nm_log_err (LOGD_SHARING, "share: error enabling IPv4 forwarding: (%d) %s", errsv, strerror (errsv)); return FALSE; } - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv4/ip_dynaddr", "1")) { + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_dynaddr"), "1")) { errsv = errno; nm_log_err (LOGD_SHARING, "share: error enabling dynamic addresses: (%d) %s", errsv, strerror (errsv)); @@ -7754,7 +7754,7 @@ activate_stage5_ip6_config_commit (NMDevice *self) method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv6/conf/all/forwarding", "1")) { + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/all/forwarding"), "1")) { errsv = errno; _LOGE (LOGD_SHARING, "share: error enabling IPv6 forwarding: (%d) %s", errsv, strerror (errsv)); nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c index 5d28d461df..4d25f9a681 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -350,7 +350,7 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data) struct ifreq ifr = { }; int fd, s; - fd = open ("/dev/net/tun", O_RDWR); + fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); g_assert (fd >= 0); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; @@ -358,7 +358,7 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data) g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0); /* Bring the interface up */ - s = socket (AF_INET, SOCK_DGRAM, 0); + s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); g_assert (s >= 0); ifr.ifr_flags |= IFF_UP; g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0); diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index df71017745..635f20ea74 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -497,18 +497,18 @@ ppp_stats (NMPPPManager *ppp_manager, static gboolean port_speed_is_zero (const char *port) { - struct termios options; - gs_fd_close int fd = -1; + struct termios options; + nm_auto_close int fd = -1; - fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY); - if (fd < 0) + fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC); + if (fd < 0) return FALSE; - memset (&options, 0, sizeof (struct termios)); - if (tcgetattr (fd, &options) != 0) - return FALSE; + memset (&options, 0, sizeof (struct termios)); + if (tcgetattr (fd, &options) != 0) + return FALSE; - return cfgetospeed (&options) == B0; + return cfgetospeed (&options) == B0; } static NMActStageReturn diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index c8ebe23a4c..c52777cff7 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -719,7 +719,7 @@ update_resolv_conf (NMDnsManager *self, } } - if ((f = fopen (MY_RESOLV_CONF_TMP, "w")) == NULL) { + if ((f = fopen (MY_RESOLV_CONF_TMP, "we")) == NULL) { errsv = errno; g_set_error (error, NM_MANAGER_ERROR, @@ -1594,7 +1594,7 @@ _check_resconf_immutable (NMDnsManagerResolvConfManager rc_manager) } } - fd = open (_PATH_RESCONF, O_RDONLY); + fd = open (_PATH_RESCONF, O_RDONLY | O_CLOEXEC); if (fd != -1) { if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1) immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL); diff --git a/src/main-utils.c b/src/main-utils.c index bad3141abf..9e3aa7bd55 100644 --- a/src/main-utils.c +++ b/src/main-utils.c @@ -95,7 +95,7 @@ nm_main_utils_write_pidfile (const char *pidfile) int fd; gboolean success = FALSE; - if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) { + if ((fd = open (pidfile, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 00644)) < 0) { fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno)); return FALSE; } diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index 6378c90039..9e5cdaa059 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -521,7 +521,7 @@ static inline int ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, int min, int max, int defval) { return (int) nm_platform_sysctl_get_int_checked (platform, - nm_utils_ip6_property_path (ifname, property), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, property)), 10, min, max, diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index a0067aafc1..70af933835 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2802,6 +2802,246 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) return 0; } +_nm_printf (3, 4) +static int +_get_contents_error (GError **error, int errsv, const char *format, ...) +{ + if (errsv < 0) + errsv = -errsv; + else if (!errsv) + errsv = errno; + + if (error) { + char *msg; + va_list args; + + va_start (args, format); + msg = g_strdup_vprintf (format, args); + va_end (args); + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "%s: %s", + msg, g_strerror (errsv)); + g_free (msg); + } + return -errsv; +} + +/** + * nm_utils_fd_get_contents: + * @fd: open file descriptor to read. The fd will not be closed, + * but don't rely on it's state afterwards. + * @max_length: allocate at most @max_length bytes. If the + * file is larger, reading will fail. Set to zero to use + * a very large default. + * + * WARNING: @max_length is here to avoid a crash for huge/unlimited files. + * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of + * 4K, although the actual real is small. @max_length is the memory + * allocated in the process of reading the file, thus it must be at least + * the size reported by fstat. + * If you set it to 1K, read will fail because fstat() claims the + * file is larger. + * + * @contents: the output buffer with the file read. It is always + * NUL terminated. The buffer is at most @max_length long, including + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. + * @length: optional output argument of the read file size. + * + * A reimplementation of g_file_get_contents() with a few differences: + * - accepts an open fd, instead of a path name. This allows you to + * use openat(). + * - limits the maxium filesize to max_length. + * + * Returns: a negative error code on failure. + */ +int +nm_utils_fd_get_contents (int fd, + gsize max_length, + char **contents, + gsize *length, + GError **error) +{ + struct stat stat_buf; + gs_free char *str = NULL; + + g_return_val_if_fail (fd >= 0, -EINVAL); + g_return_val_if_fail (contents, -EINVAL); + g_return_val_if_fail (!error || !*error, -EINVAL); + + if (fstat (fd, &stat_buf) < 0) + return _get_contents_error (error, 0, "failure during fstat"); + + if (!max_length) { + /* default to a very large size, but not extreme */ + max_length = 2 * 1024 * 1024; + } + + if ( stat_buf.st_size > 0 + && S_ISREG (stat_buf.st_mode)) { + const gsize n_stat = stat_buf.st_size; + ssize_t n_read; + + if (n_stat > max_length - 1) + return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length); + + str = g_try_malloc (n_stat + 1); + if (!str) + return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat); + + n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE); + if (n_read < 0) + return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat); + str[n_read] = '\0'; + + if (n_read < n_stat) { + char *tmp; + + tmp = g_try_realloc (str, n_read + 1); + if (!tmp) + return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1); + str = tmp; + } + NM_SET_OUT (length, n_read); + } else { + nm_auto_fclose FILE *f = NULL; + char buf[4096]; + gsize n_have, n_alloc; + + if (!(f = fdopen (fd, "r"))) + return _get_contents_error (error, 0, "failure during fdopen"); + + n_have = 0; + n_alloc = 0; + + while (!feof (f)) { + int errsv; + gsize n_read; + + n_read = fread (buf, 1, sizeof (buf), f); + errsv = errno; + if (ferror (f)) + return _get_contents_error (error, errsv, "error during fread"); + + if ( n_have > G_MAXSIZE - 1 - n_read + || n_have + n_read + 1 > max_length) { + return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)", + (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, + max_length); + } + + if (n_have + n_read + 1 >= n_alloc) { + char *tmp; + + if (str) { + if (n_alloc >= max_length / 2) + n_alloc = max_length; + else + n_alloc *= 2; + } else + n_alloc = NM_MIN (n_read + 1, sizeof (buf)); + + tmp = g_try_realloc (str, n_alloc); + if (!tmp) + return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc); + str = tmp; + } + + memcpy (str + n_have, buf, n_read); + n_have += n_read; + } + + if (n_alloc == 0) + str = g_new0 (gchar, 1); + else { + str[n_have] = '\0'; + if (n_have + 1 < n_alloc) { + char *tmp; + + tmp = g_try_realloc (str, n_have + 1); + if (!tmp) + return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); + str = tmp; + } + } + + NM_SET_OUT (length, n_have); + } + + *contents = g_steal_pointer (&str); + return 0; +} + +/** + * nm_utils_file_get_contents: + * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). + * @filename: the filename to open. Possibly relative to @dirfd. + * @max_length: allocate at most @max_length bytes. + * WARNING: see nm_utils_fd_get_contents() hint about @max_length. + * @contents: the output buffer with the file read. It is always + * NUL terminated. The buffer is at most @max_length long, including + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. + * @length: optional output argument of the read file size. + * + * A reimplementation of g_file_get_contents() with a few differences: + * - accepts an @dirfd to open @filename relative to that path via openat(). + * - limits the maxium filesize to max_length. + * - uses O_CLOEXEC on internal file descriptor + * + * Returns: a negative error code on failure. + */ +int +nm_utils_file_get_contents (int dirfd, + const char *filename, + gsize max_length, + char **contents, + gsize *length, + GError **error) +{ + nm_auto_close int fd = -1; + int errsv; + + g_return_val_if_fail (filename && filename[0], -EINVAL); + + if (dirfd >= 0) { + fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + errsv = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to open file \"%s\" with openat: %s", + filename, + g_strerror (errsv)); + return -errsv; + } + } else { + fd = open (filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + errsv = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to open file \"%s\": %s", + filename, + g_strerror (errsv)); + return -errsv; + } + } + return nm_utils_fd_get_contents (fd, + max_length, + contents, + length, + error); +} + +/*****************************************************************************/ + /* taken from systemd's dev_urandom(). */ int nm_utils_read_urandom (void *p, size_t nbytes) @@ -2810,7 +3050,7 @@ nm_utils_read_urandom (void *p, size_t nbytes) int r; again: - fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); if (fd < 0) { r = errno; if (r == EINTR) diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 9cf196f709..f9d30b5ce4 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -313,6 +313,25 @@ int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns); ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll); int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll); +int nm_utils_fd_get_contents (int fd, + gsize max_length, + char **contents, + gsize *length, + GError **error); + +int nm_utils_file_get_contents (int dirfd, + const char *filename, + gsize max_length, + char **contents, + gsize *length, + GError **error); + +gboolean nm_utils_file_set_contents (const gchar *filename, + const gchar *contents, + gssize length, + mode_t mode, + GError **error); + int nm_utils_read_urandom (void *p, size_t n); char *nm_utils_machine_id_read (void); @@ -445,12 +464,6 @@ const char *nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize siz void nm_utils_get_reverse_dns_domains_ip4 (guint32 ip, guint8 plen, GPtrArray *domains); void nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GPtrArray *domains); -gboolean nm_utils_file_set_contents (const gchar *filename, - const gchar *contents, - gssize length, - mode_t mode, - GError **error); - struct stat; gboolean nm_utils_validate_plugin (const char *path, struct stat *stat, GError **error); diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index aa14b085fe..393e8acc57 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -246,7 +246,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in char val[16]; g_snprintf (val, sizeof (val), "%d", rdata->mtu); - nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "mtu")), val); } nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT); @@ -465,7 +465,7 @@ main (int argc, char *argv[]) } if (global_opt.dhcp4_address) { - nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries"), "1"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries")), "1"); dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), global_opt.ifname, @@ -512,10 +512,10 @@ main (int argc, char *argv[]) if (iid) nm_ndisc_set_iid (ndisc, *iid); - nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra"), "1"); - nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr"), "0"); - nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo"), "0"); - nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref"), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra")), "1"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr")), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo")), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref")), "0"); g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, diff --git a/src/nm-logging.c b/src/nm-logging.c index 50927f8caf..c242d90319 100644 --- a/src/nm-logging.c +++ b/src/nm-logging.c @@ -599,6 +599,7 @@ _nm_log_impl (const char *file, va_list args; char *msg; GTimeVal tv; + int errno_saved; if ((guint) level >= G_N_ELEMENTS (_nm_logging_enabled_state)) g_return_if_reached (); @@ -606,6 +607,8 @@ _nm_log_impl (const char *file, if (!(_nm_logging_enabled_state[level] & domain)) return; + errno_saved = errno; + /* Make sure that %m maps to the specified error */ if (error != 0) { if (error < 0) @@ -719,6 +722,8 @@ _nm_log_impl (const char *file, } g_free (msg); + + errno = errno_saved; } /*****************************************************************************/ diff --git a/src/nm-manager.c b/src/nm-manager.c index 8414f18802..58a60b197f 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -5450,7 +5450,7 @@ rfkill_change (NMManager *self, const char *desc, RfKillType rtype, gboolean ena g_return_if_fail (rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN); errno = 0; - fd = open ("/dev/rfkill", O_RDWR); + fd = open ("/dev/rfkill", O_RDWR | O_CLOEXEC); if (fd < 0) { if (errno == EACCES) _LOGW (LOGD_RFKILL, "(%s): failed to open killswitch device", desc); diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 0f34b8453f..b92f56ce21 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -118,21 +118,43 @@ _ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, guint8 plen) /*****************************************************************************/ +#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ + G_STMT_START { \ + const char *const _pathid = (pathid); \ + const int _dirfd = (dirfd); \ + const char *const _path = (path); \ + \ + g_assert (_path && _path[0]); \ + g_assert (!strstr (_path, "/../")); \ + if (_dirfd < 0) { \ + g_assert (!_pathid); \ + g_assert (_path[0] == '/'); \ + g_assert ( g_str_has_prefix (_path, "/proc/sys/") \ + || g_str_has_prefix (_path, "/sys/")); \ + } else { \ + g_assert_not_reached (); \ + } \ + } G_STMT_END + static gboolean -sysctl_set (NMPlatform *platform, const char *path, const char *value) +sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + g_hash_table_insert (priv->options, g_strdup (path), g_strdup (value)); return TRUE; } static char * -sysctl_get (NMPlatform *platform, const char *path) +sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *path) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + return g_strdup (g_hash_table_lookup (priv->options, path)); } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index ab2ab9b017..7a7947c6d7 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -239,7 +239,7 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActi static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); static void cache_prune_candidates_prune (NMPlatform *platform); static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); -static void _assert_netns_current (NMPlatform *platform); +static void ASSERT_NETNS_CURRENT (NMPlatform *platform); /*****************************************************************************/ @@ -573,18 +573,14 @@ _lookup_cached_link (const NMPCache *cache, int ifindex, gboolean *completed_fro #define DEVTYPE_PREFIX "DEVTYPE=" static char * -_linktype_read_devtype (const char *ifname) +_linktype_read_devtype (int dirfd) { - char uevent[NM_STRLEN ("/sys/class/net/123456789012345/uevent\0") + 100 /*safety*/]; char *contents = NULL; char *cont, *end; - nm_sprintf_buf (uevent, - "/sys/class/net/%s/uevent", - NM_ASSERT_VALID_PATH_COMPONENT (ifname)); - nm_assert (strlen (uevent) < sizeof (uevent) - 1); + nm_assert (dirfd >= 0); - if (!g_file_get_contents (uevent, &contents, NULL, NULL)) + if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024, &contents, NULL, NULL) < 0) return NULL; for (cont = contents; cont; cont = end) { end = strpbrk (cont, "\r\n"); @@ -614,7 +610,8 @@ _linktype_get_type (NMPlatform *platform, { guint i; - _assert_netns_current (platform); + ASSERT_NETNS_CURRENT (platform); + nm_assert (ifname); if (completed_from_cache) { const NMPObject *obj; @@ -635,7 +632,7 @@ _linktype_get_type (NMPlatform *platform, * of messing stuff up. */ if ( obj && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) - && !g_strcmp0 (ifname, obj->link.name) + && nm_streq (ifname, obj->link.name) && ( !kind || !g_strcmp0 (kind, obj->link.kind))) { nm_assert (obj->link.kind == g_intern_string (obj->link.kind)); @@ -656,7 +653,7 @@ _linktype_get_type (NMPlatform *platform, NMPlatformTunProperties props; if ( platform - && nm_platform_link_tun_get_properties_ifname (platform, ifname, &props)) { + && nm_platform_link_tun_get_properties (platform, ifindex, ifname, &props)) { if (!g_strcmp0 (props.mode, "tap")) return NM_LINK_TYPE_TAP; if (!g_strcmp0 (props.mode, "tun")) @@ -679,50 +676,52 @@ _linktype_get_type (NMPlatform *platform, else if (arptype == ARPHRD_TUNNEL6) return NM_LINK_TYPE_IP6TNL; - if (ifname) { - char anycast_mask[NM_STRLEN ("/sys/class/net/123456789012345/anycast_mask\0") + 100 /*safety*/]; - gs_free char *driver = NULL; - gs_free char *devtype = NULL; + { + NMPUtilsEthtoolDriverInfo driver_info; /* Fallback OVS detection for kernel <= 3.16 */ - if (nmp_utils_ethtool_get_driver_info (ifname, &driver, NULL, NULL)) { - if (!g_strcmp0 (driver, "openvswitch")) + if (nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) { + if (nm_streq (driver_info.driver, "openvswitch")) return NM_LINK_TYPE_OPENVSWITCH; if (arptype == 256) { /* Some s390 CTC-type devices report 256 for the encapsulation type * for some reason, but we need to call them Ethernet. */ - if (!g_strcmp0 (driver, "ctcm")) + if (nm_streq (driver_info.driver, "ctcm")) return NM_LINK_TYPE_ETHERNET; } } + } - nm_sprintf_buf (anycast_mask, - "/sys/class/net/%s/anycast_mask", - NM_ASSERT_VALID_PATH_COMPONENT (ifname)); - nm_assert (strlen (anycast_mask) < sizeof (anycast_mask) - 1); - - if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS)) - return NM_LINK_TYPE_OLPC_MESH; - - devtype = _linktype_read_devtype (ifname); - for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) { - if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) { - if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) { - /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must - * use arptype to distinguish between them. - */ - if (arptype != ARPHRD_ETHER) - continue; + { + nm_auto_close int dirfd = -1; + gs_free char *devtype = NULL; + char ifname_verified[IFNAMSIZ]; + + dirfd = nmp_utils_sysctl_open_netdir (ifindex, ifname, ifname_verified); + if (dirfd >= 0) { + if (faccessat (dirfd, "anycast_mask", F_OK, 0) == 0) + return NM_LINK_TYPE_OLPC_MESH; + + devtype = _linktype_read_devtype (dirfd); + for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) { + if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) { + if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) { + /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must + * use arptype to distinguish between them. + */ + if (arptype != ARPHRD_ETHER) + continue; + } + return linktypes[i].nm_type; } - return linktypes[i].nm_type; } - } - /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ - if (wifi_utils_is_wifi (ifname)) - return NM_LINK_TYPE_WIFI; + /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ + if (wifi_utils_is_wifi (dirfd, ifname_verified)) + return NM_LINK_TYPE_WIFI; + } if (arptype == ARPHRD_ETHER) { /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary @@ -2481,50 +2480,66 @@ nm_linux_platform_setup (void) NULL); } -/*****************************************************************************/ - static void -_assert_netns_current (NMPlatform *platform) +ASSERT_NETNS_CURRENT (NMPlatform *platform) { -#if NM_MORE_ASSERTS nm_assert (NM_IS_LINUX_PLATFORM (platform)); - nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ())); -#endif } +/*****************************************************************************/ + +#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ + G_STMT_START { \ + const char *const _pathid = (pathid); \ + const int _dirfd = (dirfd); \ + const char *const _path = (path); \ + \ + nm_assert (_path && _path[0]); \ + g_assert (!strstr (_path, "/../")); \ + if (_dirfd < 0) { \ + nm_assert (!_pathid); \ + nm_assert (_path[0] == '/'); \ + nm_assert ( g_str_has_prefix (_path, "/proc/sys/") \ + || g_str_has_prefix (_path, "/sys/")); \ + } else { \ + nm_assert (_pathid && _pathid[0] && _pathid[0] != '/'); \ + nm_assert (_path[0] != '/'); \ + } \ + } G_STMT_END + static void -_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value) +_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, 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); + if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) { + _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, 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); + _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid, value_escaped); else - _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped); + _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid, value_escaped, contents_escaped); g_free (contents); g_free (contents_escaped); } g_free (value_escaped); } -#define _log_dbg_sysctl_set(platform, path, value) \ +#define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value) \ G_STMT_START { \ if (_LOGD_ENABLED ()) { \ - _log_dbg_sysctl_set_impl (platform, path, value); \ + _log_dbg_sysctl_set_impl (platform, pathid, dirfd, path, value); \ } \ } G_STMT_END static gboolean -sysctl_set (NMPlatform *platform, const char *path, const char *value) +sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) { nm_auto_pop_netns NMPNetns *netns = NULL; int fd, tries; @@ -2537,32 +2552,46 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value) 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, "/../")); + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - if (!nm_platform_netns_push (platform, &netns)) { - errno = ENETDOWN; - return FALSE; - } + if (dirfd < 0) { + if (!nm_platform_netns_push (platform, &netns)) { + errno = ENETDOWN; + return FALSE; + } - fd = open (path, O_WRONLY | O_TRUNC); - if (fd == -1) { - errsv = errno; - if (errsv == ENOENT) { - _LOGD ("sysctl: failed to open '%s': (%d) %s", - path, errsv, strerror (errsv)); - } else { - _LOGE ("sysctl: failed to open '%s': (%d) %s", - path, errsv, strerror (errsv)); + pathid = path; + + fd = open (path, O_WRONLY | O_TRUNC | O_CLOEXEC); + if (fd == -1) { + errsv = errno; + if (errsv == ENOENT) { + _LOGD ("sysctl: failed to open '%s': (%d) %s", + pathid, errsv, strerror (errsv)); + } else { + _LOGE ("sysctl: failed to open '%s': (%d) %s", + pathid, errsv, strerror (errsv)); + } + errno = errsv; + return FALSE; + } + } else { + fd = openat (dirfd, path, O_WRONLY | O_TRUNC | O_CLOEXEC); + if (fd == -1) { + errsv = errno; + if (errsv == ENOENT) { + _LOGD ("sysctl: failed to openat '%s': (%d) %s", + pathid, errsv, strerror (errsv)); + } else { + _LOGE ("sysctl: failed to openat '%s': (%d) %s", + pathid, errsv, strerror (errsv)); + } + errno = errsv; + return FALSE; } - errno = errsv; - return FALSE; } - _log_dbg_sysctl_set (platform, path, value); + _log_dbg_sysctl_set (platform, pathid, dirfd, 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 @@ -2635,7 +2664,7 @@ _nm_logging_clear_platform_logging_cache_impl (void) } static void -_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *contents) +_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char *contents) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const char *prev_value = NULL; @@ -2645,24 +2674,24 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *co 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); + prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, pathid); 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); + _LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", pathid, 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)); + g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents)); } } else { char *contents_escaped = g_strescape (contents, NULL); - _LOGD ("sysctl: reading '%s': '%s'", path, contents_escaped); + _LOGD ("sysctl: reading '%s': '%s'", pathid, contents_escaped); g_free (contents_escaped); - g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents)); + g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents)); } if ( !priv->sysctl_get_warned @@ -2672,43 +2701,42 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *co } } -#define _log_dbg_sysctl_get(platform, path, contents) \ +#define _log_dbg_sysctl_get(platform, pathid, contents) \ G_STMT_START { \ if (_LOGD_ENABLED ()) \ - _log_dbg_sysctl_get_impl (platform, path, contents); \ + _log_dbg_sysctl_get_impl (platform, pathid, contents); \ } G_STMT_END static char * -sysctl_get (NMPlatform *platform, const char *path) +sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *path) { nm_auto_pop_netns NMPNetns *netns = NULL; 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, "/../")); + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - if (!nm_platform_netns_push (platform, &netns)) - return NULL; + if (dirfd < 0) { + if (!nm_platform_netns_push (platform, &netns)) + return NULL; + pathid = path; + } - if (!g_file_get_contents (path, &contents, NULL, &error)) { + if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) { /* 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_NODEV) || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) - _LOGD ("error reading %s: %s", path, error->message); + _LOGD ("error reading %s: %s", pathid, error->message); else - _LOGE ("error reading %s: %s", path, error->message); + _LOGE ("error reading %s: %s", pathid, error->message); g_clear_error (&error); return NULL; } g_strstrip (contents); - _log_dbg_sysctl_get (platform, path, contents); + _log_dbg_sysctl_get (platform, pathid, contents); return contents; } @@ -2763,8 +2791,7 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - /* we raise the signals inside the namespace of the NMPlatform instance. */ - _assert_netns_current (platform); + ASSERT_NETNS_CURRENT (platform); switch (cache_op) { case NMP_CACHE_OPS_ADDED: @@ -4428,10 +4455,6 @@ static gboolean link_supports_carrier_detect (NMPlatform *platform, int ifindex) { nm_auto_pop_netns NMPNetns *netns = NULL; - const char *name = nm_platform_link_get_name (platform, ifindex); - - if (!name) - return FALSE; if (!nm_platform_netns_push (platform, &netns)) return FALSE; @@ -4440,7 +4463,7 @@ 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 nmp_utils_ethtool_supports_carrier_detect (name) || nmp_utils_mii_supports_carrier_detect (name); + return nmp_utils_ethtool_supports_carrier_detect (ifindex) || nmp_utils_mii_supports_carrier_detect (ifindex); } static gboolean @@ -4458,7 +4481,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) if (!nm_platform_netns_push (platform, &netns)) return FALSE; - return nmp_utils_ethtool_supports_vlans (obj->link.name); + return nmp_utils_ethtool_supports_vlans (ifindex); } static NMPlatformError @@ -4526,7 +4549,7 @@ link_get_permanent_address (NMPlatform *platform, if (!nm_platform_netns_push (platform, &netns)) return FALSE; - return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length); + return nmp_utils_ethtool_get_permanent_address (ifindex, buf, length); } static gboolean @@ -4555,44 +4578,27 @@ nla_put_failure: static char * link_get_physical_port_id (NMPlatform *platform, int ifindex) { - const char *ifname; - char *path, *id; + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; - ifname = nm_platform_link_get_name (platform, ifindex); - if (!ifname) + dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname_verified); + if (dirfd < 0) return NULL; - - ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname); - - path = g_strdup_printf ("/sys/class/net/%s/phys_port_id", ifname); - id = sysctl_get (platform, path); - g_free (path); - - return id; + return sysctl_get (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "phys_port_id")); } static guint link_get_dev_id (NMPlatform *platform, int ifindex) { - const char *ifname; - gs_free char *path = NULL, *id = NULL; - gint64 int_val; - - ifname = nm_platform_link_get_name (platform, ifindex); - if (!ifname) - return 0; - - ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname); + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; - path = g_strdup_printf ("/sys/class/net/%s/dev_id", ifname); - id = sysctl_get (platform, path); - if (!id || !*id) + dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname_verified); + if (dirfd < 0) return 0; - - /* Value is reported as hex */ - int_val = _nm_utils_ascii_str_to_int64 (id, 16, 0, G_MAXUINT16, 0); - - return errno ? 0 : (int) int_val; + return nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "dev_id"), + 16, 0, G_MAXUINT16, 0); } static int @@ -5161,7 +5167,7 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, tap ? "tap" : "tun", name, owner, group); - fd = open ("/dev/net/tun", O_RDWR); + fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd < 0) return FALSE; @@ -5250,9 +5256,9 @@ _infiniband_partition_action (NMPlatform *platform, const NMPlatformLink **out_link) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPObject *obj_parent; + nm_auto_close int dirfd = -1; + char ifname_parent[IFNAMSIZ]; const NMPObject *obj; - char path[NM_STRLEN ("/sys/class/net/%s/%s") + IFNAMSIZ + 100]; char id[20]; char name[IFNAMSIZ]; gboolean success; @@ -5260,20 +5266,18 @@ _infiniband_partition_action (NMPlatform *platform, nm_assert (NM_IN_SET (action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD)); nm_assert (p_key > 0 && p_key <= 0xffff && p_key != 0x8000); - obj_parent = nmp_cache_lookup_link (priv->cache, parent); - if (!obj_parent || !obj_parent->link.name[0]) { + dirfd = nm_platform_sysctl_open_netdir (platform, parent, ifname_parent); + if (dirfd < 0) { errno = ENOENT; return FALSE; } - nm_sprintf_buf (path, - "/sys/class/net/%s/%s", - NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name), - (action == INFINIBAND_ACTION_CREATE_CHILD - ? "create_child" - : "delete_child")); nm_sprintf_buf (id, "0x%04x", p_key); - success = nm_platform_sysctl_set (platform, path, id); + if (action == INFINIBAND_ACTION_CREATE_CHILD) + success = nm_platform_sysctl_set (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_parent, "create_child"), id); + else + success = nm_platform_sysctl_set (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_parent, "delete_child"), id); + if (!success) { if ( action == INFINIBAND_ACTION_DELETE_CHILD && errno == ENODEV) @@ -5281,7 +5285,7 @@ _infiniband_partition_action (NMPlatform *platform, return FALSE; } - nm_utils_new_infiniband_name (name, obj_parent->link.name, p_key); + nm_utils_new_infiniband_name (name, ifname_parent, p_key); do_request_link (platform, 0, name); if (action == INFINIBAND_ACTION_DELETE_CHILD) @@ -5515,7 +5519,7 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex) return FALSE; if (type == NM_LINK_TYPE_ETHERNET) - return nmp_utils_ethtool_get_wake_on_lan (nm_platform_link_get_name (platform, ifindex)); + return nmp_utils_ethtool_get_wake_on_lan (ifindex); else if (type == NM_LINK_TYPE_WIFI) { WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); @@ -5535,14 +5539,17 @@ link_get_driver_info (NMPlatform *platform, char **out_fw_version) { nm_auto_pop_netns NMPNetns *netns = NULL; + NMPUtilsEthtoolDriverInfo driver_info; if (!nm_platform_netns_push (platform, &netns)) return FALSE; - return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex), - out_driver_name, - out_driver_version, - out_fw_version); + if (!nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) + return FALSE; + NM_SET_OUT (out_driver_name, g_strdup (driver_info.driver)); + NM_SET_OUT (out_driver_version, g_strdup (driver_info.version)); + NM_SET_OUT (out_fw_version, g_strdup (driver_info.fw_version)); + return TRUE; } /*****************************************************************************/ diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 9ef0707d58..8b6994e20e 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -31,15 +31,39 @@ #include <linux/mii.h> #include <linux/version.h> #include <linux/rtnetlink.h> +#include <fcntl.h> #include "nm-utils.h" #include "nm-setting-wired.h" #include "nm-core-utils.h" +extern char *if_indextoname (unsigned int __ifindex, char *__ifname); + /****************************************************************** * ethtool ******************************************************************/ + +NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, + NM_UTILS_ENUM2STR (ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), + NM_UTILS_ENUM2STR (ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"), + NM_UTILS_ENUM2STR (ETHTOOL_GLINK, "ETHTOOL_GLINK"), + NM_UTILS_ENUM2STR (ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"), + NM_UTILS_ENUM2STR (ETHTOOL_GSET, "ETHTOOL_GSET"), + NM_UTILS_ENUM2STR (ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), + NM_UTILS_ENUM2STR (ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), + NM_UTILS_ENUM2STR (ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), + NM_UTILS_ENUM2STR (ETHTOOL_GWOL, "ETHTOOL_GWOL"), + NM_UTILS_ENUM2STR (ETHTOOL_SSET, "ETHTOOL_SSET"), + NM_UTILS_ENUM2STR (ETHTOOL_SWOL, "ETHTOOL_SWOL"), +); + +static const char * +_ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) +{ + return _ethtool_cmd_to_string (*((guint32 *) edata), buf, len); +} + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) #define ethtool_cmd_speed(pedata) ((pedata)->speed) @@ -47,55 +71,84 @@ G_STMT_START { (pedata)->speed = (guint16) (speed); } G_STMT_END #endif - static gboolean -ethtool_get (const char *name, gpointer edata) +ethtool_get (int ifindex, gpointer edata) { - struct ifreq ifr; - int fd; + char ifname[IFNAMSIZ]; + char sbuf[50]; - if (!name || !*name) - return FALSE; + nm_assert (ifindex > 0); - if (!nmp_utils_device_exists (name)) + /* ethtool ioctl API uses the ifname to refer to an interface. That is racy + * as interfaces can be renamed *sigh*. + * + * Note that we anyway have to verify whether the interface exists, before + * calling ioctl for a non-existing ifname. This is to prevent autoloading + * of kernel modules *sigh*. + * Thus, as we anyway verify the existence of ifname before doing the call, + * go one step further and lookup the ifname everytime anew. + * + * This does not solve the renaming race, but it minimizes the time for + * the race to happen as much as possible. */ + + if (!if_indextoname (ifindex, ifname)) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: request fails resolving ifindex: %s", + ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + g_strerror (errno)); return FALSE; + } - /* nmp_utils_device_exists() already errors out if @name is invalid. */ - nm_assert (strlen (name) < IFNAMSIZ); - - memset (&ifr, 0, sizeof (ifr)); - nm_utils_ifname_cpy (ifr.ifr_name, name); - ifr.ifr_data = edata; + { + nm_auto_close int fd = -1; + struct ifreq ifr = { + .ifr_data = edata, + }; + + memcpy (ifr.ifr_name, ifname, sizeof (ifname)); + + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed creating socket for ioctl: %s", + ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + ifname, + g_strerror (errno)); + return FALSE; + } - 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_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %s", + ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + ifname, + strerror (errno)); + return FALSE; + } - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - nm_log_dbg (LOGD_PLATFORM, "ethtool: Request failed: %s", strerror (errno)); - close (fd); - return FALSE; + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", + ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + ifname); + return TRUE; } - - close (fd); - return TRUE; } static int -ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) +ethtool_get_stringset_index (int ifindex, int stringset_id, const char *string) { gs_free struct ethtool_sset_info *info = NULL; gs_free struct ethtool_gstrings *strings = NULL; guint32 len, i; + g_return_val_if_fail (ifindex > 0, -1); + 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)) + if (!ethtool_get (ifindex, info)) return -1; if (!info->sset_mask) return -1; @@ -106,7 +159,7 @@ ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *s strings->cmd = ETHTOOL_GSTRINGS; strings->string_set = stringset_id; strings->len = len; - if (!ethtool_get (ifname, strings)) + if (!ethtool_get (ifindex, strings)) return -1; for (i = 0; i < len; i++) { @@ -118,32 +171,30 @@ ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *s } gboolean -nmp_utils_ethtool_get_driver_info (const char *ifname, - char **out_driver_name, - char **out_driver_version, - char **out_fw_version) +nmp_utils_ethtool_get_driver_info (int ifindex, + NMPUtilsEthtoolDriverInfo *data) { - 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; + struct ethtool_drvinfo *drvinfo; + G_STATIC_ASSERT (sizeof (*data) == sizeof (*drvinfo)); + G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, driver) == offsetof (struct ethtool_drvinfo, driver)); + G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, version) == offsetof (struct ethtool_drvinfo, version)); + G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, fw_version) == offsetof (struct ethtool_drvinfo, fw_version)); + G_STATIC_ASSERT (sizeof (data->driver) == sizeof (drvinfo->driver)); + G_STATIC_ASSERT (sizeof (data->version) == sizeof (drvinfo->version)); + G_STATIC_ASSERT (sizeof (data->fw_version) == sizeof (drvinfo->fw_version)); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (data, FALSE); + + drvinfo = (struct ethtool_drvinfo *) data; + + memset (drvinfo, 0, sizeof (*drvinfo)); + drvinfo->cmd = ETHTOOL_GDRVINFO; + return ethtool_get (ifindex, drvinfo); } gboolean -nmp_utils_ethtool_get_permanent_address (const char *ifname, +nmp_utils_ethtool_get_permanent_address (int ifindex, guint8 *buf, size_t *length) { @@ -153,14 +204,13 @@ nmp_utils_ethtool_get_permanent_address (const char *ifname, } edata; guint i; - if (!ifname) - return FALSE; + g_return_val_if_fail (ifindex > 0, FALSE); memset (&edata, 0, sizeof (edata)); edata.e.cmd = ETHTOOL_GPERMADDR; edata.e.size = NM_UTILS_HWADDR_LEN_MAX; - if (!ethtool_get (ifname, &edata.e)) + if (!ethtool_get (ifindex, &edata.e)) return FALSE; if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) @@ -187,29 +237,30 @@ not_all_0or1: } gboolean -nmp_utils_ethtool_supports_carrier_detect (const char *ifname) +nmp_utils_ethtool_supports_carrier_detect (int ifindex) { struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; + g_return_val_if_fail (ifindex > 0, FALSE); + /* 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); + return ethtool_get (ifindex, &edata); } gboolean -nmp_utils_ethtool_supports_vlans (const char *ifname) +nmp_utils_ethtool_supports_vlans (int ifindex) { gs_free struct ethtool_gfeatures *features = NULL; int idx, block, bit, size; - if (!ifname) - return FALSE; + g_return_val_if_fail (ifindex > 0, FALSE); - idx = ethtool_get_stringset_index (ifname, ETH_SS_FEATURES, "vlan-challenged"); + idx = ethtool_get_stringset_index (ifindex, ETH_SS_FEATURES, "vlan-challenged"); if (idx == -1) { - nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %s?", ifname); + nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %d?", ifindex); return FALSE; } @@ -221,54 +272,52 @@ nmp_utils_ethtool_supports_vlans (const char *ifname) features->cmd = ETHTOOL_GFEATURES; features->size = size; - if (!ethtool_get (ifname, features)) + if (!ethtool_get (ifindex, features)) return FALSE; return !(features->features[block].active & (1 << bit)); } int -nmp_utils_ethtool_get_peer_ifindex (const char *ifname) +nmp_utils_ethtool_get_peer_ifindex (int ifindex) { gs_free struct ethtool_stats *stats = NULL; int peer_ifindex_stat; - if (!ifname) - return 0; + g_return_val_if_fail (ifindex > 0, 0); - peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); + peer_ifindex_stat = ethtool_get_stringset_index (ifindex, 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); + nm_log_dbg (LOGD_PLATFORM, "ethtool: peer_ifindex stat for %d does not exist?", ifindex); 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)) + if (!ethtool_get (ifindex, stats)) return 0; return stats->data[peer_ifindex_stat]; } gboolean -nmp_utils_ethtool_get_wake_on_lan (const char *ifname) +nmp_utils_ethtool_get_wake_on_lan (int ifindex) { struct ethtool_wolinfo wol; - if (!ifname) - return FALSE; + g_return_val_if_fail (ifindex > 0, FALSE); memset (&wol, 0, sizeof (wol)); wol.cmd = ETHTOOL_GWOL; - if (!ethtool_get (ifname, &wol)) + if (!ethtool_get (ifindex, &wol)) return FALSE; return wol.wolopts != 0; } gboolean -nmp_utils_ethtool_get_link_settings (const char *ifname, +nmp_utils_ethtool_get_link_settings (int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex) @@ -277,7 +326,9 @@ nmp_utils_ethtool_get_link_settings (const char *ifname, .cmd = ETHTOOL_GSET, }; - if (!ethtool_get (ifname, &edata)) + g_return_val_if_fail (ifindex > 0, FALSE); + + if (!ethtool_get (ifindex, &edata)) return FALSE; if (out_autoneg) @@ -311,14 +362,19 @@ nmp_utils_ethtool_get_link_settings (const char *ifname, } gboolean -nmp_utils_ethtool_set_link_settings (const char *ifname, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex) +nmp_utils_ethtool_set_link_settings (int ifindex, + gboolean autoneg, + guint32 speed, + NMPlatformLinkDuplexType duplex) { struct ethtool_cmd edata = { .cmd = ETHTOOL_GSET, }; + g_return_val_if_fail (ifindex > 0, FALSE); + /* retrieve first current settings */ - if (!ethtool_get (ifname, &edata)) + if (!ethtool_get (ifindex, &edata)) return FALSE; /* then change the needed ones */ @@ -346,16 +402,18 @@ nmp_utils_ethtool_set_link_settings (const char *ifname, gboolean autoneg, guint } } - return ethtool_get (ifname, &edata); + return ethtool_get (ifindex, &edata); } gboolean -nmp_utils_ethtool_set_wake_on_lan (const char *ifname, +nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password) { struct ethtool_wolinfo wol_info = { }; + g_return_val_if_fail (ifindex > 0, FALSE); + if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) return TRUE; @@ -386,7 +444,7 @@ nmp_utils_ethtool_set_wake_on_lan (const char *ifname, wol_info.wolopts |= WAKE_MAGICSECURE; } - return ethtool_get (ifname, &wol_info); + return ethtool_get (ifindex, &wol_info); } /****************************************************************** @@ -394,51 +452,45 @@ nmp_utils_ethtool_set_wake_on_lan (const char *ifname, ******************************************************************/ gboolean -nmp_utils_mii_supports_carrier_detect (const char *ifname) +nmp_utils_mii_supports_carrier_detect (int ifindex) { - int fd, errsv; + char ifname[IFNAMSIZ]; + nm_auto_close int fd = -1; struct ifreq ifr; struct mii_ioctl_data *mii; - gboolean supports_mii = FALSE; - if (!ifname) - return FALSE; + g_return_val_if_fail (ifindex > 0, FALSE); - if (!nmp_utils_device_exists (ifname)) + if (!if_indextoname (ifindex, ifname)) { + nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: request fails resolving ifindex: %s", ifindex, g_strerror (errno)); return FALSE; + } - fd = socket (PF_INET, SOCK_DGRAM, 0); + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) { - nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname); + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: couldn't open control socket: %s", ifindex, ifname, g_strerror (errno)); return FALSE; } memset (&ifr, 0, sizeof (struct ifreq)); - nm_utils_ifname_cpy (ifr.ifr_name, ifname); + memcpy (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; + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, ifname, strerror (errno)); + return FALSE; } /* 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); + if (ioctl (fd, SIOCGMIIREG, &ifr) != 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, ifname, strerror (errno)); + return FALSE; } -out: - close (fd); - nm_log_dbg (LOGD_PLATFORM, "mii: MII %s supported (%s)", supports_mii ? "is" : "not", ifname); - return supports_mii; + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, ifname, mii->val_out); + return TRUE; } /****************************************************************** @@ -486,22 +538,6 @@ out: * utils *****************************************************************************/ -gboolean -nmp_utils_device_exists (const char *name) -{ -#define SYS_CLASS_NET "/sys/class/net/" - char sysdir[NM_STRLEN (SYS_CLASS_NET) + IFNAMSIZ]; - - if ( !name - || strlen (name) >= IFNAMSIZ - || !nm_utils_is_valid_path_component (name)) - g_return_val_if_reached (FALSE); - - memcpy (sysdir, SYS_CLASS_NET, NM_STRLEN (SYS_CLASS_NET)); - nm_utils_ifname_cpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], name); - return g_file_test (sysdir, G_FILE_TEST_EXISTS); -} - NMIPConfigSource nmp_utils_ip_config_source_from_rtprot (guint8 rtprot) { @@ -623,3 +659,83 @@ nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize return buf; } +/** + * nmp_utils_sysctl_open_netdir: + * @ifindex: the ifindex for which to open "/sys/class/net/%s" + * @ifname_guess: (allow-none): optional argument, if present used as initial + * guess as the current name for @ifindex. If guessed right, + * it saves an addtional if_indextoname() call. + * @out_ifname: (allow-none): if present, must be at least IFNAMSIZ + * characters. On success, this will contain the actual ifname + * found while opening the directory. + * + * Returns: a negative value on failure, on success returns the open fd + * to the "/sys/class/net/%s" directory for @ifindex. + */ +int +nmp_utils_sysctl_open_netdir (int ifindex, + const char *ifname_guess, + char *out_ifname) +{ + #define SYS_CLASS_NET "/sys/class/net/" + const char *ifname = ifname_guess; + char ifname_buf_last_try[IFNAMSIZ]; + char ifname_buf[IFNAMSIZ]; + guint try_count = 0; + char sysdir[NM_STRLEN (SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET; + char fd_buf[256]; + ssize_t nn; + + g_return_val_if_fail (ifindex >= 0, -1); + + ifname_buf_last_try[0] = '\0'; + + for (try_count = 0; try_count < 10; try_count++, ifname = NULL) { + nm_auto_close int fd_dir = -1; + nm_auto_close int fd_ifindex = -1; + int fd; + + if (!ifname) { + ifname = if_indextoname (ifindex, ifname_buf); + if (!ifname) + return -1; + } + + nm_assert (nm_utils_iface_valid_name (ifname)); + + if (g_strlcpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], ifname, IFNAMSIZ) >= IFNAMSIZ) + g_return_val_if_reached (-1); + + /* we only retry, if the name changed since previous attempt. + * Hence, it is extremely unlikely that this loop runes until the + * end of the @try_count. */ + if (nm_streq (ifname, ifname_buf_last_try)) + return -1; + strcpy (ifname_buf_last_try, ifname); + + fd_dir = open (sysdir, O_DIRECTORY | O_CLOEXEC); + if (fd_dir < 0) + continue; + + fd_ifindex = openat (fd_dir, "ifindex", O_CLOEXEC); + if (fd_ifindex < 0) + continue; + + nn = nm_utils_fd_read_loop (fd_ifindex, fd_buf, sizeof (fd_buf) - 2, FALSE); + if (nn <= 0) + continue; + fd_buf[nn] = '\0'; + + if (ifindex != _nm_utils_ascii_str_to_int64 (fd_buf, 10, 1, G_MAXINT, -1)) + continue; + + if (out_ifname) + strcpy (out_ifname, ifname); + + fd = fd_dir; + fd_dir = -1; + return fd; + } + + return -1; +} diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index 4b4868c1e8..ea9607e6a3 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -27,38 +27,55 @@ #include "nm-setting-wired.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_set_wake_on_lan (const char *ifname, NMSettingWiredWakeOnLan wol, +const char *nmp_utils_ethtool_get_driver (int ifindex); +gboolean nmp_utils_ethtool_supports_carrier_detect (int ifindex); +gboolean nmp_utils_ethtool_supports_vlans (int ifindex); +int nmp_utils_ethtool_get_peer_ifindex (int ifindex); +gboolean nmp_utils_ethtool_get_wake_on_lan (int ifindex); +gboolean nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password); -gboolean nmp_utils_ethtool_get_link_settings (const char *ifname, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); -gboolean nmp_utils_ethtool_set_link_settings (const char *ifname, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); +gboolean nmp_utils_ethtool_get_link_settings (int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +gboolean nmp_utils_ethtool_set_link_settings (int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); -gboolean nmp_utils_ethtool_get_driver_info (const char *ifname, - char **out_driver_name, - char **out_driver_version, - char **out_fw_version); +typedef struct { + /* We don't want to include <linux/ethtool.h> in header files, + * thus create a ABI compatible version of struct ethtool_drvinfo.*/ + guint32 _private_cmd; + char driver[32]; + char version[32]; + char fw_version[32]; + char _private_bus_info[32]; + char _private_erom_version[32]; + char _private_reserved2[12]; + guint32 _private_n_priv_flags; + guint32 _private_n_stats; + guint32 _private_testinfo_len; + guint32 _private_eedump_len; + guint32 _private_regdump_len; +} NMPUtilsEthtoolDriverInfo; -gboolean nmp_utils_ethtool_get_permanent_address (const char *ifname, +gboolean nmp_utils_ethtool_get_driver_info (int ifindex, + NMPUtilsEthtoolDriverInfo *data); + +gboolean nmp_utils_ethtool_get_permanent_address (int ifindex, guint8 *buf, size_t *length); -gboolean nmp_utils_mii_supports_carrier_detect (const char *ifname); +gboolean nmp_utils_mii_supports_carrier_detect (int ifindex); const char *nmp_utils_udev_get_driver (GUdevDevice *device); -gboolean nmp_utils_device_exists (const char *name); - NMIPConfigSource nmp_utils_ip_config_source_from_rtprot (guint8 rtprot) _nm_const; guint8 nmp_utils_ip_config_source_coerce_to_rtprot (NMIPConfigSource source) _nm_const; NMIPConfigSource nmp_utils_ip_config_source_coerce_from_rtprot (NMIPConfigSource source) _nm_const; NMIPConfigSource nmp_utils_ip_config_source_round_trip_rtprot (NMIPConfigSource source) _nm_const; const char * nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize len); +int nmp_utils_sysctl_open_netdir (int ifindex, + const char *ifname_guess, + char *out_ifname); + #endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index af574a355c..d0b65adf08 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -266,8 +266,39 @@ nm_platform_process_events (NMPlatform *self) /*****************************************************************************/ /** + * nm_platform_sysctl_open_netdir: + * @self: platform instance + * @ifindex: the ifindex for which to open /sys/class/net/%s + * @out_ifname: optional output argument of the found ifname. + * + * Wraps nmp_utils_sysctl_open_netdir() by first changing into the right + * network-namespace. + * + * Returns: on success, the open file descriptor to the /sys/class/net/%s + * directory. + */ +int +nm_platform_sysctl_open_netdir (NMPlatform *self, int ifindex, char *out_ifname) +{ + const char*ifname_guess; + _CHECK_SELF_NETNS (self, klass, netns, -1); + + g_return_val_if_fail (ifindex > 0, -1); + + /* we don't have an @ifname_guess argument to make the API nicer. + * But still do a cache-lookup first. Chances are good that we have + * the right ifname cached and save if_indextoname() */ + ifname_guess = nm_platform_link_get_name (self, ifindex); + + return nmp_utils_sysctl_open_netdir (ifindex, ifname_guess, out_ifname); +} + +/** * nm_platform_sysctl_set: * @self: platform instance + * @pathid: if @dirfd is present, this must be the full path that is looked up. + * It is required for logging. + * @dirfd: optional file descriptor for parent directory for openat() * @path: Absolute option path * @value: Value to write * @@ -278,14 +309,14 @@ nm_platform_process_events (NMPlatform *self) * Returns: %TRUE on success. */ gboolean -nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value) +nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (path, FALSE); g_return_val_if_fail (value, FALSE); - return klass->sysctl_set (self, path, value); + return klass->sysctl_set (self, pathid, dirfd, path, value); } gboolean @@ -305,7 +336,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, return FALSE; path = nm_utils_ip6_property_path (iface, "hop_limit"); - cur = nm_platform_sysctl_get_int_checked (self, path, 10, 1, G_MAXINT32, -1); + cur = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 1, G_MAXINT32, -1); /* only allow increasing the hop-limit to avoid DOS by an attacker * setting a low hop-limit (CVE-2015-2924, rh#1209902) */ @@ -316,7 +347,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, char svalue[20]; sprintf (svalue, "%d", value); - nm_platform_sysctl_set (self, path, svalue); + nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), svalue); } return TRUE; @@ -325,23 +356,29 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, /** * nm_platform_sysctl_get: * @self: platform instance + * @dirfd: if non-negative, used to lookup the path via openat(). + * @pathid: if @dirfd is present, this must be the full path that is looked up. + * It is required for logging. * @path: Absolute path to sysctl * * Returns: (transfer full): Contents of the virtual sysctl file. */ char * -nm_platform_sysctl_get (NMPlatform *self, const char *path) +nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, const char *path) { _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (path, NULL); - return klass->sysctl_get (self, path); + return klass->sysctl_get (self, pathid, dirfd, path); } /** * nm_platform_sysctl_get_int32: * @self: platform instance + * @pathid: if @dirfd is present, this must be the full path that is looked up. + * It is required for logging. + * @dirfd: if non-negative, used to lookup the path via openat(). * @path: Absolute path to sysctl * @fallback: default value, if the content of path could not be read * as decimal integer. @@ -351,14 +388,17 @@ nm_platform_sysctl_get (NMPlatform *self, const char *path) * value, on success %errno will be set to zero. */ gint32 -nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback) +nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, const char *path, gint32 fallback) { - return nm_platform_sysctl_get_int_checked (self, path, 10, G_MININT32, G_MAXINT32, fallback); + return nm_platform_sysctl_get_int_checked (self, pathid, dirfd, path, 10, G_MININT32, G_MAXINT32, fallback); } /** * nm_platform_sysctl_get_int_checked: * @self: platform instance + * @pathid: if @dirfd is present, this must be the full path that is looked up. + * It is required for logging. + * @dirfd: if non-negative, used to lookup the path via openat(). * @path: Absolute path to sysctl * @base: base of numeric conversion * @min: minimal value that is still valid @@ -373,7 +413,7 @@ nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallbac * (inclusive) or @fallback. */ gint64 -nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint base, gint64 min, gint64 max, gint64 fallback) +nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int dirfd, const char *path, guint base, gint64 min, gint64 max, gint64 fallback) { char *value = NULL; gint32 ret; @@ -383,7 +423,7 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint ba g_return_val_if_fail (path, fallback); if (path) - value = nm_platform_sysctl_get (self, path); + value = nm_platform_sysctl_get (self, pathid, dirfd, path); if (!value) { errno = EINVAL; @@ -1668,34 +1708,44 @@ nm_platform_link_tun_add (NMPlatform *self, /*****************************************************************************/ -static char * -link_option_path (NMPlatform *self, int master, const char *category, const char *option) +static gboolean +link_set_option (NMPlatform *self, int ifindex, const char *category, const char *option, const char *value) { - const char *name = nm_platform_link_get_name (self, master); - - if (!name || !category || !option) - return NULL; + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; + const char *path; - return g_strdup_printf ("/sys/class/net/%s/%s/%s", - NM_ASSERT_VALID_PATH_COMPONENT (name), - NM_ASSERT_VALID_PATH_COMPONENT (category), - NM_ASSERT_VALID_PATH_COMPONENT (option)); -} + if (!category || !option) + return FALSE; -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); + dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified); + if (dirfd < 0) + return FALSE; - return path && nm_platform_sysctl_set (self, path, value); + path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2, + "%s/%s", + category, option); + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path), value); } static char * -link_get_option (NMPlatform *self, int master, const char *category, const char *option) +link_get_option (NMPlatform *self, int ifindex, const char *category, const char *option) { - gs_free char *path = link_option_path (self, master, category, option); + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; + const char *path; + + if (!category || !option) + return NULL; + + dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified); + if (dirfd < 0) + return NULL; - return path ? nm_platform_sysctl_get (self, path) : NULL; + path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2, + "%s/%s", + category, option); + return nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path)); } static const char * @@ -1973,10 +2023,11 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self, int *out_p_key, const char **out_mode) { + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; const NMPlatformLnkInfiniband *plnk; const NMPlatformLink *plink; - const char *iface; - char *path, *contents; + char *contents; const char *mode; int p_key = 0; @@ -2000,15 +2051,13 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self, /* Could not get the link information via netlink. To support older kernels, * fallback to reading sysfs. */ - iface = NM_ASSERT_VALID_PATH_COMPONENT (plink->name); + dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified); + if (dirfd < 0) + return FALSE; - /* 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); + contents = nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode")); if (!contents) return FALSE; - if (strstr (contents, "datagram")) mode = "datagram"; else if (strstr (contents, "connected")) @@ -2017,13 +2066,7 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self, 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); + p_key = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "pkey"), 16, 0, 0xFFFF, -1); if (p_key < 0) return FALSE; @@ -2216,7 +2259,7 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe if (!nm_platform_netns_push (self, &netns)) return FALSE; - peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->name); + peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->ifindex); if (peer_ifindex <= 0) return FALSE; @@ -2226,57 +2269,53 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe } gboolean -nm_platform_link_tun_get_properties_ifname (NMPlatform *self, const char *ifname, NMPlatformTunProperties *props) +nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, const char *ifname_guess, NMPlatformTunProperties *props) { - char path[256]; - char *val; + nm_auto_close int dirfd = -1; + const char *ifname; + char ifname_verified[IFNAMSIZ]; + gint64 flags; gboolean success = TRUE; - _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props, FALSE); + /* ifname_guess is an optional argument to find a guess for the ifname corresponding to + * ifindex. */ + if (!ifname_guess) { + /* if NULL, obtain the guess from the platform cache. */ + ifname = nm_platform_link_get_name (self, ifindex); + } else if (!ifname_guess[0]) { + /* if empty, don't use a guess. That means to use if_indextoname(). */ + ifname = NULL; + } else + ifname = ifname_guess; + memset (props, 0, sizeof (*props)); props->owner = -1; props->group = -1; - if (!ifname || !nm_utils_iface_valid_name (ifname)) + dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified); + if (dirfd < 0) return FALSE; - nm_sprintf_buf (path, "/sys/class/net/%s/owner", ifname); - val = nm_platform_sysctl_get (self, 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 + ifname = ifname_verified; + + props->owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXINT64, -1); + if (errno) success = FALSE; - nm_sprintf_buf (path, "/sys/class/net/%s/group", ifname); - val = nm_platform_sysctl_get (self, 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 + props->group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXINT64, -1); + if (errno) success = FALSE; - nm_sprintf_buf (path, "/sys/class/net/%s/tun_flags", ifname); - val = nm_platform_sysctl_get (self, 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); + flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1); + if (flags >= 0) { + 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; @@ -2284,17 +2323,6 @@ nm_platform_link_tun_get_properties_ifname (NMPlatform *self, const char *ifname } gboolean -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 nm_platform_link_tun_get_properties_ifname (self, nm_platform_link_get_name (self, ifindex), props); -} - -gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps) { _CHECK_SELF (self, klass, FALSE); @@ -2479,27 +2507,33 @@ _to_string_ifa_flags (guint32 ifa_flags, char *buf, gsize size) /*****************************************************************************/ gboolean -nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, const char *ifname, NMSettingWiredWakeOnLan wol, const char *wol_password) +nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password) { _CHECK_SELF_NETNS (self, klass, netns, FALSE); - return nmp_utils_ethtool_set_wake_on_lan (ifname, wol, wol_password); + g_return_val_if_fail (ifindex > 0, FALSE); + + return nmp_utils_ethtool_set_wake_on_lan (ifindex, wol, wol_password); } gboolean -nm_platform_ethtool_set_link_settings (NMPlatform *self, const char *ifname, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex) +nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex) { _CHECK_SELF_NETNS (self, klass, netns, FALSE); - return nmp_utils_ethtool_set_link_settings (ifname, autoneg, speed, duplex); + g_return_val_if_fail (ifindex > 0, FALSE); + + return nmp_utils_ethtool_set_link_settings (ifindex, autoneg, speed, duplex); } gboolean -nm_platform_ethtool_get_link_settings (NMPlatform *self, const char *ifname, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex) +nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex) { _CHECK_SELF_NETNS (self, klass, netns, FALSE); - return nmp_utils_ethtool_get_link_settings (ifname, out_autoneg, out_speed, out_duplex); + g_return_val_if_fail (ifindex > 0, FALSE); + + return nmp_utils_ethtool_get_link_settings (ifindex, out_autoneg, out_speed, out_duplex); } /*****************************************************************************/ diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index a546e4eaec..55d7a6f20e 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -500,8 +500,8 @@ struct _NMPlatform { typedef struct { GObjectClass parent; - gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value); - char * (*sysctl_get) (NMPlatform *, const char *path); + gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); + char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); @@ -717,10 +717,24 @@ const char *nm_link_type_to_string (NMLinkType link_type); const char *_nm_platform_error_to_string (NMPlatformError error); #define nm_platform_error_to_string(error) NM_UTILS_LOOKUP_STR (_nm_platform_error_to_string, error) -gboolean nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value); -char *nm_platform_sysctl_get (NMPlatform *self, const char *path); -gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback); -gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint base, gint64 min, gint64 max, gint64 fallback); +#define NMP_SYSCTL_PATHID_ABSOLUTE(path) \ + ((const char *) NULL), -1, (path) + +#define NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname, path) \ + nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//\0") + IFNAMSIZ + strlen (path), \ + "net:/sys/class/net/%s/%s", (ifname), (path)), \ + (dirfd), (path) + +#define NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, path) \ + nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//"path"/\0") + IFNAMSIZ, \ + "net:/sys/class/net/%s/%s", (ifname), path), \ + (dirfd), (""path"") + +int nm_platform_sysctl_open_netdir (NMPlatform *self, int ifindex, char *out_ifname); +gboolean nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value); +char *nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, const char *path); +gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, const char *path, gint32 fallback); +gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int dirfd, const char *path, guint base, gint64 min, gint64 max, gint64 fallback); gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value); @@ -851,9 +865,7 @@ NMPlatformError nm_platform_link_infiniband_delete (NMPlatform *self, 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_link_tun_get_properties (NMPlatform *self, int ifindex, const char *ifname_guess, NMPlatformTunProperties *properties); gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid); @@ -980,8 +992,8 @@ 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); -gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, const char *ifname, NMSettingWiredWakeOnLan wol, const char *wol_password); -gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, const char *ifname, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); -gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, const char *ifname, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password); +gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); +gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c index f3704f0313..232c6efc9e 100644 --- a/src/platform/nmp-netns.c +++ b/src/platform/nmp-netns.c @@ -284,7 +284,7 @@ _netns_new (GError **error) int fd_net, fd_mnt; int errsv; - fd_net = open (PROC_SELF_NS_NET, O_RDONLY); + fd_net = open (PROC_SELF_NS_NET, O_RDONLY | O_CLOEXEC); if (fd_net == -1) { errsv = errno; g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -293,7 +293,7 @@ _netns_new (GError **error) return NULL; } - fd_mnt = open (PROC_SELF_NS_MNT, O_RDONLY); + fd_mnt = open (PROC_SELF_NS_MNT, O_RDONLY | O_CLOEXEC); if (fd_mnt == -1) { errsv = errno; g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -631,7 +631,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd) } if (out_fd) { - if ((fd = open (filename, O_RDONLY)) == -1) { + if ((fd = open (filename, O_RDONLY | O_CLOEXEC)) == -1) { errsv = errno; _LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv)); umount2 (filename, MNT_DETACH); diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h index eb33f2ed64..56c1e7e83a 100644 --- a/src/platform/nmp-netns.h +++ b/src/platform/nmp-netns.h @@ -53,8 +53,12 @@ int nmp_netns_get_fd_mnt (NMPNetns *self); static inline void _nm_auto_pop_netns (NMPNetns **p) { - if (*p) + if (*p) { + int errsv = errno; + nmp_netns_pop (*p); + errno = errsv; + } } #define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns))) diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 936c41f270..c0380ec567 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -125,7 +125,7 @@ _vlan_xgress_qos_mappings_cpy (guint *dst_n_map, /*****************************************************************************/ static const char * -_link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname) +_link_get_driver (GUdevDevice *udev_device, const char *kind, int ifindex) { const char *driver = NULL; @@ -140,14 +140,12 @@ _link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname if (kind) return kind; - if (ifname) { - char *d; + if (ifindex > 0) { + NMPUtilsEthtoolDriverInfo driver_info; - if (nmp_utils_ethtool_get_driver_info (ifname, &d, NULL, NULL)) { - driver = d && d[0] ? g_intern_string (d) : NULL; - g_free (d); - if (driver) - return driver; + if (nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) { + if (driver_info.driver[0]) + return g_intern_string (driver_info.driver); } } @@ -169,7 +167,7 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) if (obj->_link.netlink.is_in_netlink) { driver = _link_get_driver (obj->_link.udev.device, obj->link.kind, - obj->link.name); + obj->link.ifindex); if (obj->_link.udev.device) initialized = TRUE; else if (!use_udev) { diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index f23cec4606..89cbd188ed 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1407,7 +1407,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error) int pipefd_p2c[2]; ssize_t r; - e = pipe (pipefd_c2p); + e = pipe2 (pipefd_c2p, O_CLOEXEC); if (e != 0) { errsv = errno; g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -1415,7 +1415,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error) return FALSE; } - e = pipe (pipefd_p2c); + e = pipe2 (pipefd_p2c, O_CLOEXEC); if (e != 0) { errsv = errno; g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -1549,7 +1549,26 @@ nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name) nm_sprintf_buf (p, "/proc/%lu/ns/%s", (long unsigned) pid, ns_name); - return open(p, O_RDONLY); + return open(p, O_RDONLY | O_CLOEXEC); +} + +/*****************************************************************************/ + +void +nmtstp_netns_select_random (NMPlatform **platforms, gsize n_platforms, NMPNetns **netns) +{ + int i; + + g_assert (platforms); + g_assert (n_platforms && n_platforms <= G_MAXINT32); + g_assert (netns && !*netns); + for (i = 0; i < n_platforms; i++) + g_assert (NM_IS_PLATFORM (platforms[i])); + + i = nmtst_get_rand_int () % (n_platforms + 1); + if (i == 0) + return; + g_assert (nm_platform_netns_push (platforms[i - 1], netns)); } /*****************************************************************************/ @@ -1573,21 +1592,21 @@ unshare_user (void) /* 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"); + f = fopen ("/proc/self/setgroups", "we"); if (f) { fprintf (f, "deny"); fclose (f); } /* Map current UID to root in NS to be created. */ - f = fopen ("/proc/self/uid_map", "w"); + f = fopen ("/proc/self/uid_map", "we"); 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"); + f = fopen ("/proc/self/gid_map", "we"); if (!f) return FALSE; fprintf (f, "0 %d 1", gid); diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index a9f4959091..48a41de63a 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -53,6 +53,10 @@ int nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name); /*****************************************************************************/ +void nmtstp_netns_select_random (NMPlatform **platforms, gsize n_platforms, NMPNetns **netns); + +/*****************************************************************************/ + typedef struct { gulong handler_id; const char *name; diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index e3b55e73c8..c2b3de1391 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -398,26 +398,28 @@ test_software (NMLinkType link_type, const char *link_typename) accept_signal (link_changed); /* Set master option */ - switch (link_type) { - case NM_LINK_TYPE_BRIDGE: - if (nmtstp_is_sysfs_writable ()) { - g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "628")); - value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); - g_assert_cmpstr (value, ==, "628"); - g_free (value); - } - break; - case NM_LINK_TYPE_BOND: - 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); + if (nmtstp_is_root_test ()) { + switch (link_type) { + case NM_LINK_TYPE_BRIDGE: + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "628")); + value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); + g_assert_cmpstr (value, ==, "628"); + g_free (value); + } + break; + case NM_LINK_TYPE_BOND: + 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); + } + break; + default: + break; } - break; - default: - break; } /* Enslave and release */ @@ -1941,7 +1943,7 @@ _test_netns_check_skip (void) G_STMT_START { \ gs_free char *_val = NULL; \ \ - _val = nm_platform_sysctl_get (plat, path); \ + _val = nm_platform_sysctl_get (plat, NMP_SYSCTL_PATHID_ABSOLUTE (path)); \ g_assert_cmpstr (_val, ==, value); \ } G_STMT_END @@ -1954,6 +1956,7 @@ test_netns_general (gpointer fixture, gconstpointer test_data) char sbuf[100]; int i, j, k; gboolean ethtool_support; + NMPUtilsEthtoolDriverInfo driver_info; if (_test_netns_check_skip ()) return; @@ -2013,7 +2016,7 @@ test_netns_general (gpointer fixture, gconstpointer test_data) else path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"; } - g_assert (nm_platform_sysctl_set (pl, path, nm_sprintf_buf (sbuf, "%d", j))); + g_assert (nm_platform_sysctl_set (pl, NMP_SYSCTL_PATHID_ABSOLUTE (path), nm_sprintf_buf (sbuf, "%d", j))); _sysctl_assert_eq (pl, path, nm_sprintf_buf (sbuf, "%d", j)); } @@ -2024,9 +2027,8 @@ test_netns_general (gpointer fixture, gconstpointer test_data) * skip asserts that are known to fail. */ ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0; if (ethtool_support) { - g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL)); - g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL)); - g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL)); + g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); + g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0); g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a > /dev/null"), ==, 0); g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b 2> /dev/null"), !=, 0); @@ -2035,9 +2037,8 @@ test_netns_general (gpointer fixture, gconstpointer test_data) g_assert (nm_platform_netns_push (platform_2, &netns_tmp)); if (ethtool_support) { - g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL)); - g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL)); - g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL)); + g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); + g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0); g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a 2> /dev/null"), !=, 0); g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b > /dev/null"), ==, 0); @@ -2056,7 +2057,6 @@ test_netns_set_netns (gpointer fixture, gconstpointer test_data) gs_unref_object NMPlatform *platform_1 = NULL; gs_unref_object NMPlatform *platform_2 = NULL; nm_auto_pop_netns NMPNetns *netns_pop = NULL; - int i; if (_test_netns_check_skip ()) return; @@ -2065,9 +2065,7 @@ test_netns_set_netns (gpointer fixture, gconstpointer test_data) platforms[1] = platform_1 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform (); - i = nmtst_get_rand_int () % 4; - if (i != 3) - g_assert (nm_platform_netns_push (platforms[i], &netns_pop)); + nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop); #define LINK_MOVE_NAME "link-move" g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME)); @@ -2187,7 +2185,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data) _ADD_DUMMY (pl[i].platform, pl[i].device_name); - g_assert (nm_platform_sysctl_set (pl[i].platform, pl[i].sysctl_path, pl[i].sysctl_value)); + g_assert (nm_platform_sysctl_set (pl[i].platform, NMP_SYSCTL_PATHID_ABSOLUTE (pl[i].sysctl_path), pl[i].sysctl_value)); tmp = _get_current_namespace_id (CLONE_NEWNET); g_ptr_array_add (device_names, tmp); @@ -2294,9 +2292,7 @@ test_netns_bind_to_path (gpointer fixture, gconstpointer test_data) platforms[1] = platform_1 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform (); - i = nmtst_get_rand_int () % 4; - if (i != 3) - g_assert (nm_platform_netns_push (platforms[i], &netns_pop)); + nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop); g_assert_cmpint (mount ("tmpfs", P_VAR_RUN, "tmpfs", MS_NOATIME | MS_NODEV | MS_NOSUID, "mode=0755,size=32K"), ==, 0); g_assert_cmpint (mkdir (P_VAR_RUN_NETNS, 755), ==, 0); @@ -2325,6 +2321,173 @@ test_netns_bind_to_path (gpointer fixture, gconstpointer test_data) /*****************************************************************************/ +static void +test_sysctl_rename (void) +{ + NMPlatform *const PL = NM_PLATFORM_GET; + const char *const IFNAME[3] = { + "nm-dummy-0", + "nm-dummy-1", + "nm-dummy-2", + }; + int ifindex[G_N_ELEMENTS (IFNAME)] = { 0 }; + nm_auto_close int dirfd = -1; + int i; + char ifname_buf[IFNAMSIZ]; + char *s; + const NMPlatformLink *pllink; + + ifindex[0] = nmtstp_link_dummy_add (PL, -1, IFNAME[0])->ifindex; + ifindex[1] = nmtstp_link_dummy_add (PL, -1, IFNAME[1])->ifindex; + + s = (nmtst_get_rand_int () % 2) ? NULL : ifname_buf; + + if (nmtst_get_rand_int () % 2) { + /* bring the platform cache out of sync */ + nmtstp_run_command_check ("ip link set %s name %s", IFNAME[0], IFNAME[2]); + nm_platform_process_events (PL); + nmtstp_run_command_check ("ip link set %s name %s", IFNAME[2], IFNAME[0]); + + pllink = nm_platform_link_get_by_ifname (PL, IFNAME[2]); + g_assert (pllink && pllink->ifindex == ifindex[0]); + pllink = nm_platform_link_get_by_ifname (PL, IFNAME[0]); + g_assert (!pllink); + } + + /* open dirfd for IFNAME[0] */ + i = nmtst_get_rand_int () % (2 + G_N_ELEMENTS (IFNAME)); + if (i == 0) { + dirfd = nm_platform_sysctl_open_netdir (PL, + ifindex[0], + s); + } else { + const char *ifname_guess; + + /* provide a wrong or no guess. */ + ifname_guess = i == 1 ? NULL : IFNAME[i - 2]; + dirfd = nmp_utils_sysctl_open_netdir (ifindex[0], + ifname_guess, + s); + } + g_assert (dirfd >= 0); + if (s) + g_assert_cmpstr (s, ==, IFNAME[0]); + + /* possibly rename the interfaces. */ + switch (nmtst_get_rand_int () % 4) { + case 0: + break; + case 1: + nmtstp_run_command_check ("ip link set %s name %s", IFNAME[0], IFNAME[2]); + break; + case 2: + nmtstp_run_command_check ("ip link set %s name %s", IFNAME[0], IFNAME[2]); + nmtstp_run_command_check ("ip link set %s name %s", IFNAME[1], IFNAME[0]); + break; + } + + /* possibly, resync platform cache (should make no difference). */ + if (nmtst_get_rand_int () % 2) + nm_platform_process_events (PL); + + /* check that we still read the same file. */ + switch (nmtst_get_rand_int () % 2) { + case 0: { + gs_free char *c = NULL; + + if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024, &c, NULL, NULL) < 0) + g_assert_not_reached(); + g_assert_cmpint (ifindex[0], ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1)); + break; + } + case 1: { + g_assert_cmpint (ifindex[0], ==, (gint32) nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_NETDIR (dirfd, s ?: "<unknown>", "ifindex"), -1)); + break; + } + default: + g_assert_not_reached (); + } + + nm_platform_process_events (PL); + nmtstp_link_del (PL, -1, ifindex[0], NULL); + nmtstp_link_del (PL, -1, ifindex[1], NULL); +} + +/*****************************************************************************/ + +static void +test_sysctl_netns_switch (void) +{ + const char *const IFNAME = "nm-dummy-0"; + int ifindex, ifindex_tmp; + nm_auto_close int dirfd = -1; + char ifname_buf[IFNAMSIZ]; + char *s; + gs_unref_object NMPlatform *platform_0 = NULL; + gs_unref_object NMPlatform *platform_1 = NULL; + gs_unref_object NMPlatform *platform_2 = NULL; + nm_auto_pop_netns NMPNetns *netns_pop_1 = NULL; + nm_auto_pop_netns NMPNetns *netns_pop_2 = NULL; + nm_auto_pop_netns NMPNetns *netns_pop_3 = NULL; + NMPlatform *PL; + NMPlatform *platforms[3]; + + if (_test_netns_check_skip ()) + return; + + platforms[0] = platform_0 = nm_linux_platform_new (TRUE); + platforms[1] = platform_1 = _test_netns_create_platform (); + platforms[2] = platform_2 = _test_netns_create_platform (); + PL = platforms[nmtst_get_rand_int () % 3]; + + nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_1); + + ifindex = nmtstp_link_dummy_add (PL, FALSE, IFNAME)->ifindex; + + nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_2); + + s = (nmtst_get_rand_int () % 2) ? NULL : ifname_buf; + dirfd = nm_platform_sysctl_open_netdir (PL, + ifindex, + s); + g_assert (dirfd >= 0); + if (s) + g_assert_cmpstr (s, ==, IFNAME); + + nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_3); + + /* even if we switch to other namespaces, we can still lookup the path correctly, + * either using dirfd or via the platform instance (which switches namespace as needed). */ + { + gs_free char *c = NULL; + + if (nm_utils_file_get_contents (dirfd, "ifindex", 0, &c, NULL, NULL) < 0) + g_assert_not_reached(); + g_assert_cmpint (ifindex, ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1)); + } + g_assert_cmpint (ifindex, ==, (gint32) nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_NETDIR (dirfd, s ?: "<unknown>", "ifindex"), -1)); + g_assert_cmpint (ifindex, ==, (gint32) nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_ABSOLUTE (nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME)), -1)); + + /* accessing the path directly, only succeeds iff the current namespace happens to be the namespace + * in which we created the link. */ + { + gs_free char *c = NULL; + + if (nm_utils_file_get_contents (-1, nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME), 0, &c, NULL, NULL) < 0) + ifindex_tmp = -1; + else + ifindex_tmp = _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -2); + } + if (nmp_netns_get_current () == nm_platform_netns_get (PL)) + g_assert_cmpint (ifindex_tmp, ==, ifindex); + else + g_assert_cmpint (ifindex_tmp, ==, -1); + + nmtstp_link_del (PL, FALSE, ifindex, NULL); +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -2378,5 +2541,8 @@ _nmtstp_setup_tests (void) g_test_add_vtable ("/general/netns/set-netns", 0, NULL, _test_netns_setup, test_netns_set_netns, _test_netns_teardown); g_test_add_vtable ("/general/netns/push", 0, NULL, _test_netns_setup, test_netns_push, _test_netns_teardown); g_test_add_vtable ("/general/netns/bind-to-path", 0, NULL, _test_netns_setup, test_netns_bind_to_path, _test_netns_teardown); + + g_test_add_func ("/general/sysctl/rename", test_sysctl_rename); + g_test_add_func ("/general/sysctl/netns-switch", test_sysctl_netns_switch); } } diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c index 3936bbe37c..af8cf2dee6 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/wifi-utils-wext.c @@ -577,7 +577,7 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan) wext->parent.set_mesh_channel = wifi_wext_set_mesh_channel; wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid; - wext->fd = socket (PF_INET, SOCK_DGRAM, 0); + wext->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (wext->fd < 0) goto error; @@ -662,10 +662,17 @@ wifi_wext_is_wifi (const char *iface) struct iwreq iwr; gboolean is_wifi = FALSE; - if (!nmp_utils_device_exists (iface)) - return FALSE; + /* performing an ioctl on a non-existing name may cause the automatic + * loading of kernel modules, which should be avoided. + * + * Usually, we should thus make sure that an inteface with this name + * exists. + * + * Note that wifi_wext_is_wifi() has only one caller which just verified + * that an interface with this name exists. + */ - fd = socket (PF_INET, SOCK_DGRAM, 0); + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd >= 0) { nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface); if (ioctl (fd, SIOCGIWNAME, &iwr) == 0) diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index de858e499a..b8da02c15e 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -26,6 +26,7 @@ #include <sys/stat.h> #include <stdio.h> #include <string.h> +#include <fcntl.h> #include "wifi-utils-private.h" #include "wifi-utils-nl80211.h" @@ -34,6 +35,8 @@ #endif #include "nm-core-utils.h" +#include "platform/nm-platform-utils.h" + gpointer wifi_data_new (const char *iface, int ifindex, gsize len) { @@ -180,30 +183,19 @@ wifi_utils_deinit (WifiData *data) } gboolean -wifi_utils_is_wifi (const char *iface) +wifi_utils_is_wifi (int dirfd, const char *ifname) { - char phy80211_path[NM_STRLEN ("/sys/class/net/123456789012345/phy80211\0") + 100 /*safety*/]; - struct stat s; - - g_return_val_if_fail (iface != NULL, FALSE); - - nm_sprintf_buf (phy80211_path, - "/sys/class/net/%s/phy80211", - NM_ASSERT_VALID_PATH_COMPONENT (iface)); - nm_assert (strlen (phy80211_path) < sizeof (phy80211_path) - 1); + g_return_val_if_fail (dirfd >= 0, FALSE); - if ((stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR))) + if (faccessat (dirfd, "phy80211", F_OK, 0) == 0) return TRUE; - #if HAVE_WEXT - if (wifi_wext_is_wifi (iface)) + if (wifi_wext_is_wifi (ifname)) return TRUE; #endif - return FALSE; } - /* OLPC Mesh-only functions */ guint32 diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index 8e2b93f1f0..4fd5a80bbe 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -28,7 +28,7 @@ typedef struct WifiData WifiData; -gboolean wifi_utils_is_wifi (const char *iface); +gboolean wifi_utils_is_wifi (int dirfd, const char *ifname); WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan); diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index c8d43299d9..e33d9fe6fc 100644 --- a/src/ppp/nm-ppp-manager.c +++ b/src/ppp/nm-ppp-manager.c @@ -148,7 +148,7 @@ monitor_cb (gpointer user_data) if (errno != ENODEV) _LOGW ("could not read ppp stats: %s", strerror (errno)); } else { - g_signal_emit (manager, signals[STATS], 0, + g_signal_emit (manager, signals[STATS], 0, stats.p.ppp_ibytes, stats.p.ppp_obytes); } @@ -165,7 +165,7 @@ monitor_stats (NMPPPManager *manager) if (priv->monitor_fd >= 0) return; - priv->monitor_fd = socket (AF_INET, SOCK_DGRAM, 0); + priv->monitor_fd = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (priv->monitor_fd >= 0) { g_warn_if_fail (priv->monitor_id == 0); if (priv->monitor_id) diff --git a/src/settings/nm-inotify-helper.c b/src/settings/nm-inotify-helper.c index 44a102b059..a0432a25c0 100644 --- a/src/settings/nm-inotify-helper.c +++ b/src/settings/nm-inotify-helper.c @@ -144,7 +144,7 @@ init_inotify (NMInotifyHelper *self) GIOChannel *channel; guint source_id; - priv->ifd = inotify_init (); + priv->ifd = inotify_init1 (IN_CLOEXEC); if (priv->ifd == -1) { int errsv = errno; diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 22c942fce2..28989e1dfc 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -641,11 +641,11 @@ svOpenFileInternal (const char *name, gboolean create, GError **error) s->fd = -1; if (create) - s->fd = open (name, O_RDWR); /* NOT O_CREAT */ + s->fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */ if (!create || s->fd == -1) { /* try read-only */ - s->fd = open (name, O_RDONLY); /* NOT O_CREAT */ + s->fd = open (name, O_RDONLY | O_CLOEXEC); /* NOT O_CREAT */ if (s->fd == -1) errsv = errno; else @@ -1017,7 +1017,7 @@ svWriteFile (shvarFile *s, int mode, GError **error) if (s->modified) { if (s->fd == -1) - s->fd = open (s->fileName, O_WRONLY | O_CREAT, mode); + s->fd = open (s->fileName, O_WRONLY | O_CREAT | O_CLOEXEC, mode); if (s->fd == -1) { int errsv = errno; diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c index df6d248c03..e86f521647 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -117,7 +117,7 @@ _recursive_ifparser (const char *eni_file, int quiet) nm_log_warn (LOGD_SETTINGS, "interfaces file %s doesn't exist\n", eni_file); return; } - inp = fopen (eni_file, "r"); + inp = fopen (eni_file, "re"); if (inp == NULL) { if (!quiet) nm_log_warn (LOGD_SETTINGS, "Can't open %s\n", eni_file); diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 9338557b41..fbed7799b1 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -26,6 +26,7 @@ #include <netinet/ether.h> #include <sys/types.h> #include <sys/wait.h> +#include <fcntl.h> #include "NetworkManagerUtils.h" #include "nm-multi-index.h" @@ -173,7 +174,7 @@ test_nm_utils_kill_child_create_and_join_pgroup (void) int pipefd[2]; pid_t pgid; - err = pipe (pipefd); + err = pipe2 (pipefd, O_CLOEXEC); g_assert (err == 0); pgid = fork(); |