core: merge branch 'th/sysctl-ifname-race-bgo775613'

https://bugzilla.gnome.org/show_bug.cgi?id=775613
This commit is contained in:
Thomas Haller 2016-12-13 12:02:24 +01:00
commit 0d7bf7dee3
38 changed files with 1223 additions and 536 deletions

View file

@ -22,7 +22,9 @@
#ifndef __NM_MACROS_INTERNAL_H__ #ifndef __NM_MACROS_INTERNAL_H__
#define __NM_MACROS_INTERNAL_H__ #define __NM_MACROS_INTERNAL_H__
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include "nm-glib.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) #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 */ /* http://stackoverflow.com/a/11172679 */

View file

@ -137,7 +137,7 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
atm_index_path = g_strdup_printf ("/sys/class/atm/%s/atmindex", atm_index_path = g_strdup_printf ("/sys/class/atm/%s/atmindex",
NM_ASSERT_VALID_PATH_COMPONENT (ifname)); NM_ASSERT_VALID_PATH_COMPONENT (ifname));
atm_index = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, 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, 10, 0, G_MAXINT,
-1); -1);
if (atm_index < 0) { if (atm_index < 0) {

View file

@ -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->brfd == -1, FALSE);
g_return_val_if_fail (priv->nas_ifname != NULL, 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) { if (priv->brfd < 0) {
errsv = errno; errsv = errno;
_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv); _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); 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) { if (fd < 0) {
errsv = errno; errsv = errno;
_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv); _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", path = g_strdup_printf ("/sys/class/atm/%s/carrier",
NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self)))); 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); g_free (path);
if (carrier != -1) if (carrier != -1)

View file

@ -64,7 +64,7 @@ dun_connect (NMBluez5DunContext *context)
.channel = context->rfcomm_channel .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) { if (context->rfcomm_fd < 0) {
int errsv = errno; int errsv = errno;
error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, 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; context->rfcomm_id = devid;
snprintf (tty, ttylen, "/dev/rfcomm%d", 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) { if (try) {
g_usleep (100 * 1000); g_usleep (100 * 1000);
continue; continue;

View file

@ -208,7 +208,7 @@ _update_s390_subchannels (NMDeviceEthernet *self)
gs_free char *path = NULL, *value = NULL; gs_free char *path = NULL, *value = NULL;
path = g_strdup_printf ("%s/%s", parent_path, item); 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") if ( !strcmp (item, "portname")
&& !g_strcmp0 (value, "no portname required")) { && !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)) { &link_autoneg, &link_speed, &link_duplex)) {
_LOGW (LOGD_DEVICE, "set-link: unable to retrieve link negotiation"); _LOGW (LOGD_DEVICE, "set-link: unable to retrieve link negotiation");
return; return;
@ -852,7 +852,7 @@ link_negotiation_set (NMDevice *device)
} }
if (!nm_platform_ethtool_set_link_settings (NM_PLATFORM_GET, if (!nm_platform_ethtool_set_link_settings (NM_PLATFORM_GET,
nm_device_get_iface (device), nm_device_get_ifindex (device),
autoneg, autoneg,
speed, speed,
duplex)) { duplex)) {
@ -1243,7 +1243,7 @@ wake_on_lan_enable (NMDevice *device)
} }
wol = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; wol = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
found: 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); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
guint32 speed; 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; return;
if (priv->speed == speed) if (priv->speed == speed)
return; return;

View file

@ -77,10 +77,11 @@ get_generic_capabilities (NMDevice *device)
static NMActStageReturn static NMActStageReturn
act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
{ {
nm_auto_close int dirfd = -1;
NMActStageReturn ret; NMActStageReturn ret;
NMSettingInfiniband *s_infiniband; NMSettingInfiniband *s_infiniband;
char ifname_verified[IFNAMSIZ];
const char *transport_mode; const char *transport_mode;
char *mode_path;
gboolean ok, no_firmware = FALSE; gboolean ok, no_firmware = FALSE;
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); 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); transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband);
mode_path = g_strdup_printf ("/sys/class/net/%s/mode", dirfd = nm_platform_sysctl_open_netdir (NM_PLATFORM_GET, nm_device_get_ifindex (dev), ifname_verified);
NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (dev))); if (dirfd < 0) {
if (!g_file_test (mode_path, G_FILE_TEST_EXISTS)) {
g_free (mode_path);
if (!strcmp (transport_mode, "datagram")) if (!strcmp (transport_mode, "datagram"))
return NM_ACT_STAGE_RETURN_SUCCESS; return NM_ACT_STAGE_RETURN_SUCCESS;
else { else {
@ -109,8 +107,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
/* With some drivers the interface must be down to set transport mode */ /* With some drivers the interface must be down to set transport mode */
nm_device_take_down (dev, TRUE); nm_device_take_down (dev, TRUE);
ok = nm_platform_sysctl_set (NM_PLATFORM_GET, mode_path, transport_mode); ok = nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"), transport_mode);
g_free (mode_path);
nm_device_bring_up (dev, TRUE, &no_firmware); nm_device_bring_up (dev, TRUE, &no_firmware);
if (!ok) { if (!ok) {

View file

@ -80,7 +80,7 @@ reload_tun_properties (NMDeviceTun *self)
ifindex = nm_device_get_ifindex (NM_DEVICE (self)); ifindex = nm_device_get_ifindex (NM_DEVICE (self));
if (ifindex > 0) { 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); _LOGD (LOGD_DEVICE, "tun-properties: cannot loading tun properties from platform for ifindex %d", ifindex);
ifindex = 0; ifindex = 0;
} else if (g_strcmp0 (priv->mode, props.mode) != 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); 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."); _LOGW (LOGD_PLATFORM, "failed to get TUN interface info while updating connection.");
return; return;
} }

View file

@ -629,13 +629,13 @@ init_ip6_config_dns_priority (NMDevice *self, NMIP6Config *config)
gboolean gboolean
nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) 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 static guint32
nm_device_ipv6_sysctl_get_int32 (NMDevice *self, const char *property, gint32 fallback) 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 gboolean
@ -6771,7 +6771,7 @@ save_ip6_properties (NMDevice *self)
g_hash_table_remove_all (priv->ip6_saved_properties); g_hash_table_remove_all (priv->ip6_saved_properties);
for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { 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) { if (value) {
g_hash_table_insert (priv->ip6_saved_properties, g_hash_table_insert (priv->ip6_saved_properties,
(char *) ip6_properties_to_save[i], (char *) ip6_properties_to_save[i],
@ -6832,7 +6832,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
if (enable) { if (enable) {
/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
value = nm_platform_sysctl_get (NM_PLATFORM_GET, 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) if (g_strcmp0 (value, "0") == 0)
nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
g_free (value); 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. * 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 * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves
* the "default" entry untouched. */ * 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); return _ip6_privacy_clamp (ip6_privacy);
} }
@ -7406,14 +7406,14 @@ share_init (void)
char **iter; char **iter;
int errsv; 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; errsv = errno;
nm_log_err (LOGD_SHARING, "share: error enabling IPv4 forwarding: (%d) %s", nm_log_err (LOGD_SHARING, "share: error enabling IPv4 forwarding: (%d) %s",
errsv, strerror (errsv)); errsv, strerror (errsv));
return FALSE; 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; errsv = errno;
nm_log_err (LOGD_SHARING, "share: error enabling dynamic addresses: (%d) %s", nm_log_err (LOGD_SHARING, "share: error enabling dynamic addresses: (%d) %s",
errsv, strerror (errsv)); 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); method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { 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; errsv = errno;
_LOGE (LOGD_SHARING, "share: error enabling IPv6 forwarding: (%d) %s", errsv, strerror (errsv)); _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); nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);

View file

@ -350,7 +350,7 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
struct ifreq ifr = { }; struct ifreq ifr = { };
int fd, s; int fd, s;
fd = open ("/dev/net/tun", O_RDWR); fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
g_assert (fd >= 0); g_assert (fd >= 0);
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 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); g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
/* Bring the interface up */ /* Bring the interface up */
s = socket (AF_INET, SOCK_DGRAM, 0); s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
g_assert (s >= 0); g_assert (s >= 0);
ifr.ifr_flags |= IFF_UP; ifr.ifr_flags |= IFF_UP;
g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0); g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);

View file

@ -497,18 +497,18 @@ ppp_stats (NMPPPManager *ppp_manager,
static gboolean static gboolean
port_speed_is_zero (const char *port) port_speed_is_zero (const char *port)
{ {
struct termios options; struct termios options;
gs_fd_close int fd = -1; nm_auto_close int fd = -1;
fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY); fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return FALSE; return FALSE;
memset (&options, 0, sizeof (struct termios)); memset (&options, 0, sizeof (struct termios));
if (tcgetattr (fd, &options) != 0) if (tcgetattr (fd, &options) != 0)
return FALSE; return FALSE;
return cfgetospeed (&options) == B0; return cfgetospeed (&options) == B0;
} }
static NMActStageReturn static NMActStageReturn

View file

@ -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; errsv = errno;
g_set_error (error, g_set_error (error,
NM_MANAGER_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 (fd != -1) {
if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1) if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1)
immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL); immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL);

View file

@ -95,7 +95,7 @@ nm_main_utils_write_pidfile (const char *pidfile)
int fd; int fd;
gboolean success = FALSE; 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)); fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno));
return FALSE; return FALSE;
} }

View file

@ -521,7 +521,7 @@ static inline int
ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, int min, int max, int defval) 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, 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, 10,
min, min,
max, max,

View file

@ -2802,6 +2802,246 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
return 0; 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(). */ /* taken from systemd's dev_urandom(). */
int int
nm_utils_read_urandom (void *p, size_t nbytes) nm_utils_read_urandom (void *p, size_t nbytes)
@ -2810,7 +3050,7 @@ nm_utils_read_urandom (void *p, size_t nbytes)
int r; int r;
again: again:
fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY);
if (fd < 0) { if (fd < 0) {
r = errno; r = errno;
if (r == EINTR) if (r == EINTR)

View file

@ -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); 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_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); int nm_utils_read_urandom (void *p, size_t n);
char *nm_utils_machine_id_read (void); 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_ip4 (guint32 ip, guint8 plen, GPtrArray *domains);
void nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *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; struct stat;
gboolean nm_utils_validate_plugin (const char *path, struct stat *stat, GError **error); gboolean nm_utils_validate_plugin (const char *path, struct stat *stat, GError **error);

View file

@ -246,7 +246,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
char val[16]; char val[16];
g_snprintf (val, sizeof (val), "%d", rdata->mtu); 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); 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) { 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 (), dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
global_opt.ifname, global_opt.ifname,
@ -512,10 +512,10 @@ main (int argc, char *argv[])
if (iid) if (iid)
nm_ndisc_set_iid (ndisc, *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, NMP_SYSCTL_PATHID_ABSOLUTE (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, NMP_SYSCTL_PATHID_ABSOLUTE (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, NMP_SYSCTL_PATHID_ABSOLUTE (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_rtr_pref")), "0");
g_signal_connect (NM_PLATFORM_GET, g_signal_connect (NM_PLATFORM_GET,
NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,

View file

@ -599,6 +599,7 @@ _nm_log_impl (const char *file,
va_list args; va_list args;
char *msg; char *msg;
GTimeVal tv; GTimeVal tv;
int errno_saved;
if ((guint) level >= G_N_ELEMENTS (_nm_logging_enabled_state)) if ((guint) level >= G_N_ELEMENTS (_nm_logging_enabled_state))
g_return_if_reached (); g_return_if_reached ();
@ -606,6 +607,8 @@ _nm_log_impl (const char *file,
if (!(_nm_logging_enabled_state[level] & domain)) if (!(_nm_logging_enabled_state[level] & domain))
return; return;
errno_saved = errno;
/* Make sure that %m maps to the specified error */ /* Make sure that %m maps to the specified error */
if (error != 0) { if (error != 0) {
if (error < 0) if (error < 0)
@ -719,6 +722,8 @@ _nm_log_impl (const char *file,
} }
g_free (msg); g_free (msg);
errno = errno_saved;
} }
/*****************************************************************************/ /*****************************************************************************/

View file

@ -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); g_return_if_fail (rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN);
errno = 0; errno = 0;
fd = open ("/dev/rfkill", O_RDWR); fd = open ("/dev/rfkill", O_RDWR | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
if (errno == EACCES) if (errno == EACCES)
_LOGW (LOGD_RFKILL, "(%s): failed to open killswitch device", desc); _LOGW (LOGD_RFKILL, "(%s): failed to open killswitch device", desc);

View file

@ -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 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); 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)); g_hash_table_insert (priv->options, g_strdup (path), g_strdup (value));
return TRUE; return TRUE;
} }
static char * 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); 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)); return g_strdup (g_hash_table_lookup (priv->options, path));
} }

View file

@ -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_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 void cache_prune_candidates_prune (NMPlatform *platform);
static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); 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=" #define DEVTYPE_PREFIX "DEVTYPE="
static char * 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 *contents = NULL;
char *cont, *end; char *cont, *end;
nm_sprintf_buf (uevent, nm_assert (dirfd >= 0);
"/sys/class/net/%s/uevent",
NM_ASSERT_VALID_PATH_COMPONENT (ifname));
nm_assert (strlen (uevent) < sizeof (uevent) - 1);
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; return NULL;
for (cont = contents; cont; cont = end) { for (cont = contents; cont; cont = end) {
end = strpbrk (cont, "\r\n"); end = strpbrk (cont, "\r\n");
@ -614,7 +610,8 @@ _linktype_get_type (NMPlatform *platform,
{ {
guint i; guint i;
_assert_netns_current (platform); ASSERT_NETNS_CURRENT (platform);
nm_assert (ifname);
if (completed_from_cache) { if (completed_from_cache) {
const NMPObject *obj; const NMPObject *obj;
@ -635,7 +632,7 @@ _linktype_get_type (NMPlatform *platform,
* of messing stuff up. */ * of messing stuff up. */
if ( obj if ( obj
&& !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) && !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 && ( !kind
|| !g_strcmp0 (kind, obj->link.kind))) { || !g_strcmp0 (kind, obj->link.kind))) {
nm_assert (obj->link.kind == g_intern_string (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; NMPlatformTunProperties props;
if ( platform 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")) if (!g_strcmp0 (props.mode, "tap"))
return NM_LINK_TYPE_TAP; return NM_LINK_TYPE_TAP;
if (!g_strcmp0 (props.mode, "tun")) if (!g_strcmp0 (props.mode, "tun"))
@ -679,50 +676,52 @@ _linktype_get_type (NMPlatform *platform,
else if (arptype == ARPHRD_TUNNEL6) else if (arptype == ARPHRD_TUNNEL6)
return NM_LINK_TYPE_IP6TNL; return NM_LINK_TYPE_IP6TNL;
if (ifname) { {
char anycast_mask[NM_STRLEN ("/sys/class/net/123456789012345/anycast_mask\0") + 100 /*safety*/]; NMPUtilsEthtoolDriverInfo driver_info;
gs_free char *driver = NULL;
gs_free char *devtype = NULL;
/* Fallback OVS detection for kernel <= 3.16 */ /* Fallback OVS detection for kernel <= 3.16 */
if (nmp_utils_ethtool_get_driver_info (ifname, &driver, NULL, NULL)) { if (nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) {
if (!g_strcmp0 (driver, "openvswitch")) if (nm_streq (driver_info.driver, "openvswitch"))
return NM_LINK_TYPE_OPENVSWITCH; return NM_LINK_TYPE_OPENVSWITCH;
if (arptype == 256) { if (arptype == 256) {
/* Some s390 CTC-type devices report 256 for the encapsulation type /* Some s390 CTC-type devices report 256 for the encapsulation type
* for some reason, but we need to call them Ethernet. * 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; return NM_LINK_TYPE_ETHERNET;
} }
} }
}
nm_sprintf_buf (anycast_mask, {
"/sys/class/net/%s/anycast_mask", nm_auto_close int dirfd = -1;
NM_ASSERT_VALID_PATH_COMPONENT (ifname)); gs_free char *devtype = NULL;
nm_assert (strlen (anycast_mask) < sizeof (anycast_mask) - 1); char ifname_verified[IFNAMSIZ];
if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS)) dirfd = nmp_utils_sysctl_open_netdir (ifindex, ifname, ifname_verified);
return NM_LINK_TYPE_OLPC_MESH; if (dirfd >= 0) {
if (faccessat (dirfd, "anycast_mask", F_OK, 0) == 0)
return NM_LINK_TYPE_OLPC_MESH;
devtype = _linktype_read_devtype (ifname); devtype = _linktype_read_devtype (dirfd);
for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) { for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) {
if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) { if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) {
if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) { if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) {
/* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must
* use arptype to distinguish between them. * use arptype to distinguish between them.
*/ */
if (arptype != ARPHRD_ETHER) if (arptype != ARPHRD_ETHER)
continue; continue;
}
return linktypes[i].nm_type;
} }
return linktypes[i].nm_type;
} }
}
/* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */
if (wifi_utils_is_wifi (ifname)) if (wifi_utils_is_wifi (dirfd, ifname_verified))
return NM_LINK_TYPE_WIFI; return NM_LINK_TYPE_WIFI;
}
if (arptype == ARPHRD_ETHER) { if (arptype == ARPHRD_ETHER) {
/* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary
@ -2481,50 +2480,66 @@ nm_linux_platform_setup (void)
NULL); NULL);
} }
/*****************************************************************************/
static void 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_IS_LINUX_PLATFORM (platform));
nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ())); 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 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; GError *error = NULL;
char *contents, *contents_escaped; char *contents, *contents_escaped;
char *value_escaped = g_strescape (value, NULL); char *value_escaped = g_strescape (value, NULL);
if (!g_file_get_contents (path, &contents, NULL, &error)) { 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)", path, value_escaped, error->message); _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message);
g_clear_error (&error); g_clear_error (&error);
} else { } else {
g_strstrip (contents); g_strstrip (contents);
contents_escaped = g_strescape (contents, NULL); contents_escaped = g_strescape (contents, NULL);
if (strcmp (contents, value) == 0) 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 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);
g_free (contents_escaped); g_free (contents_escaped);
} }
g_free (value_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 { \ G_STMT_START { \
if (_LOGD_ENABLED ()) { \ if (_LOGD_ENABLED ()) { \
_log_dbg_sysctl_set_impl (platform, path, value); \ _log_dbg_sysctl_set_impl (platform, pathid, dirfd, path, value); \
} \ } \
} G_STMT_END } G_STMT_END
static gboolean 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; nm_auto_pop_netns NMPNetns *netns = NULL;
int fd, tries; 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 (path != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE);
/* Don't write outside known locations */ ASSERT_SYSCTL_ARGS (pathid, dirfd, path);
g_assert (g_str_has_prefix (path, "/proc/sys/")
|| g_str_has_prefix (path, "/sys/"));
/* Don't write to suspicious locations */
g_assert (!strstr (path, "/../"));
if (!nm_platform_netns_push (platform, &netns)) { if (dirfd < 0) {
errno = ENETDOWN; if (!nm_platform_netns_push (platform, &netns)) {
return FALSE; errno = ENETDOWN;
} return FALSE;
}
fd = open (path, O_WRONLY | O_TRUNC);
if (fd == -1) { pathid = path;
errsv = errno;
if (errsv == ENOENT) { fd = open (path, O_WRONLY | O_TRUNC | O_CLOEXEC);
_LOGD ("sysctl: failed to open '%s': (%d) %s", if (fd == -1) {
path, errsv, strerror (errsv)); errsv = errno;
} else { if (errsv == ENOENT) {
_LOGE ("sysctl: failed to open '%s': (%d) %s", _LOGD ("sysctl: failed to open '%s': (%d) %s",
path, errsv, strerror (errsv)); 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 /* 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 * (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 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); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
const char *prev_value = NULL; 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); 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); priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
} else } 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 (prev_value) {
if (strcmp (prev_value, contents) != 0) { if (strcmp (prev_value, contents) != 0) {
char *contents_escaped = g_strescape (contents, NULL); char *contents_escaped = g_strescape (contents, NULL);
char *prev_value_escaped = g_strescape (prev_value, 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 (contents_escaped);
g_free (prev_value_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 { } else {
char *contents_escaped = g_strescape (contents, NULL); 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_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 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 { \ G_STMT_START { \
if (_LOGD_ENABLED ()) \ if (_LOGD_ENABLED ()) \
_log_dbg_sysctl_get_impl (platform, path, contents); \ _log_dbg_sysctl_get_impl (platform, pathid, contents); \
} G_STMT_END } G_STMT_END
static char * 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; nm_auto_pop_netns NMPNetns *netns = NULL;
GError *error = NULL; GError *error = NULL;
char *contents; char *contents;
/* Don't write outside known locations */ ASSERT_SYSCTL_ARGS (pathid, dirfd, path);
g_assert (g_str_has_prefix (path, "/proc/sys/")
|| g_str_has_prefix (path, "/sys/"));
/* Don't write to suspicious locations */
g_assert (!strstr (path, "/../"));
if (!nm_platform_netns_push (platform, &netns)) if (dirfd < 0) {
return NULL; 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 */ /* We assume FAILED means EOPNOTSUP */
if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) 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_NODEV)
|| g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) || 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 else
_LOGE ("error reading %s: %s", path, error->message); _LOGE ("error reading %s: %s", pathid, error->message);
g_clear_error (&error); g_clear_error (&error);
return NULL; return NULL;
} }
g_strstrip (contents); g_strstrip (contents);
_log_dbg_sysctl_get (platform, path, contents); _log_dbg_sysctl_get (platform, pathid, contents);
return 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));
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) { switch (cache_op) {
case NMP_CACHE_OPS_ADDED: case NMP_CACHE_OPS_ADDED:
@ -4428,10 +4455,6 @@ static gboolean
link_supports_carrier_detect (NMPlatform *platform, int ifindex) link_supports_carrier_detect (NMPlatform *platform, int ifindex)
{ {
nm_auto_pop_netns NMPNetns *netns = NULL; 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)) if (!nm_platform_netns_push (platform, &netns))
return FALSE; 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 * us whether the device actually supports carrier detection in the first
* place. We assume any device that does implements one of these two APIs. * 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 static gboolean
@ -4458,7 +4481,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
if (!nm_platform_netns_push (platform, &netns)) if (!nm_platform_netns_push (platform, &netns))
return FALSE; return FALSE;
return nmp_utils_ethtool_supports_vlans (obj->link.name); return nmp_utils_ethtool_supports_vlans (ifindex);
} }
static NMPlatformError static NMPlatformError
@ -4526,7 +4549,7 @@ link_get_permanent_address (NMPlatform *platform,
if (!nm_platform_netns_push (platform, &netns)) if (!nm_platform_netns_push (platform, &netns))
return FALSE; 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 static gboolean
@ -4555,44 +4578,27 @@ nla_put_failure:
static char * static char *
link_get_physical_port_id (NMPlatform *platform, int ifindex) link_get_physical_port_id (NMPlatform *platform, int ifindex)
{ {
const char *ifname; nm_auto_close int dirfd = -1;
char *path, *id; char ifname_verified[IFNAMSIZ];
ifname = nm_platform_link_get_name (platform, ifindex); dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname_verified);
if (!ifname) if (dirfd < 0)
return NULL; return NULL;
return sysctl_get (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "phys_port_id"));
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;
} }
static guint static guint
link_get_dev_id (NMPlatform *platform, int ifindex) link_get_dev_id (NMPlatform *platform, int ifindex)
{ {
const char *ifname; nm_auto_close int dirfd = -1;
gs_free char *path = NULL, *id = NULL; char ifname_verified[IFNAMSIZ];
gint64 int_val;
ifname = nm_platform_link_get_name (platform, ifindex); dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname_verified);
if (!ifname) if (dirfd < 0)
return 0; return 0;
return nm_platform_sysctl_get_int_checked (platform,
ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname); NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "dev_id"),
16, 0, G_MAXUINT16, 0);
path = g_strdup_printf ("/sys/class/net/%s/dev_id", ifname);
id = sysctl_get (platform, path);
if (!id || !*id)
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;
} }
static int 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, _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT,
tap ? "tap" : "tun", name, owner, group); 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) if (fd < 0)
return FALSE; return FALSE;
@ -5250,9 +5256,9 @@ _infiniband_partition_action (NMPlatform *platform,
const NMPlatformLink **out_link) const NMPlatformLink **out_link)
{ {
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); 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; const NMPObject *obj;
char path[NM_STRLEN ("/sys/class/net/%s/%s") + IFNAMSIZ + 100];
char id[20]; char id[20];
char name[IFNAMSIZ]; char name[IFNAMSIZ];
gboolean success; 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 (NM_IN_SET (action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD));
nm_assert (p_key > 0 && p_key <= 0xffff && p_key != 0x8000); nm_assert (p_key > 0 && p_key <= 0xffff && p_key != 0x8000);
obj_parent = nmp_cache_lookup_link (priv->cache, parent); dirfd = nm_platform_sysctl_open_netdir (platform, parent, ifname_parent);
if (!obj_parent || !obj_parent->link.name[0]) { if (dirfd < 0) {
errno = ENOENT; errno = ENOENT;
return FALSE; 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); 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 (!success) {
if ( action == INFINIBAND_ACTION_DELETE_CHILD if ( action == INFINIBAND_ACTION_DELETE_CHILD
&& errno == ENODEV) && errno == ENODEV)
@ -5281,7 +5285,7 @@ _infiniband_partition_action (NMPlatform *platform,
return FALSE; 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); do_request_link (platform, 0, name);
if (action == INFINIBAND_ACTION_DELETE_CHILD) if (action == INFINIBAND_ACTION_DELETE_CHILD)
@ -5515,7 +5519,7 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex)
return FALSE; return FALSE;
if (type == NM_LINK_TYPE_ETHERNET) 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) { else if (type == NM_LINK_TYPE_WIFI) {
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
@ -5535,14 +5539,17 @@ link_get_driver_info (NMPlatform *platform,
char **out_fw_version) char **out_fw_version)
{ {
nm_auto_pop_netns NMPNetns *netns = NULL; nm_auto_pop_netns NMPNetns *netns = NULL;
NMPUtilsEthtoolDriverInfo driver_info;
if (!nm_platform_netns_push (platform, &netns)) if (!nm_platform_netns_push (platform, &netns))
return FALSE; return FALSE;
return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex), if (!nmp_utils_ethtool_get_driver_info (ifindex, &driver_info))
out_driver_name, return FALSE;
out_driver_version, NM_SET_OUT (out_driver_name, g_strdup (driver_info.driver));
out_fw_version); 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;
} }
/*****************************************************************************/ /*****************************************************************************/

View file

@ -31,15 +31,39 @@
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <fcntl.h>
#include "nm-utils.h" #include "nm-utils.h"
#include "nm-setting-wired.h" #include "nm-setting-wired.h"
#include "nm-core-utils.h" #include "nm-core-utils.h"
extern char *if_indextoname (unsigned int __ifindex, char *__ifname);
/****************************************************************** /******************************************************************
* ethtool * 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) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
#define ethtool_cmd_speed(pedata) ((pedata)->speed) #define ethtool_cmd_speed(pedata) ((pedata)->speed)
@ -47,55 +71,84 @@
G_STMT_START { (pedata)->speed = (guint16) (speed); } G_STMT_END G_STMT_START { (pedata)->speed = (guint16) (speed); } G_STMT_END
#endif #endif
static gboolean static gboolean
ethtool_get (const char *name, gpointer edata) ethtool_get (int ifindex, gpointer edata)
{ {
struct ifreq ifr; char ifname[IFNAMSIZ];
int fd; char sbuf[50];
if (!name || !*name) nm_assert (ifindex > 0);
return FALSE;
if (!nmp_utils_device_exists (name)) /* ethtool ioctl API uses the ifname to refer to an interface. That is racy
return FALSE; * 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. */
/* nmp_utils_device_exists() already errors out if @name is invalid. */ if (!if_indextoname (ifindex, ifname)) {
nm_assert (strlen (name) < IFNAMSIZ); nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: request fails resolving ifindex: %s",
ifindex,
memset (&ifr, 0, sizeof (ifr)); _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)),
nm_utils_ifname_cpy (ifr.ifr_name, name); g_strerror (errno));
ifr.ifr_data = edata;
fd = socket (PF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
nm_log_err (LOGD_PLATFORM, "ethtool: Could not open socket.");
return FALSE; return FALSE;
} }
if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { {
nm_log_dbg (LOGD_PLATFORM, "ethtool: Request failed: %s", strerror (errno)); nm_auto_close int fd = -1;
close (fd); struct ifreq ifr = {
return FALSE; .ifr_data = edata,
} };
close (fd); memcpy (ifr.ifr_name, ifname, sizeof (ifname));
return TRUE;
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;
}
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;
}
nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success",
ifindex,
_ethtool_data_to_string (edata, sbuf, sizeof (sbuf)),
ifname);
return TRUE;
}
} }
static int 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_sset_info *info = NULL;
gs_free struct ethtool_gstrings *strings = NULL; gs_free struct ethtool_gstrings *strings = NULL;
guint32 len, i; guint32 len, i;
g_return_val_if_fail (ifindex > 0, -1);
info = g_malloc0 (sizeof (*info) + sizeof (guint32)); info = g_malloc0 (sizeof (*info) + sizeof (guint32));
info->cmd = ETHTOOL_GSSET_INFO; info->cmd = ETHTOOL_GSSET_INFO;
info->reserved = 0; info->reserved = 0;
info->sset_mask = 1ULL << stringset_id; info->sset_mask = 1ULL << stringset_id;
if (!ethtool_get (ifname, info)) if (!ethtool_get (ifindex, info))
return -1; return -1;
if (!info->sset_mask) if (!info->sset_mask)
return -1; return -1;
@ -106,7 +159,7 @@ ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *s
strings->cmd = ETHTOOL_GSTRINGS; strings->cmd = ETHTOOL_GSTRINGS;
strings->string_set = stringset_id; strings->string_set = stringset_id;
strings->len = len; strings->len = len;
if (!ethtool_get (ifname, strings)) if (!ethtool_get (ifindex, strings))
return -1; return -1;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
@ -118,32 +171,30 @@ ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *s
} }
gboolean gboolean
nmp_utils_ethtool_get_driver_info (const char *ifname, nmp_utils_ethtool_get_driver_info (int ifindex,
char **out_driver_name, NMPUtilsEthtoolDriverInfo *data)
char **out_driver_version,
char **out_fw_version)
{ {
struct ethtool_drvinfo drvinfo = { 0 }; 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));
if (!ifname) g_return_val_if_fail (ifindex > 0, FALSE);
return FALSE; g_return_val_if_fail (data, FALSE);
drvinfo.cmd = ETHTOOL_GDRVINFO; drvinfo = (struct ethtool_drvinfo *) data;
if (!ethtool_get (ifname, &drvinfo))
return FALSE;
if (out_driver_name) memset (drvinfo, 0, sizeof (*drvinfo));
*out_driver_name = g_strdup (drvinfo.driver); drvinfo->cmd = ETHTOOL_GDRVINFO;
if (out_driver_version) return ethtool_get (ifindex, drvinfo);
*out_driver_version = g_strdup (drvinfo.version);
if (out_fw_version)
*out_fw_version = g_strdup (drvinfo.fw_version);
return TRUE;
} }
gboolean gboolean
nmp_utils_ethtool_get_permanent_address (const char *ifname, nmp_utils_ethtool_get_permanent_address (int ifindex,
guint8 *buf, guint8 *buf,
size_t *length) size_t *length)
{ {
@ -153,14 +204,13 @@ nmp_utils_ethtool_get_permanent_address (const char *ifname,
} edata; } edata;
guint i; guint i;
if (!ifname) g_return_val_if_fail (ifindex > 0, FALSE);
return FALSE;
memset (&edata, 0, sizeof (edata)); memset (&edata, 0, sizeof (edata));
edata.e.cmd = ETHTOOL_GPERMADDR; edata.e.cmd = ETHTOOL_GPERMADDR;
edata.e.size = NM_UTILS_HWADDR_LEN_MAX; edata.e.size = NM_UTILS_HWADDR_LEN_MAX;
if (!ethtool_get (ifname, &edata.e)) if (!ethtool_get (ifindex, &edata.e))
return FALSE; return FALSE;
if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX)
@ -187,29 +237,30 @@ not_all_0or1:
} }
gboolean 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 }; 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 /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we
* assume the device supports carrier-detect, otherwise we assume it * assume the device supports carrier-detect, otherwise we assume it
* doesn't. * doesn't.
*/ */
return ethtool_get (ifname, &edata); return ethtool_get (ifindex, &edata);
} }
gboolean gboolean
nmp_utils_ethtool_supports_vlans (const char *ifname) nmp_utils_ethtool_supports_vlans (int ifindex)
{ {
gs_free struct ethtool_gfeatures *features = NULL; gs_free struct ethtool_gfeatures *features = NULL;
int idx, block, bit, size; int idx, block, bit, size;
if (!ifname) g_return_val_if_fail (ifindex > 0, FALSE);
return 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) { 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; return FALSE;
} }
@ -221,54 +272,52 @@ nmp_utils_ethtool_supports_vlans (const char *ifname)
features->cmd = ETHTOOL_GFEATURES; features->cmd = ETHTOOL_GFEATURES;
features->size = size; features->size = size;
if (!ethtool_get (ifname, features)) if (!ethtool_get (ifindex, features))
return FALSE; return FALSE;
return !(features->features[block].active & (1 << bit)); return !(features->features[block].active & (1 << bit));
} }
int 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; gs_free struct ethtool_stats *stats = NULL;
int peer_ifindex_stat; int peer_ifindex_stat;
if (!ifname) g_return_val_if_fail (ifindex > 0, 0);
return 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) { 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; return FALSE;
} }
stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64));
stats->cmd = ETHTOOL_GSTATS; stats->cmd = ETHTOOL_GSTATS;
stats->n_stats = peer_ifindex_stat + 1; stats->n_stats = peer_ifindex_stat + 1;
if (!ethtool_get (ifname, stats)) if (!ethtool_get (ifindex, stats))
return 0; return 0;
return stats->data[peer_ifindex_stat]; return stats->data[peer_ifindex_stat];
} }
gboolean gboolean
nmp_utils_ethtool_get_wake_on_lan (const char *ifname) nmp_utils_ethtool_get_wake_on_lan (int ifindex)
{ {
struct ethtool_wolinfo wol; struct ethtool_wolinfo wol;
if (!ifname) g_return_val_if_fail (ifindex > 0, FALSE);
return FALSE;
memset (&wol, 0, sizeof (wol)); memset (&wol, 0, sizeof (wol));
wol.cmd = ETHTOOL_GWOL; wol.cmd = ETHTOOL_GWOL;
if (!ethtool_get (ifname, &wol)) if (!ethtool_get (ifindex, &wol))
return FALSE; return FALSE;
return wol.wolopts != 0; return wol.wolopts != 0;
} }
gboolean gboolean
nmp_utils_ethtool_get_link_settings (const char *ifname, nmp_utils_ethtool_get_link_settings (int ifindex,
gboolean *out_autoneg, gboolean *out_autoneg,
guint32 *out_speed, guint32 *out_speed,
NMPlatformLinkDuplexType *out_duplex) NMPlatformLinkDuplexType *out_duplex)
@ -277,7 +326,9 @@ nmp_utils_ethtool_get_link_settings (const char *ifname,
.cmd = ETHTOOL_GSET, .cmd = ETHTOOL_GSET,
}; };
if (!ethtool_get (ifname, &edata)) g_return_val_if_fail (ifindex > 0, FALSE);
if (!ethtool_get (ifindex, &edata))
return FALSE; return FALSE;
if (out_autoneg) if (out_autoneg)
@ -311,14 +362,19 @@ nmp_utils_ethtool_get_link_settings (const char *ifname,
} }
gboolean 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 = { struct ethtool_cmd edata = {
.cmd = ETHTOOL_GSET, .cmd = ETHTOOL_GSET,
}; };
g_return_val_if_fail (ifindex > 0, FALSE);
/* retrieve first current settings */ /* retrieve first current settings */
if (!ethtool_get (ifname, &edata)) if (!ethtool_get (ifindex, &edata))
return FALSE; return FALSE;
/* then change the needed ones */ /* 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 gboolean
nmp_utils_ethtool_set_wake_on_lan (const char *ifname, nmp_utils_ethtool_set_wake_on_lan (int ifindex,
NMSettingWiredWakeOnLan wol, NMSettingWiredWakeOnLan wol,
const char *wol_password) const char *wol_password)
{ {
struct ethtool_wolinfo wol_info = { }; struct ethtool_wolinfo wol_info = { };
g_return_val_if_fail (ifindex > 0, FALSE);
if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE)
return TRUE; return TRUE;
@ -386,7 +444,7 @@ nmp_utils_ethtool_set_wake_on_lan (const char *ifname,
wol_info.wolopts |= WAKE_MAGICSECURE; 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 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 ifreq ifr;
struct mii_ioctl_data *mii; struct mii_ioctl_data *mii;
gboolean supports_mii = FALSE;
if (!ifname) g_return_val_if_fail (ifindex > 0, FALSE);
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; return FALSE;
}
if (!nmp_utils_device_exists (ifname)) fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
return FALSE;
fd = socket (PF_INET, SOCK_DGRAM, 0);
if (fd < 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; return FALSE;
} }
memset (&ifr, 0, sizeof (struct ifreq)); 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) { if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) {
errsv = errno; nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, ifname, strerror (errno));
nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIPHY failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); return FALSE;
goto out;
} }
/* If we can read the BMSR register, we assume that the card supports MII link detection */ /* 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 = (struct mii_ioctl_data *) &ifr.ifr_ifru;
mii->reg_num = MII_BMSR; mii->reg_num = MII_BMSR;
if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) { if (ioctl (fd, SIOCGMIIREG, &ifr) != 0) {
nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG result 0x%X (%s)", mii->val_out, ifname); nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, ifname, strerror (errno));
supports_mii = TRUE; return FALSE;
} else {
errsv = errno;
nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG failed: %s (%d) (%s)", strerror (errsv), errsv, ifname);
} }
out: nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, ifname, mii->val_out);
close (fd); return TRUE;
nm_log_dbg (LOGD_PLATFORM, "mii: MII %s supported (%s)", supports_mii ? "is" : "not", ifname);
return supports_mii;
} }
/****************************************************************** /******************************************************************
@ -486,22 +538,6 @@ out:
* utils * 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 NMIPConfigSource
nmp_utils_ip_config_source_from_rtprot (guint8 rtprot) 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; 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;
}

View file

@ -27,38 +27,55 @@
#include "nm-setting-wired.h" #include "nm-setting-wired.h"
const char *nmp_utils_ethtool_get_driver (const char *ifname); const char *nmp_utils_ethtool_get_driver (int ifindex);
gboolean nmp_utils_ethtool_supports_carrier_detect (const char *ifname); gboolean nmp_utils_ethtool_supports_carrier_detect (int ifindex);
gboolean nmp_utils_ethtool_supports_vlans (const char *ifname); gboolean nmp_utils_ethtool_supports_vlans (int ifindex);
int nmp_utils_ethtool_get_peer_ifindex (const char *ifname); int nmp_utils_ethtool_get_peer_ifindex (int ifindex);
gboolean nmp_utils_ethtool_get_wake_on_lan (const char *ifname); gboolean nmp_utils_ethtool_get_wake_on_lan (int ifindex);
gboolean nmp_utils_ethtool_set_wake_on_lan (const char *ifname, NMSettingWiredWakeOnLan wol, gboolean nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan wol,
const char *wol_password); 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_get_link_settings (int ifindex, 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_set_link_settings (int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex);
gboolean nmp_utils_ethtool_get_driver_info (const char *ifname, typedef struct {
char **out_driver_name, /* We don't want to include <linux/ethtool.h> in header files,
char **out_driver_version, * thus create a ABI compatible version of struct ethtool_drvinfo.*/
char **out_fw_version); 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, guint8 *buf,
size_t *length); 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); 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; 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; 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_coerce_from_rtprot (NMIPConfigSource source) _nm_const;
NMIPConfigSource nmp_utils_ip_config_source_round_trip_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); 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__ */ #endif /* __NM_PLATFORM_UTILS_H__ */

View file

@ -265,9 +265,40 @@ 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: * nm_platform_sysctl_set:
* @self: platform instance * @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 * @path: Absolute option path
* @value: Value to write * @value: Value to write
* *
@ -278,14 +309,14 @@ nm_platform_process_events (NMPlatform *self)
* Returns: %TRUE on success. * Returns: %TRUE on success.
*/ */
gboolean 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); _CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (path, FALSE); g_return_val_if_fail (path, FALSE);
g_return_val_if_fail (value, 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 gboolean
@ -305,7 +336,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface,
return FALSE; return FALSE;
path = nm_utils_ip6_property_path (iface, "hop_limit"); 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 /* only allow increasing the hop-limit to avoid DOS by an attacker
* setting a low hop-limit (CVE-2015-2924, rh#1209902) */ * 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]; char svalue[20];
sprintf (svalue, "%d", value); sprintf (svalue, "%d", value);
nm_platform_sysctl_set (self, path, svalue); nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), svalue);
} }
return TRUE; return TRUE;
@ -325,23 +356,29 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface,
/** /**
* nm_platform_sysctl_get: * nm_platform_sysctl_get:
* @self: platform instance * @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 * @path: Absolute path to sysctl
* *
* Returns: (transfer full): Contents of the virtual sysctl file. * Returns: (transfer full): Contents of the virtual sysctl file.
*/ */
char * 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); _CHECK_SELF (self, klass, NULL);
g_return_val_if_fail (path, 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: * nm_platform_sysctl_get_int32:
* @self: platform instance * @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 * @path: Absolute path to sysctl
* @fallback: default value, if the content of path could not be read * @fallback: default value, if the content of path could not be read
* as decimal integer. * 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. * value, on success %errno will be set to zero.
*/ */
gint32 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: * nm_platform_sysctl_get_int_checked:
* @self: platform instance * @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 * @path: Absolute path to sysctl
* @base: base of numeric conversion * @base: base of numeric conversion
* @min: minimal value that is still valid * @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. * (inclusive) or @fallback.
*/ */
gint64 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; char *value = NULL;
gint32 ret; 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); g_return_val_if_fail (path, fallback);
if (path) if (path)
value = nm_platform_sysctl_get (self, path); value = nm_platform_sysctl_get (self, pathid, dirfd, path);
if (!value) { if (!value) {
errno = EINVAL; errno = EINVAL;
@ -1668,34 +1708,44 @@ nm_platform_link_tun_add (NMPlatform *self,
/*****************************************************************************/ /*****************************************************************************/
static char * static gboolean
link_option_path (NMPlatform *self, int master, const char *category, const char *option) 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); nm_auto_close int dirfd = -1;
char ifname_verified[IFNAMSIZ];
const char *path;
if (!name || !category || !option) if (!category || !option)
return FALSE;
dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified);
if (dirfd < 0)
return FALSE;
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 ifindex, const char *category, const char *option)
{
nm_auto_close int dirfd = -1;
char ifname_verified[IFNAMSIZ];
const char *path;
if (!category || !option)
return NULL; return NULL;
return g_strdup_printf ("/sys/class/net/%s/%s/%s", dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified);
NM_ASSERT_VALID_PATH_COMPONENT (name), if (dirfd < 0)
NM_ASSERT_VALID_PATH_COMPONENT (category), return NULL;
NM_ASSERT_VALID_PATH_COMPONENT (option));
}
static gboolean path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2,
link_set_option (NMPlatform *self, int master, const char *category, const char *option, const char *value) "%s/%s",
{ category, option);
gs_free char *path = link_option_path (self, master, category, option); return nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path));
return path && nm_platform_sysctl_set (self, path, value);
}
static char *
link_get_option (NMPlatform *self, int master, const char *category, const char *option)
{
gs_free char *path = link_option_path (self, master, category, option);
return path ? nm_platform_sysctl_get (self, path) : NULL;
} }
static const char * static const char *
@ -1973,10 +2023,11 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self,
int *out_p_key, int *out_p_key,
const char **out_mode) const char **out_mode)
{ {
nm_auto_close int dirfd = -1;
char ifname_verified[IFNAMSIZ];
const NMPlatformLnkInfiniband *plnk; const NMPlatformLnkInfiniband *plnk;
const NMPlatformLink *plink; const NMPlatformLink *plink;
const char *iface; char *contents;
char *path, *contents;
const char *mode; const char *mode;
int p_key = 0; 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, /* Could not get the link information via netlink. To support older kernels,
* fallback to reading sysfs. */ * 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)
/* Fall back to reading sysfs */
path = g_strdup_printf ("/sys/class/net/%s/mode", iface);
contents = nm_platform_sysctl_get (self, path);
g_free (path);
if (!contents)
return FALSE; return FALSE;
contents = nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"));
if (!contents)
return FALSE;
if (strstr (contents, "datagram")) if (strstr (contents, "datagram"))
mode = "datagram"; mode = "datagram";
else if (strstr (contents, "connected")) else if (strstr (contents, "connected"))
@ -2017,13 +2066,7 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self,
mode = NULL; mode = NULL;
g_free (contents); g_free (contents);
path = g_strdup_printf ("/sys/class/net/%s/pkey", iface); p_key = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "pkey"), 16, 0, 0xFFFF, -1);
contents = nm_platform_sysctl_get (self, path);
g_free (path);
if (!contents)
return FALSE;
p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1);
g_free (contents);
if (p_key < 0) if (p_key < 0)
return FALSE; 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)) if (!nm_platform_netns_push (self, &netns))
return FALSE; 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) if (peer_ifindex <= 0)
return FALSE; return FALSE;
@ -2226,74 +2269,59 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe
} }
gboolean 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]; nm_auto_close int dirfd = -1;
char *val; const char *ifname;
char ifname_verified[IFNAMSIZ];
gint64 flags;
gboolean success = TRUE; gboolean success = TRUE;
_CHECK_SELF (self, klass, FALSE); _CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (ifindex > 0, FALSE);
g_return_val_if_fail (props, 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)); memset (props, 0, sizeof (*props));
props->owner = -1; props->owner = -1;
props->group = -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; return FALSE;
nm_sprintf_buf (path, "/sys/class/net/%s/owner", ifname); ifname = ifname_verified;
val = nm_platform_sysctl_get (self, path);
if (val) { props->owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXINT64, -1);
props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); if (errno)
if (errno)
success = FALSE;
g_free (val);
} else
success = FALSE; success = FALSE;
nm_sprintf_buf (path, "/sys/class/net/%s/group", ifname); props->group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXINT64, -1);
val = nm_platform_sysctl_get (self, path); if (errno)
if (val) {
props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
if (errno)
success = FALSE;
g_free (val);
} else
success = FALSE; success = FALSE;
nm_sprintf_buf (path, "/sys/class/net/%s/tun_flags", ifname); flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1);
val = nm_platform_sysctl_get (self, path); if (flags >= 0) {
if (val) { props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap";
gint64 flags; props->no_pi = !!(flags & IFF_NO_PI);
props->vnet_hdr = !!(flags & IFF_VNET_HDR);
flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE);
if (!errno) {
props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap";
props->no_pi = !!(flags & IFF_NO_PI);
props->vnet_hdr = !!(flags & IFF_VNET_HDR);
props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE);
} else
success = FALSE;
g_free (val);
} else } else
success = FALSE; success = FALSE;
return success; return success;
} }
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 gboolean
nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps) nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps)
{ {
@ -2479,27 +2507,33 @@ _to_string_ifa_flags (guint32 ifa_flags, char *buf, gsize size)
/*****************************************************************************/ /*****************************************************************************/
gboolean 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); _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 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); _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 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); _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);
} }
/*****************************************************************************/ /*****************************************************************************/

View file

@ -500,8 +500,8 @@ struct _NMPlatform {
typedef struct { typedef struct {
GObjectClass parent; GObjectClass parent;
gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value); gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value);
char * (*sysctl_get) (NMPlatform *, const char *path); char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path);
const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex);
const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); 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); 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) #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); #define NMP_SYSCTL_PATHID_ABSOLUTE(path) \
char *nm_platform_sysctl_get (NMPlatform *self, const char *path); ((const char *) NULL), -1, (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_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); 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_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_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 (NMPlatform *self, int ifindex, const char *ifname_guess, NMPlatformTunProperties *properties);
gboolean nm_platform_link_tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props);
gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps);
gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid); 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); 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_wake_on_lan (NMPlatform *self, int ifindex, 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_set_link_settings (NMPlatform *self, int ifindex, 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_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex);
#endif /* __NETWORKMANAGER_PLATFORM_H__ */ #endif /* __NETWORKMANAGER_PLATFORM_H__ */

View file

@ -284,7 +284,7 @@ _netns_new (GError **error)
int fd_net, fd_mnt; int fd_net, fd_mnt;
int errsv; 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) { if (fd_net == -1) {
errsv = errno; errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
@ -293,7 +293,7 @@ _netns_new (GError **error)
return NULL; 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) { if (fd_mnt == -1) {
errsv = errno; errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, 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 (out_fd) {
if ((fd = open (filename, O_RDONLY)) == -1) { if ((fd = open (filename, O_RDONLY | O_CLOEXEC)) == -1) {
errsv = errno; errsv = errno;
_LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv)); _LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv));
umount2 (filename, MNT_DETACH); umount2 (filename, MNT_DETACH);

View file

@ -53,8 +53,12 @@ int nmp_netns_get_fd_mnt (NMPNetns *self);
static inline void static inline void
_nm_auto_pop_netns (NMPNetns **p) _nm_auto_pop_netns (NMPNetns **p)
{ {
if (*p) if (*p) {
int errsv = errno;
nmp_netns_pop (*p); nmp_netns_pop (*p);
errno = errsv;
}
} }
#define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns))) #define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns)))

View file

@ -125,7 +125,7 @@ _vlan_xgress_qos_mappings_cpy (guint *dst_n_map,
/*****************************************************************************/ /*****************************************************************************/
static const char * 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; const char *driver = NULL;
@ -140,14 +140,12 @@ _link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname
if (kind) if (kind)
return kind; return kind;
if (ifname) { if (ifindex > 0) {
char *d; NMPUtilsEthtoolDriverInfo driver_info;
if (nmp_utils_ethtool_get_driver_info (ifname, &d, NULL, NULL)) { if (nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) {
driver = d && d[0] ? g_intern_string (d) : NULL; if (driver_info.driver[0])
g_free (d); return g_intern_string (driver_info.driver);
if (driver)
return driver;
} }
} }
@ -169,7 +167,7 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev)
if (obj->_link.netlink.is_in_netlink) { if (obj->_link.netlink.is_in_netlink) {
driver = _link_get_driver (obj->_link.udev.device, driver = _link_get_driver (obj->_link.udev.device,
obj->link.kind, obj->link.kind,
obj->link.name); obj->link.ifindex);
if (obj->_link.udev.device) if (obj->_link.udev.device)
initialized = TRUE; initialized = TRUE;
else if (!use_udev) { else if (!use_udev) {

View file

@ -1407,7 +1407,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
int pipefd_p2c[2]; int pipefd_p2c[2];
ssize_t r; ssize_t r;
e = pipe (pipefd_c2p); e = pipe2 (pipefd_c2p, O_CLOEXEC);
if (e != 0) { if (e != 0) {
errsv = errno; errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, 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; return FALSE;
} }
e = pipe (pipefd_p2c); e = pipe2 (pipefd_p2c, O_CLOEXEC);
if (e != 0) { if (e != 0) {
errsv = errno; errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, 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); 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. /* Since Linux 3.19 we have to disable setgroups() in order to map users.
* Just proceed if the file is not there. */ * Just proceed if the file is not there. */
f = fopen ("/proc/self/setgroups", "w"); f = fopen ("/proc/self/setgroups", "we");
if (f) { if (f) {
fprintf (f, "deny"); fprintf (f, "deny");
fclose (f); fclose (f);
} }
/* Map current UID to root in NS to be created. */ /* 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) if (!f)
return FALSE; return FALSE;
fprintf (f, "0 %d 1", uid); fprintf (f, "0 %d 1", uid);
fclose (f); fclose (f);
/* Map current GID to root in NS to be created. */ /* 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) if (!f)
return FALSE; return FALSE;
fprintf (f, "0 %d 1", gid); fprintf (f, "0 %d 1", gid);

View file

@ -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 { typedef struct {
gulong handler_id; gulong handler_id;
const char *name; const char *name;

View file

@ -398,26 +398,28 @@ test_software (NMLinkType link_type, const char *link_typename)
accept_signal (link_changed); accept_signal (link_changed);
/* Set master option */ /* Set master option */
switch (link_type) { if (nmtstp_is_root_test ()) {
case NM_LINK_TYPE_BRIDGE: switch (link_type) {
if (nmtstp_is_sysfs_writable ()) { case NM_LINK_TYPE_BRIDGE:
g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "628")); if (nmtstp_is_sysfs_writable ()) {
value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "628"));
g_assert_cmpstr (value, ==, "628"); value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay");
g_free (value); 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;
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;
} }
/* Enslave and release */ /* Enslave and release */
@ -1941,7 +1943,7 @@ _test_netns_check_skip (void)
G_STMT_START { \ G_STMT_START { \
gs_free char *_val = NULL; \ 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_assert_cmpstr (_val, ==, value); \
} G_STMT_END } G_STMT_END
@ -1954,6 +1956,7 @@ test_netns_general (gpointer fixture, gconstpointer test_data)
char sbuf[100]; char sbuf[100];
int i, j, k; int i, j, k;
gboolean ethtool_support; gboolean ethtool_support;
NMPUtilsEthtoolDriverInfo driver_info;
if (_test_netns_check_skip ()) if (_test_netns_check_skip ())
return; return;
@ -2013,7 +2016,7 @@ test_netns_general (gpointer fixture, gconstpointer test_data)
else else
path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"; 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)); _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. */ * skip asserts that are known to fail. */
ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0; ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0;
if (ethtool_support) { if (ethtool_support) {
g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", 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 ("dummy2a", NULL, NULL, NULL)); 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 (!nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0); 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 dummy2a > /dev/null"), ==, 0);
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b 2> /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)); g_assert (nm_platform_netns_push (platform_2, &netns_tmp));
if (ethtool_support) { if (ethtool_support) {
g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", 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 ("dummy2a", NULL, NULL, NULL)); 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 ( nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0); 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 dummy2a 2> /dev/null"), !=, 0);
g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b > /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_1 = NULL;
gs_unref_object NMPlatform *platform_2 = NULL; gs_unref_object NMPlatform *platform_2 = NULL;
nm_auto_pop_netns NMPNetns *netns_pop = NULL; nm_auto_pop_netns NMPNetns *netns_pop = NULL;
int i;
if (_test_netns_check_skip ()) if (_test_netns_check_skip ())
return; return;
@ -2065,9 +2065,7 @@ test_netns_set_netns (gpointer fixture, gconstpointer test_data)
platforms[1] = platform_1 = _test_netns_create_platform (); platforms[1] = platform_1 = _test_netns_create_platform ();
platforms[2] = platform_2 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform ();
i = nmtst_get_rand_int () % 4; nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop);
if (i != 3)
g_assert (nm_platform_netns_push (platforms[i], &netns_pop));
#define LINK_MOVE_NAME "link-move" #define LINK_MOVE_NAME "link-move"
g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME)); 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); _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); tmp = _get_current_namespace_id (CLONE_NEWNET);
g_ptr_array_add (device_names, tmp); 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[1] = platform_1 = _test_netns_create_platform ();
platforms[2] = platform_2 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform ();
i = nmtst_get_rand_int () % 4; nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop);
if (i != 3)
g_assert (nm_platform_netns_push (platforms[i], &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 (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); 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; NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
void 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/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/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_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);
} }
} }

View file

@ -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_channel = wifi_wext_set_mesh_channel;
wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid; 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) if (wext->fd < 0)
goto error; goto error;
@ -662,10 +662,17 @@ wifi_wext_is_wifi (const char *iface)
struct iwreq iwr; struct iwreq iwr;
gboolean is_wifi = FALSE; gboolean is_wifi = FALSE;
if (!nmp_utils_device_exists (iface)) /* performing an ioctl on a non-existing name may cause the automatic
return FALSE; * 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) { if (fd >= 0) {
nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface); nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface);
if (ioctl (fd, SIOCGIWNAME, &iwr) == 0) if (ioctl (fd, SIOCGIWNAME, &iwr) == 0)

View file

@ -26,6 +26,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <fcntl.h>
#include "wifi-utils-private.h" #include "wifi-utils-private.h"
#include "wifi-utils-nl80211.h" #include "wifi-utils-nl80211.h"
@ -34,6 +35,8 @@
#endif #endif
#include "nm-core-utils.h" #include "nm-core-utils.h"
#include "platform/nm-platform-utils.h"
gpointer gpointer
wifi_data_new (const char *iface, int ifindex, gsize len) wifi_data_new (const char *iface, int ifindex, gsize len)
{ {
@ -180,30 +183,19 @@ wifi_utils_deinit (WifiData *data)
} }
gboolean 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*/]; g_return_val_if_fail (dirfd >= 0, FALSE);
struct stat s;
g_return_val_if_fail (iface != NULL, FALSE); if (faccessat (dirfd, "phy80211", F_OK, 0) == 0)
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);
if ((stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR)))
return TRUE; return TRUE;
#if HAVE_WEXT #if HAVE_WEXT
if (wifi_wext_is_wifi (iface)) if (wifi_wext_is_wifi (ifname))
return TRUE; return TRUE;
#endif #endif
return FALSE; return FALSE;
} }
/* OLPC Mesh-only functions */ /* OLPC Mesh-only functions */
guint32 guint32

View file

@ -28,7 +28,7 @@
typedef struct WifiData WifiData; 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); WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan);

View file

@ -148,7 +148,7 @@ monitor_cb (gpointer user_data)
if (errno != ENODEV) if (errno != ENODEV)
_LOGW ("could not read ppp stats: %s", strerror (errno)); _LOGW ("could not read ppp stats: %s", strerror (errno));
} else { } else {
g_signal_emit (manager, signals[STATS], 0, g_signal_emit (manager, signals[STATS], 0,
stats.p.ppp_ibytes, stats.p.ppp_ibytes,
stats.p.ppp_obytes); stats.p.ppp_obytes);
} }
@ -165,7 +165,7 @@ monitor_stats (NMPPPManager *manager)
if (priv->monitor_fd >= 0) if (priv->monitor_fd >= 0)
return; 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) { if (priv->monitor_fd >= 0) {
g_warn_if_fail (priv->monitor_id == 0); g_warn_if_fail (priv->monitor_id == 0);
if (priv->monitor_id) if (priv->monitor_id)

View file

@ -144,7 +144,7 @@ init_inotify (NMInotifyHelper *self)
GIOChannel *channel; GIOChannel *channel;
guint source_id; guint source_id;
priv->ifd = inotify_init (); priv->ifd = inotify_init1 (IN_CLOEXEC);
if (priv->ifd == -1) { if (priv->ifd == -1) {
int errsv = errno; int errsv = errno;

View file

@ -641,11 +641,11 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
s->fd = -1; s->fd = -1;
if (create) 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) { if (!create || s->fd == -1) {
/* try read-only */ /* 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) if (s->fd == -1)
errsv = errno; errsv = errno;
else else
@ -1017,7 +1017,7 @@ svWriteFile (shvarFile *s, int mode, GError **error)
if (s->modified) { if (s->modified) {
if (s->fd == -1) 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) { if (s->fd == -1) {
int errsv = errno; int errsv = errno;

View file

@ -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); nm_log_warn (LOGD_SETTINGS, "interfaces file %s doesn't exist\n", eni_file);
return; return;
} }
inp = fopen (eni_file, "r"); inp = fopen (eni_file, "re");
if (inp == NULL) { if (inp == NULL) {
if (!quiet) if (!quiet)
nm_log_warn (LOGD_SETTINGS, "Can't open %s\n", eni_file); nm_log_warn (LOGD_SETTINGS, "Can't open %s\n", eni_file);

View file

@ -26,6 +26,7 @@
#include <netinet/ether.h> #include <netinet/ether.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <fcntl.h>
#include "NetworkManagerUtils.h" #include "NetworkManagerUtils.h"
#include "nm-multi-index.h" #include "nm-multi-index.h"
@ -173,7 +174,7 @@ test_nm_utils_kill_child_create_and_join_pgroup (void)
int pipefd[2]; int pipefd[2];
pid_t pgid; pid_t pgid;
err = pipe (pipefd); err = pipe2 (pipefd, O_CLOEXEC);
g_assert (err == 0); g_assert (err == 0);
pgid = fork(); pgid = fork();