2020-12-23 22:21:36 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2019-09-25 13:13:40 +02:00
|
|
|
/*
|
2015-04-14 22:34:01 +02:00
|
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-02-04 08:48:06 +01:00
|
|
|
#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2015-04-14 22:34:01 +02:00
|
|
|
#include "nm-platform-utils.h"
|
|
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
|
#include <linux/sockios.h>
|
2015-05-03 12:49:46 +02:00
|
|
|
#include <linux/mii.h>
|
2018-11-12 15:23:36 +01:00
|
|
|
#include <linux/if.h>
|
2015-06-01 12:06:12 +02:00
|
|
|
#include <linux/version.h>
|
2016-04-10 12:01:51 +02:00
|
|
|
#include <linux/rtnetlink.h>
|
2016-12-07 18:15:13 +08:00
|
|
|
#include <fcntl.h>
|
2017-03-12 15:54:02 +01:00
|
|
|
#include <libudev.h>
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2021-01-09 12:00:33 +01:00
|
|
|
#include "nm-base/nm-ethtool-base.h"
|
2021-01-10 09:55:06 +01:00
|
|
|
#include "nm-log-core/nm-logging.h"
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2021-01-08 18:39:27 +01:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
#define ONOFF(bool_val) ((bool_val) ? "on" : "off")
|
|
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
/******************************************************************************
|
2016-12-26 11:54:30 +01:00
|
|
|
* utils
|
2018-07-16 10:43:28 +02:00
|
|
|
*****************************************************************************/
|
2016-12-26 11:54:30 +01:00
|
|
|
|
2017-03-14 11:15:05 +01:00
|
|
|
extern char *if_indextoname(unsigned __ifindex, char *__ifname);
|
|
|
|
|
unsigned if_nametoindex(const char *__ifname);
|
2016-12-26 11:54:30 +01:00
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail(ifindex > 0, NULL);
|
|
|
|
|
g_return_val_if_fail(out_ifname, NULL);
|
|
|
|
|
|
|
|
|
|
return if_indextoname(ifindex, out_ifname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
nmp_utils_if_nametoindex(const char *ifname)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail(ifname, 0);
|
|
|
|
|
|
|
|
|
|
return if_nametoindex(ifname);
|
|
|
|
|
}
|
2016-12-08 13:55:17 +01:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2021-01-08 20:24:36 +01:00
|
|
|
NM_UTILS_LOOKUP_STR_DEFINE(nm_platform_link_duplex_type_to_string,
|
|
|
|
|
NMPlatformLinkDuplexType,
|
|
|
|
|
NM_UTILS_LOOKUP_DEFAULT_WARN(NULL),
|
|
|
|
|
NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"),
|
|
|
|
|
NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_FULL, "full"),
|
|
|
|
|
NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_HALF, "half"), );
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
typedef struct {
|
|
|
|
|
int fd;
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
const int ifindex;
|
2018-07-16 13:08:38 +02:00
|
|
|
char ifname[IFNAMSIZ];
|
|
|
|
|
} SocketHandle;
|
|
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
#define SOCKET_HANDLE_INIT(_ifindex) \
|
|
|
|
|
{ \
|
|
|
|
|
.fd = -1, .ifindex = (_ifindex), \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_nm_auto_socket_handle(SocketHandle *shandle)
|
|
|
|
|
{
|
|
|
|
|
if (shandle->fd >= 0)
|
|
|
|
|
nm_close(shandle->fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle)
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_NONE,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRDATA,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRU,
|
|
|
|
|
} IoctlCallDataType;
|
|
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
static int
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
_ioctl_call(const char * log_ioctl_type,
|
|
|
|
|
const char * log_subtype,
|
|
|
|
|
unsigned long int ioctl_request,
|
|
|
|
|
int ifindex,
|
|
|
|
|
int * inout_fd,
|
|
|
|
|
char * inout_ifname,
|
|
|
|
|
IoctlCallDataType edata_type,
|
|
|
|
|
gpointer edata,
|
|
|
|
|
gsize edata_size,
|
|
|
|
|
struct ifreq * out_ifreq)
|
2018-07-16 13:08:38 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_close int fd_close = -1;
|
|
|
|
|
int fd;
|
|
|
|
|
int r;
|
|
|
|
|
gpointer edata_backup = NULL;
|
|
|
|
|
gs_free gpointer edata_backup_free = NULL;
|
|
|
|
|
guint try_count;
|
|
|
|
|
char known_ifnames[2][IFNAMSIZ];
|
|
|
|
|
const char * failure_reason = NULL;
|
|
|
|
|
struct ifreq ifr;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_assert(ifindex > 0);
|
|
|
|
|
nm_assert(NM_IN_SET(edata_type,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_NONE,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRDATA,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRU));
|
|
|
|
|
nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0);
|
|
|
|
|
nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0);
|
|
|
|
|
nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRU
|
|
|
|
|
|| (edata_size > 0 && edata_size <= sizeof(ifr.ifr_ifru)));
|
|
|
|
|
nm_assert(edata_size == 0 || edata);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
/* open a file descriptor (or use the one provided). */
|
|
|
|
|
if (inout_fd && *inout_fd >= 0)
|
|
|
|
|
fd = *inout_fd;
|
|
|
|
|
else {
|
|
|
|
|
fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
r = -NM_ERRNO_NATIVE(errno);
|
|
|
|
|
failure_reason = "failed creating socket or ioctl";
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (inout_fd)
|
|
|
|
|
*inout_fd = fd;
|
|
|
|
|
else
|
|
|
|
|
fd_close = fd;
|
2018-07-16 13:08:38 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
/* resolve the ifindex to name (or use the one provided). */
|
|
|
|
|
if (inout_ifname && inout_ifname[0])
|
|
|
|
|
nm_utils_ifname_cpy(known_ifnames[0], inout_ifname);
|
|
|
|
|
else {
|
|
|
|
|
if (!nmp_utils_if_indextoname(ifindex, known_ifnames[0])) {
|
|
|
|
|
failure_reason = "cannot resolve ifindex";
|
|
|
|
|
r = -ENODEV;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (inout_ifname)
|
|
|
|
|
nm_utils_ifname_cpy(inout_ifname, known_ifnames[0]);
|
2018-07-16 13:08:38 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
/* we might need to retry the request. Backup edata so that we can
|
|
|
|
|
* restore it on retry. */
|
|
|
|
|
if (edata_size > 0)
|
|
|
|
|
edata_backup = nm_memdup_maybe_a(500, edata, edata_size, &edata_backup_free);
|
|
|
|
|
|
|
|
|
|
try_count = 0;
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
{
|
|
|
|
|
const char *ifname = known_ifnames[try_count % 2];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_assert(ifindex > 0);
|
2020-02-12 18:01:13 +01:00
|
|
|
nm_assert(ifname && nm_utils_ifname_valid_kernel(ifname, NULL));
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_assert(fd >= 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-05-15 21:00:52 +02:00
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
2019-05-16 10:11:39 +02:00
|
|
|
nm_utils_ifname_cpy(ifr.ifr_name, ifname);
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA)
|
|
|
|
|
ifr.ifr_data = edata;
|
|
|
|
|
else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU)
|
|
|
|
|
memcpy(&ifr.ifr_ifru, edata, NM_MIN(edata_size, sizeof(ifr.ifr_ifru)));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (ioctl(fd, ioctl_request, &ifr) < 0) {
|
|
|
|
|
r = -NM_ERRNO_NATIVE(errno);
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"%s[%d]: %s, %s: failed: %s",
|
|
|
|
|
log_ioctl_type,
|
|
|
|
|
ifindex,
|
|
|
|
|
log_subtype,
|
|
|
|
|
ifname,
|
|
|
|
|
nm_strerror_native(-r));
|
|
|
|
|
} else {
|
|
|
|
|
r = 0;
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"%s[%d]: %s, %s: success",
|
|
|
|
|
log_ioctl_type,
|
|
|
|
|
ifindex,
|
|
|
|
|
log_subtype,
|
|
|
|
|
ifname);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
try_count++;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
/* resolve the name again to see whether the ifindex still has the same name. */
|
|
|
|
|
if (!nmp_utils_if_indextoname(ifindex, known_ifnames[try_count % 2])) {
|
|
|
|
|
/* we could not find the ifindex again. Probably the device just got
|
|
|
|
|
* removed.
|
|
|
|
|
*
|
|
|
|
|
* In both cases we return the error code we got from ioctl above.
|
|
|
|
|
* Either it failed because the device was gone already or it still
|
|
|
|
|
* managed to complete the call. In both cases, the error code is good. */
|
|
|
|
|
failure_reason =
|
|
|
|
|
"cannot resolve ifindex after ioctl call. Probably the device was just removed";
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check whether the ifname changed in the meantime. If yes, would render the result
|
|
|
|
|
* invalid. Note that this cannot detect every race regarding renames, for example:
|
|
|
|
|
*
|
|
|
|
|
* - if_indextoname(#10) gives eth0
|
|
|
|
|
* - rename(#10) => eth0_tmp
|
|
|
|
|
* - rename(#11) => eth0
|
|
|
|
|
* - ioctl(eth0) (wrongly fetching #11, formerly eth1)
|
|
|
|
|
* - rename(#11) => eth_something
|
|
|
|
|
* - rename(#10) => eth0
|
|
|
|
|
* - if_indextoname(#10) gives eth0
|
|
|
|
|
*/
|
|
|
|
|
if (!nm_streq(known_ifnames[0], known_ifnames[1])) {
|
|
|
|
|
gboolean retry;
|
|
|
|
|
|
|
|
|
|
/* we detected a possible(!) rename.
|
|
|
|
|
*
|
|
|
|
|
* For getters it's straight forward to just retry the call.
|
|
|
|
|
*
|
|
|
|
|
* For setters we also always retry. If our previous call operated on the right device,
|
|
|
|
|
* calling it again should have no bad effect (just setting the same thing more than once).
|
|
|
|
|
*
|
|
|
|
|
* The only potential bad thing is if there was a race involving swapping names, and we just
|
2020-07-04 11:37:01 +03:00
|
|
|
* set the ioctl option on the wrong device. But then the bad thing already happenned and
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
* we cannot detect it (nor do anything about it). At least, we can retry and set the
|
|
|
|
|
* option on the right interface. */
|
|
|
|
|
retry = (try_count < 5);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s",
|
|
|
|
|
log_ioctl_type,
|
|
|
|
|
ifindex,
|
|
|
|
|
log_subtype,
|
|
|
|
|
known_ifnames[(try_count - 1) % 2],
|
|
|
|
|
known_ifnames[try_count % 2],
|
|
|
|
|
retry ? "Retry" : "No retry");
|
|
|
|
|
if (inout_ifname)
|
|
|
|
|
nm_utils_ifname_cpy(inout_ifname, known_ifnames[try_count % 2]);
|
|
|
|
|
if (retry) {
|
|
|
|
|
if (edata_size > 0)
|
|
|
|
|
memcpy(edata, edata_backup, edata_size);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
2018-07-16 13:08:38 +02:00
|
|
|
}
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (failure_reason) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"%s[%d]: %s: %s: %s",
|
|
|
|
|
log_ioctl_type,
|
|
|
|
|
ifindex,
|
|
|
|
|
log_subtype,
|
|
|
|
|
failure_reason,
|
|
|
|
|
r < 0 ? nm_strerror_native(-r) : "assume success");
|
|
|
|
|
}
|
|
|
|
|
if (r >= 0)
|
|
|
|
|
NM_SET_OUT(out_ifreq, ifr);
|
|
|
|
|
return r;
|
2018-07-16 13:08:38 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
/******************************************************************************
|
2015-05-02 07:59:59 +02:00
|
|
|
* ethtool
|
2018-07-16 10:43:28 +02:00
|
|
|
*****************************************************************************/
|
2016-12-08 13:55:17 +01:00
|
|
|
|
2020-02-13 14:55:26 +01:00
|
|
|
static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string,
|
|
|
|
|
guint32,
|
2020-05-07 17:11:34 +02:00
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_GCOALESCE, "ETHTOOL_GCOALESCE"),
|
2016-12-11 22:46:14 +01:00
|
|
|
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"),
|
2020-05-14 18:27:54 +02:00
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"),
|
2016-12-11 22:46:14 +01:00
|
|
|
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"),
|
2020-05-07 17:11:34 +02:00
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"),
|
2018-07-16 15:42:07 +02:00
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"),
|
2020-05-14 18:27:54 +02:00
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"),
|
2016-12-11 22:46:14 +01:00
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"),
|
|
|
|
|
NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), );
|
|
|
|
|
|
|
|
|
|
static const char *
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
_ethtool_edata_to_string(gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len)
|
2016-12-11 22:46:14 +01:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_assert(edata);
|
|
|
|
|
nm_assert(edata_size >= sizeof(guint32));
|
|
|
|
|
nm_assert((((intptr_t) edata) % _nm_alignof(guint32)) == 0);
|
|
|
|
|
|
|
|
|
|
return _ethtool_cmd_to_string(*((guint32 *) edata), sbuf, sbuf_len);
|
2016-12-11 22:46:14 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
|
|
|
|
|
#define ethtool_cmd_speed(pedata) ((pedata)->speed)
|
|
|
|
|
|
|
|
|
|
#define ethtool_cmd_speed_set(pedata, speed) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
|
|
|
|
(pedata)->speed = (guint16)(speed); \
|
|
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
static int
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
_ethtool_call_handle(SocketHandle *shandle, gpointer edata, gsize edata_size)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
2016-12-11 22:46:14 +01:00
|
|
|
char sbuf[50];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
return _ioctl_call("ethtool",
|
|
|
|
|
_ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)),
|
|
|
|
|
SIOCETHTOOL,
|
|
|
|
|
shandle->ifindex,
|
|
|
|
|
&shandle->fd,
|
|
|
|
|
shandle->ifname,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRDATA,
|
|
|
|
|
edata,
|
|
|
|
|
edata_size,
|
|
|
|
|
NULL);
|
2018-07-16 13:08:38 +02:00
|
|
|
}
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
static int
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
_ethtool_call_once(int ifindex, gpointer edata, gsize edata_size)
|
2018-07-16 13:08:38 +02:00
|
|
|
{
|
|
|
|
|
char sbuf[50];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
return _ioctl_call("ethtool",
|
|
|
|
|
_ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)),
|
|
|
|
|
SIOCETHTOOL,
|
|
|
|
|
ifindex,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRDATA,
|
|
|
|
|
edata,
|
|
|
|
|
edata_size,
|
|
|
|
|
NULL);
|
2015-05-02 07:59:59 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static struct ethtool_gstrings *
|
2018-07-16 13:08:38 +02:00
|
|
|
ethtool_get_stringset(SocketHandle *shandle, int stringset_id)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
2020-02-10 10:51:49 +01:00
|
|
|
struct {
|
|
|
|
|
struct ethtool_sset_info info;
|
|
|
|
|
guint32 sentinel;
|
|
|
|
|
} sset_info = {
|
|
|
|
|
.info.cmd = ETHTOOL_GSSET_INFO,
|
|
|
|
|
.info.reserved = 0,
|
|
|
|
|
.info.sset_mask = (1ULL << stringset_id),
|
|
|
|
|
};
|
2020-02-10 10:55:12 +01:00
|
|
|
const guint32 * pdata;
|
2018-07-16 10:43:28 +02:00
|
|
|
gs_free struct ethtool_gstrings *gstrings = NULL;
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
gsize gstrings_len;
|
2018-07-16 10:43:28 +02:00
|
|
|
guint32 i, len;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-02-10 10:51:49 +01:00
|
|
|
if (_ethtool_call_handle(shandle, &sset_info, sizeof(sset_info)) < 0)
|
2018-07-16 10:43:28 +02:00
|
|
|
return NULL;
|
2020-02-10 10:51:49 +01:00
|
|
|
if (!sset_info.info.sset_mask)
|
2018-07-16 10:43:28 +02:00
|
|
|
return NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-02-10 10:55:12 +01:00
|
|
|
pdata = (guint32 *) sset_info.info.data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-02-10 10:55:12 +01:00
|
|
|
len = *pdata;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
gstrings_len = sizeof(*gstrings) + (len * ETH_GSTRING_LEN);
|
|
|
|
|
gstrings = g_malloc0(gstrings_len);
|
2018-07-16 10:43:28 +02:00
|
|
|
gstrings->cmd = ETHTOOL_GSTRINGS;
|
|
|
|
|
gstrings->string_set = stringset_id;
|
|
|
|
|
gstrings->len = len;
|
|
|
|
|
if (gstrings->len > 0) {
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_handle(shandle, gstrings, gstrings_len) < 0)
|
2018-07-16 10:43:28 +02:00
|
|
|
return NULL;
|
|
|
|
|
for (i = 0; i < gstrings->len; i++) {
|
|
|
|
|
/* ensure NUL terminated */
|
|
|
|
|
gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
return g_steal_pointer(&gstrings);
|
|
|
|
|
}
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
static int
|
|
|
|
|
ethtool_gstrings_find(const struct ethtool_gstrings *gstrings, const char *needle)
|
|
|
|
|
{
|
|
|
|
|
guint32 i;
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
/* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN.
|
|
|
|
|
* that means, we cannot possibly request longer names. */
|
|
|
|
|
nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN);
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
for (i = 0; i < gstrings->len; i++) {
|
|
|
|
|
if (nm_streq((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle))
|
2015-05-02 07:59:59 +02:00
|
|
|
return i;
|
|
|
|
|
}
|
2018-07-16 10:43:28 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2018-07-16 13:08:38 +02:00
|
|
|
ethtool_get_stringset_index(SocketHandle *shandle, int stringset_id, const char *needle)
|
2018-07-16 10:43:28 +02:00
|
|
|
{
|
|
|
|
|
gs_free struct ethtool_gstrings *gstrings = NULL;
|
|
|
|
|
|
|
|
|
|
/* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN.
|
|
|
|
|
* that means, we cannot possibly request longer names. */
|
|
|
|
|
nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN);
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
gstrings = ethtool_get_stringset(shandle, stringset_id);
|
2018-07-16 10:43:28 +02:00
|
|
|
if (gstrings)
|
|
|
|
|
return ethtool_gstrings_find(gstrings, needle);
|
2015-05-02 07:59:59 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static const NMEthtoolFeatureInfo _ethtool_feature_infos[_NM_ETHTOOL_ID_FEATURE_NUM] = {
|
|
|
|
|
#define ETHT_FEAT(eid, ...) \
|
|
|
|
|
{ \
|
|
|
|
|
.ethtool_id = eid, .n_kernel_names = NM_NARG(__VA_ARGS__), \
|
|
|
|
|
.kernel_names = ((const char *const[]){__VA_ARGS__}), \
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
/* the order does only matter for one thing: if it happens that more than one NMEthtoolID
|
|
|
|
|
* reference the same kernel-name, then the one that is mentioned *later* will win in
|
|
|
|
|
* case these NMEthtoolIDs are set. That mostly only makes sense for ethtool-ids which
|
|
|
|
|
* refer to multiple features ("feature-tso"), while also having more specific ids
|
|
|
|
|
* ("feature-tx-tcp-segmentation"). */
|
|
|
|
|
|
|
|
|
|
/* names from ethtool utility, which are aliases for multiple features. */
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_SG, "tx-scatter-gather", "tx-scatter-gather-fraglist"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TSO,
|
|
|
|
|
"tx-tcp-segmentation",
|
|
|
|
|
"tx-tcp-ecn-segmentation",
|
|
|
|
|
"tx-tcp-mangleid-segmentation",
|
|
|
|
|
"tx-tcp6-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX,
|
|
|
|
|
"tx-checksum-ipv4",
|
|
|
|
|
"tx-checksum-ip-generic",
|
|
|
|
|
"tx-checksum-ipv6",
|
|
|
|
|
"tx-checksum-fcoe-crc",
|
|
|
|
|
"tx-checksum-sctp"),
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
/* names from ethtool utility, which are aliases for one feature. */
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"),
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
/* names of features, as known by kernel. */
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"),
|
2021-01-27 13:50:48 +01:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, "macsec-hw-offload"),
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"),
|
2021-01-27 13:50:48 +01:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, "rx-gro-list"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, "rx-udp-gro-forwarding"),
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"),
|
2021-01-27 13:50:48 +01:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, "tls-hw-rx-offload"),
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"),
|
2021-01-27 13:50:48 +01:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, "tx-gso-list"),
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"),
|
2021-01-27 13:50:48 +01:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION,
|
|
|
|
|
"tx-tunnel-remcsum-segmentation"),
|
2018-08-09 20:17:02 +02:00
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"),
|
|
|
|
|
ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"),
|
2018-07-16 15:42:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* the number of kernel features that we handle. It essentially is the sum of all
|
|
|
|
|
* kernel_names. So, all ethtool-ids that reference exactly one kernel-name
|
|
|
|
|
* (_NM_ETHTOOL_ID_FEATURE_NUM) + some extra, for ethtool-ids that are aliases
|
|
|
|
|
* for multiple kernel-names. */
|
|
|
|
|
#define N_ETHTOOL_KERNEL_FEATURES (((guint) _NM_ETHTOOL_ID_FEATURE_NUM) + 8u)
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_ASSERT_ethtool_feature_infos(void)
|
|
|
|
|
{
|
|
|
|
|
#if NM_MORE_ASSERTS > 10
|
|
|
|
|
guint i, k, n;
|
|
|
|
|
bool found[_NM_ETHTOOL_ID_FEATURE_NUM] = {};
|
|
|
|
|
|
|
|
|
|
G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(_ethtool_feature_infos) == _NM_ETHTOOL_ID_FEATURE_NUM);
|
|
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(_ethtool_feature_infos); i++) {
|
|
|
|
|
NMEthtoolFeatureState kstate;
|
|
|
|
|
const NMEthtoolFeatureInfo *inf = &_ethtool_feature_infos[i];
|
|
|
|
|
|
|
|
|
|
g_assert(inf->ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST);
|
|
|
|
|
g_assert(inf->ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST);
|
|
|
|
|
g_assert(inf->n_kernel_names > 0);
|
|
|
|
|
|
|
|
|
|
for (k = 0; k < i; k++)
|
|
|
|
|
g_assert(inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id);
|
|
|
|
|
|
2020-05-14 21:20:05 +02:00
|
|
|
g_assert(!found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)]);
|
|
|
|
|
found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)] = TRUE;
|
2018-07-16 15:42:07 +02:00
|
|
|
|
|
|
|
|
kstate.idx_kernel_name = inf->n_kernel_names - 1;
|
|
|
|
|
g_assert((guint) kstate.idx_kernel_name == (guint)(inf->n_kernel_names - 1));
|
|
|
|
|
|
|
|
|
|
n += inf->n_kernel_names;
|
|
|
|
|
for (k = 0; k < inf->n_kernel_names; k++) {
|
2021-01-27 13:50:48 +01:00
|
|
|
const char *name = inf->kernel_names[k];
|
|
|
|
|
|
|
|
|
|
g_assert(nm_utils_strv_find_first((char **) inf->kernel_names, k, name) < 0);
|
|
|
|
|
|
|
|
|
|
/* these offload features are only informational and cannot be set from user-space
|
|
|
|
|
* (NETIF_F_NEVER_CHANGE). We should not track them in _ethtool_feature_infos. */
|
|
|
|
|
g_assert(!nm_streq(name, "netns-local"));
|
|
|
|
|
g_assert(!nm_streq(name, "tx-lockless"));
|
|
|
|
|
g_assert(!nm_streq(name, "vlan-challenged"));
|
2018-07-16 15:42:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++)
|
|
|
|
|
g_assert(found[i]);
|
|
|
|
|
|
|
|
|
|
g_assert(n == N_ETHTOOL_KERNEL_FEATURES);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NMEthtoolFeatureStates *
|
|
|
|
|
ethtool_get_features(SocketHandle *shandle)
|
|
|
|
|
{
|
|
|
|
|
gs_free NMEthtoolFeatureStates * states = NULL;
|
|
|
|
|
gs_free struct ethtool_gstrings *ss_features = NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
_ASSERT_ethtool_feature_infos();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
ss_features = ethtool_get_stringset(shandle, ETH_SS_FEATURES);
|
|
|
|
|
if (!ss_features)
|
|
|
|
|
return NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (ss_features->len > 0) {
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
gs_free struct ethtool_gfeatures * gfeatures_free = NULL;
|
|
|
|
|
struct ethtool_gfeatures * gfeatures;
|
|
|
|
|
gsize gfeatures_len;
|
2018-07-16 15:42:07 +02:00
|
|
|
guint idx;
|
|
|
|
|
const NMEthtoolFeatureState * states_list0 = NULL;
|
|
|
|
|
const NMEthtoolFeatureState *const *states_plist0 = NULL;
|
|
|
|
|
guint states_plist_n = 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
gfeatures_len = sizeof(struct ethtool_gfeatures)
|
|
|
|
|
+ (NM_DIV_ROUND_UP(ss_features->len, 32u) * sizeof(gfeatures->features[0]));
|
|
|
|
|
gfeatures = nm_malloc0_maybe_a(300, gfeatures_len, &gfeatures_free);
|
2018-07-16 15:42:07 +02:00
|
|
|
gfeatures->cmd = ETHTOOL_GFEATURES;
|
|
|
|
|
gfeatures->size = NM_DIV_ROUND_UP(ss_features->len, 32u);
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_handle(shandle, gfeatures, gfeatures_len) < 0)
|
2018-07-16 15:42:07 +02:00
|
|
|
return NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
for (idx = 0; idx < G_N_ELEMENTS(_ethtool_feature_infos); idx++) {
|
|
|
|
|
const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx];
|
|
|
|
|
guint idx_kernel_name;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
for (idx_kernel_name = 0; idx_kernel_name < info->n_kernel_names; idx_kernel_name++) {
|
|
|
|
|
NMEthtoolFeatureState *kstate;
|
|
|
|
|
const char * kernel_name = info->kernel_names[idx_kernel_name];
|
|
|
|
|
int i_feature;
|
|
|
|
|
guint i_block;
|
|
|
|
|
guint32 i_flag;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
i_feature = ethtool_gstrings_find(ss_features, kernel_name);
|
|
|
|
|
if (i_feature < 0)
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
i_block = ((guint) i_feature) / 32u;
|
|
|
|
|
i_flag = (guint32)(1u << (((guint) i_feature) % 32u));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (!states) {
|
|
|
|
|
states = g_malloc0(
|
|
|
|
|
sizeof(NMEthtoolFeatureStates)
|
|
|
|
|
+ (N_ETHTOOL_KERNEL_FEATURES * sizeof(NMEthtoolFeatureState))
|
|
|
|
|
+ ((N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos))
|
|
|
|
|
* sizeof(NMEthtoolFeatureState *)));
|
|
|
|
|
states_list0 = &states->states_list[0];
|
|
|
|
|
states_plist0 = (gpointer) &states_list0[N_ETHTOOL_KERNEL_FEATURES];
|
|
|
|
|
states->n_ss_features = ss_features->len;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_assert(states->n_states < N_ETHTOOL_KERNEL_FEATURES);
|
|
|
|
|
kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states];
|
|
|
|
|
states->n_states++;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
kstate->info = info;
|
|
|
|
|
kstate->idx_ss_features = i_feature;
|
|
|
|
|
kstate->idx_kernel_name = idx_kernel_name;
|
|
|
|
|
kstate->available = !!(gfeatures->features[i_block].available & i_flag);
|
|
|
|
|
kstate->requested = !!(gfeatures->features[i_block].requested & i_flag);
|
|
|
|
|
kstate->active = !!(gfeatures->features[i_block].active & i_flag);
|
|
|
|
|
kstate->never_changed = !!(gfeatures->features[i_block].never_changed & i_flag);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_assert(states_plist_n
|
|
|
|
|
< N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 21:20:05 +02:00
|
|
|
if (!states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)])
|
|
|
|
|
states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)] =
|
|
|
|
|
&states_plist0[states_plist_n];
|
2018-07-16 15:42:07 +02:00
|
|
|
((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate;
|
|
|
|
|
states_plist_n++;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 21:20:05 +02:00
|
|
|
if (states && states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) {
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_assert(states_plist_n
|
|
|
|
|
< N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos));
|
|
|
|
|
nm_assert(!states_plist0[states_plist_n]);
|
|
|
|
|
states_plist_n++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
return g_steal_pointer(&states);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMEthtoolFeatureStates *
|
|
|
|
|
nmp_utils_ethtool_get_features(int ifindex)
|
|
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2018-07-16 15:42:07 +02:00
|
|
|
NMEthtoolFeatureStates * features;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
g_return_val_if_fail(ifindex > 0, 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
features = ethtool_get_features(&shandle);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (!features) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: failure getting features",
|
|
|
|
|
ifindex,
|
|
|
|
|
"get-features");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: retrieved kernel features",
|
|
|
|
|
ifindex,
|
|
|
|
|
"get-features");
|
|
|
|
|
return features;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
_ethtool_feature_state_to_string(char * buf,
|
|
|
|
|
gsize buf_size,
|
|
|
|
|
const NMEthtoolFeatureState *s,
|
|
|
|
|
const char * prefix)
|
|
|
|
|
{
|
|
|
|
|
int l;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
l = g_snprintf(buf,
|
|
|
|
|
buf_size,
|
|
|
|
|
"%s %s%s",
|
|
|
|
|
prefix ?: "",
|
|
|
|
|
ONOFF(s->active),
|
|
|
|
|
(!s->available || s->never_changed)
|
|
|
|
|
? ", [fixed]"
|
|
|
|
|
: ((s->requested != s->active)
|
|
|
|
|
? (s->requested ? ", [requested on]" : ", [requested off]")
|
|
|
|
|
: ""));
|
|
|
|
|
nm_assert(l < buf_size);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nmp_utils_ethtool_set_features(
|
|
|
|
|
int ifindex,
|
|
|
|
|
const NMEthtoolFeatureStates *features,
|
2021-01-10 16:51:37 +01:00
|
|
|
const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */,
|
|
|
|
|
gboolean do_set /* or reset */)
|
2018-07-16 15:42:07 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
|
|
|
|
gs_free struct ethtool_sfeatures * sfeatures_free = NULL;
|
|
|
|
|
struct ethtool_sfeatures * sfeatures;
|
|
|
|
|
gsize sfeatures_len;
|
2018-07-16 15:42:07 +02:00
|
|
|
int r;
|
|
|
|
|
guint i, j;
|
|
|
|
|
struct {
|
|
|
|
|
const NMEthtoolFeatureState *f_state;
|
2021-01-10 16:51:37 +01:00
|
|
|
NMOptionBool requested;
|
2018-07-16 15:42:07 +02:00
|
|
|
} set_states[N_ETHTOOL_KERNEL_FEATURES];
|
|
|
|
|
guint set_states_n = 0;
|
|
|
|
|
gboolean success = TRUE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
g_return_val_if_fail(ifindex > 0, 0);
|
|
|
|
|
g_return_val_if_fail(features, 0);
|
|
|
|
|
g_return_val_if_fail(requested, 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_assert(features->n_states <= N_ETHTOOL_KERNEL_FEATURES);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) {
|
|
|
|
|
const NMEthtoolFeatureState *const *states_indexed;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-01-10 16:51:37 +01:00
|
|
|
if (requested[i] == NM_OPTION_BOOL_DEFAULT)
|
2018-07-16 15:42:07 +02:00
|
|
|
continue;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (!(states_indexed = features->states_indexed[i])) {
|
|
|
|
|
if (do_set) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: set feature %s: skip (not found)",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-features",
|
|
|
|
|
nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname);
|
|
|
|
|
success = FALSE;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
for (j = 0; states_indexed[j]; j++) {
|
|
|
|
|
const NMEthtoolFeatureState *s = states_indexed[j];
|
|
|
|
|
char sbuf[255];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (set_states_n >= G_N_ELEMENTS(set_states))
|
|
|
|
|
g_return_val_if_reached(FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (s->never_changed) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: %s feature %s (%s): %s, %s (skip feature marked as "
|
|
|
|
|
"never changed)",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-features",
|
|
|
|
|
do_set ? "set" : "reset",
|
|
|
|
|
nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname,
|
|
|
|
|
s->info->kernel_names[s->idx_kernel_name],
|
2021-01-10 16:51:37 +01:00
|
|
|
ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active),
|
2018-07-16 15:42:07 +02:00
|
|
|
_ethtool_feature_state_to_string(sbuf,
|
|
|
|
|
sizeof(sbuf),
|
|
|
|
|
s,
|
|
|
|
|
do_set ? " currently:" : " before:"));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: %s feature %s (%s): %s, %s",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-features",
|
|
|
|
|
do_set ? "set" : "reset",
|
|
|
|
|
nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname,
|
|
|
|
|
s->info->kernel_names[s->idx_kernel_name],
|
2021-01-10 16:51:37 +01:00
|
|
|
ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active),
|
2018-07-16 15:42:07 +02:00
|
|
|
_ethtool_feature_state_to_string(sbuf,
|
|
|
|
|
sizeof(sbuf),
|
|
|
|
|
s,
|
|
|
|
|
do_set ? " currently:" : " before:"));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (do_set && (!s->available || s->never_changed)
|
2021-01-10 16:51:37 +01:00
|
|
|
&& (s->active != (requested[i] == NM_OPTION_BOOL_TRUE))) {
|
2018-07-16 15:42:07 +02:00
|
|
|
/* we request to change a flag which kernel reported as fixed.
|
|
|
|
|
* While the ethtool operation will silently succeed, mark the request
|
|
|
|
|
* as failure. */
|
|
|
|
|
success = FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
set_states[set_states_n].f_state = s;
|
|
|
|
|
set_states[set_states_n].requested = requested[i];
|
|
|
|
|
set_states_n++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (set_states_n == 0) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: no feature requested",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-features");
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
sfeatures_len =
|
|
|
|
|
sizeof(struct ethtool_sfeatures)
|
|
|
|
|
+ (NM_DIV_ROUND_UP(features->n_ss_features, 32U) * sizeof(sfeatures->features[0]));
|
|
|
|
|
sfeatures = nm_malloc0_maybe_a(300, sfeatures_len, &sfeatures_free);
|
2018-07-16 15:42:07 +02:00
|
|
|
sfeatures->cmd = ETHTOOL_SFEATURES;
|
|
|
|
|
sfeatures->size = NM_DIV_ROUND_UP(features->n_ss_features, 32U);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
for (i = 0; i < set_states_n; i++) {
|
|
|
|
|
const NMEthtoolFeatureState *s = set_states[i].f_state;
|
|
|
|
|
guint i_block;
|
|
|
|
|
guint32 i_flag;
|
|
|
|
|
gboolean is_requested;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
i_block = s->idx_ss_features / 32u;
|
|
|
|
|
i_flag = (guint32)(1u << (s->idx_ss_features % 32u));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
sfeatures->features[i_block].valid |= i_flag;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (do_set)
|
2021-01-10 16:51:37 +01:00
|
|
|
is_requested = (set_states[i].requested == NM_OPTION_BOOL_TRUE);
|
2018-07-16 15:42:07 +02:00
|
|
|
else
|
|
|
|
|
is_requested = s->active;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
if (is_requested)
|
|
|
|
|
sfeatures->features[i_block].requested |= i_flag;
|
|
|
|
|
else
|
|
|
|
|
sfeatures->features[i_block].requested &= ~i_flag;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
r = _ethtool_call_handle(&shandle, sfeatures, sfeatures_len);
|
|
|
|
|
if (r < 0) {
|
2018-07-16 15:42:07 +02:00
|
|
|
success = FALSE;
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: failure setting features (%s)",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-features",
|
2019-01-31 17:08:03 +01:00
|
|
|
nm_strerror_native(-r));
|
2018-07-16 15:42:07 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: %s",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-features",
|
|
|
|
|
success ? "successfully setting features"
|
2018-09-14 23:49:20 -04:00
|
|
|
: "at least some of the features were not successfully set");
|
2018-07-16 15:42:07 +02:00
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
static gboolean
|
|
|
|
|
ethtool_get_coalesce(SocketHandle *shandle, NMEthtoolCoalesceState *coalesce)
|
|
|
|
|
{
|
|
|
|
|
struct ethtool_coalesce eth_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
eth_data.cmd = ETHTOOL_GCOALESCE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) != 0)
|
|
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 09:16:31 +02:00
|
|
|
*coalesce = (NMEthtoolCoalesceState){
|
|
|
|
|
.s = {
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)] =
|
|
|
|
|
eth_data.rx_coalesce_usecs,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)] =
|
|
|
|
|
eth_data.rx_max_coalesced_frames,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)] =
|
|
|
|
|
eth_data.rx_coalesce_usecs_irq,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)] =
|
|
|
|
|
eth_data.rx_max_coalesced_frames_irq,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)] =
|
|
|
|
|
eth_data.tx_coalesce_usecs,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)] =
|
|
|
|
|
eth_data.tx_max_coalesced_frames,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)] =
|
|
|
|
|
eth_data.tx_coalesce_usecs_irq,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)] =
|
|
|
|
|
eth_data.tx_max_coalesced_frames_irq,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)] =
|
|
|
|
|
eth_data.stats_block_coalesce_usecs,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)] =
|
|
|
|
|
eth_data.use_adaptive_rx_coalesce,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)] =
|
|
|
|
|
eth_data.use_adaptive_tx_coalesce,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)] =
|
|
|
|
|
eth_data.pkt_rate_low,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)] =
|
|
|
|
|
eth_data.rx_coalesce_usecs_low,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)] =
|
|
|
|
|
eth_data.rx_max_coalesced_frames_low,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)] =
|
|
|
|
|
eth_data.tx_coalesce_usecs_low,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)] =
|
|
|
|
|
eth_data.tx_max_coalesced_frames_low,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)] =
|
|
|
|
|
eth_data.pkt_rate_high,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)] =
|
|
|
|
|
eth_data.rx_coalesce_usecs_high,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)] =
|
|
|
|
|
eth_data.rx_max_coalesced_frames_high,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)] =
|
|
|
|
|
eth_data.tx_coalesce_usecs_high,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)] =
|
|
|
|
|
eth_data.tx_max_coalesced_frames_high,
|
|
|
|
|
[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)] =
|
|
|
|
|
eth_data.rate_sample_interval,
|
|
|
|
|
}};
|
2020-05-07 17:11:34 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-14 09:16:30 +02:00
|
|
|
gboolean
|
|
|
|
|
nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce)
|
2020-05-07 17:11:34 +02:00
|
|
|
{
|
|
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 09:16:30 +02:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail(coalesce, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 09:16:30 +02:00
|
|
|
if (!ethtool_get_coalesce(&shandle, coalesce)) {
|
2020-05-07 17:11:34 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: failure getting coalesce settings",
|
|
|
|
|
ifindex,
|
|
|
|
|
"get-coalesce");
|
2020-05-14 09:16:30 +02:00
|
|
|
return FALSE;
|
2020-05-07 17:11:34 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: retrieved kernel coalesce settings",
|
|
|
|
|
ifindex,
|
|
|
|
|
"get-coalesce");
|
2020-05-14 09:16:30 +02:00
|
|
|
return TRUE;
|
2020-05-07 17:11:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ethtool_set_coalesce(SocketHandle *shandle, const NMEthtoolCoalesceState *coalesce)
|
|
|
|
|
{
|
|
|
|
|
struct ethtool_coalesce eth_data;
|
2020-05-14 09:16:31 +02:00
|
|
|
gboolean success;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 09:16:31 +02:00
|
|
|
nm_assert(shandle);
|
|
|
|
|
nm_assert(coalesce);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
eth_data = (struct ethtool_coalesce){
|
|
|
|
|
.cmd = ETHTOOL_SCOALESCE,
|
2020-05-14 09:16:31 +02:00
|
|
|
.rx_coalesce_usecs =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)],
|
|
|
|
|
.rx_max_coalesced_frames =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)],
|
|
|
|
|
.rx_coalesce_usecs_irq =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)],
|
|
|
|
|
.rx_max_coalesced_frames_irq =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)],
|
|
|
|
|
.tx_coalesce_usecs =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)],
|
|
|
|
|
.tx_max_coalesced_frames =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)],
|
|
|
|
|
.tx_coalesce_usecs_irq =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)],
|
|
|
|
|
.tx_max_coalesced_frames_irq =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)],
|
|
|
|
|
.stats_block_coalesce_usecs =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)],
|
|
|
|
|
.use_adaptive_rx_coalesce =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)],
|
|
|
|
|
.use_adaptive_tx_coalesce =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)],
|
|
|
|
|
.pkt_rate_low =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)],
|
|
|
|
|
.rx_coalesce_usecs_low =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)],
|
|
|
|
|
.rx_max_coalesced_frames_low =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)],
|
|
|
|
|
.tx_coalesce_usecs_low =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)],
|
|
|
|
|
.tx_max_coalesced_frames_low =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)],
|
|
|
|
|
.pkt_rate_high =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)],
|
|
|
|
|
.rx_coalesce_usecs_high =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)],
|
|
|
|
|
.rx_max_coalesced_frames_high =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)],
|
|
|
|
|
.tx_coalesce_usecs_high =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)],
|
|
|
|
|
.tx_max_coalesced_frames_high =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)],
|
|
|
|
|
.rate_sample_interval =
|
|
|
|
|
coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)],
|
2020-05-07 17:11:34 +02:00
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) == 0);
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-07 17:11:34 +02:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail(coalesce, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 09:16:30 +02:00
|
|
|
if (!ethtool_set_coalesce(&shandle, coalesce)) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: failure setting coalesce settings",
|
2020-05-07 17:11:34 +02:00
|
|
|
ifindex,
|
2020-05-14 09:16:30 +02:00
|
|
|
"set-coalesce");
|
2020-05-07 17:11:34 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 09:16:30 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: set kernel coalesce settings",
|
2020-05-07 17:11:34 +02:00
|
|
|
ifindex,
|
2020-05-14 09:16:30 +02:00
|
|
|
"set-coalesce");
|
2020-05-07 17:11:34 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
static gboolean
|
|
|
|
|
ethtool_get_ring(SocketHandle *shandle, NMEthtoolRingState *ring)
|
|
|
|
|
{
|
|
|
|
|
struct ethtool_ringparam eth_data;
|
|
|
|
|
|
|
|
|
|
eth_data.cmd = ETHTOOL_GRINGPARAM;
|
|
|
|
|
|
|
|
|
|
if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) != 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
ring->rx_pending = eth_data.rx_pending;
|
|
|
|
|
ring->rx_jumbo_pending = eth_data.rx_jumbo_pending;
|
|
|
|
|
ring->rx_mini_pending = eth_data.rx_mini_pending;
|
|
|
|
|
ring->tx_pending = eth_data.tx_pending;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail(ring, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
if (!ethtool_get_ring(&shandle, ring)) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: failure getting ring settings",
|
|
|
|
|
ifindex,
|
|
|
|
|
"get-ring");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: retrieved kernel ring settings",
|
|
|
|
|
ifindex,
|
|
|
|
|
"get-ring");
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ethtool_set_ring(SocketHandle *shandle, const NMEthtoolRingState *ring)
|
|
|
|
|
{
|
|
|
|
|
gboolean success;
|
|
|
|
|
struct ethtool_ringparam eth_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
g_return_val_if_fail(shandle, FALSE);
|
|
|
|
|
g_return_val_if_fail(ring, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
eth_data = (struct ethtool_ringparam){
|
|
|
|
|
.cmd = ETHTOOL_SRINGPARAM,
|
|
|
|
|
.rx_pending = ring->rx_pending,
|
|
|
|
|
.rx_jumbo_pending = ring->rx_jumbo_pending,
|
|
|
|
|
.rx_mini_pending = ring->rx_mini_pending,
|
|
|
|
|
.tx_pending = ring->tx_pending,
|
|
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) == 0);
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail(ring, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
if (!ethtool_set_ring(&shandle, ring)) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %s: failure setting ring settings",
|
|
|
|
|
ifindex,
|
|
|
|
|
"set-ring");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-05-14 18:27:54 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM, "ethtool[%d]: %s: set kernel ring settings", ifindex, "set-ring");
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 15:42:07 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
2016-12-12 13:47:52 +01:00
|
|
|
struct ethtool_drvinfo *drvinfo;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(*data) == sizeof(*drvinfo));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, driver)
|
|
|
|
|
== offsetof(struct ethtool_drvinfo, driver));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, version)
|
|
|
|
|
== offsetof(struct ethtool_drvinfo, version));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, fw_version)
|
|
|
|
|
== offsetof(struct ethtool_drvinfo, fw_version));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(data->driver) == sizeof(drvinfo->driver));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(data->version) == sizeof(drvinfo->version));
|
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(data->fw_version) == sizeof(drvinfo->fw_version));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2016-12-12 13:47:52 +01:00
|
|
|
g_return_val_if_fail(data, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-12 13:47:52 +01:00
|
|
|
drvinfo = (struct ethtool_drvinfo *) data;
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
*drvinfo = (struct ethtool_drvinfo){
|
|
|
|
|
.cmd = ETHTOOL_GDRVINFO,
|
|
|
|
|
};
|
|
|
|
|
return _ethtool_call_once(ifindex, drvinfo, sizeof(*drvinfo)) >= 0;
|
2015-05-02 07:59:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
2020-02-10 10:57:10 +01:00
|
|
|
struct {
|
|
|
|
|
struct ethtool_perm_addr e;
|
2021-01-08 18:45:16 +01:00
|
|
|
guint8 _extra_data[_NM_UTILS_HWADDR_LEN_MAX + 1];
|
2020-02-10 10:57:10 +01:00
|
|
|
} edata = {
|
|
|
|
|
.e.cmd = ETHTOOL_GPERMADDR,
|
2021-01-08 18:45:16 +01:00
|
|
|
.e.size = _NM_UTILS_HWADDR_LEN_MAX,
|
2020-02-10 10:57:10 +01:00
|
|
|
};
|
2020-02-10 11:01:43 +01:00
|
|
|
const guint8 *pdata;
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
|
2016-05-20 17:53:58 +02:00
|
|
|
guint i;
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2020-02-10 10:57:10 +01:00
|
|
|
if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0)
|
2015-05-02 07:59:59 +02:00
|
|
|
return FALSE;
|
|
|
|
|
|
2021-01-08 18:45:16 +01:00
|
|
|
if (edata.e.size > _NM_UTILS_HWADDR_LEN_MAX)
|
2016-05-20 17:53:58 +02:00
|
|
|
return FALSE;
|
2020-02-10 10:57:10 +01:00
|
|
|
if (edata.e.size < 1)
|
2015-09-17 19:53:56 +02:00
|
|
|
return FALSE;
|
|
|
|
|
|
2020-02-10 11:01:43 +01:00
|
|
|
pdata = (const guint8 *) edata.e.data;
|
|
|
|
|
|
|
|
|
|
if (NM_IN_SET(pdata[0], 0, 0xFF)) {
|
2016-05-20 17:53:58 +02:00
|
|
|
/* Some drivers might return a permanent address of all zeros.
|
|
|
|
|
* Reject that (rh#1264024)
|
|
|
|
|
*
|
|
|
|
|
* Some drivers return a permanent address of all ones. Reject that too */
|
2020-02-10 10:57:10 +01:00
|
|
|
for (i = 1; i < edata.e.size; i++) {
|
2020-02-10 11:01:43 +01:00
|
|
|
if (pdata[0] != pdata[i])
|
2016-05-20 17:53:58 +02:00
|
|
|
goto not_all_0or1;
|
|
|
|
|
}
|
2016-01-26 11:33:49 -06:00
|
|
|
return FALSE;
|
2016-05-20 17:53:58 +02:00
|
|
|
}
|
2016-01-26 11:33:49 -06:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
not_all_0or1:
|
2020-02-10 11:01:43 +01:00
|
|
|
memcpy(buf, pdata, edata.e.size);
|
2020-02-10 10:57:10 +01:00
|
|
|
*length = edata.e.size;
|
2015-05-02 07:59:59 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_supports_carrier_detect(int ifindex)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
|
|
|
|
struct ethtool_cmd edata = {.cmd = ETHTOOL_GLINK};
|
|
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
|
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
/* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we
|
|
|
|
|
* assume the device supports carrier-detect, otherwise we assume it
|
|
|
|
|
* doesn't.
|
|
|
|
|
*/
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
return _ethtool_call_once(ifindex, &edata, sizeof(edata)) >= 0;
|
2015-05-02 07:59:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_supports_vlans(int ifindex)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
|
|
|
|
gs_free struct ethtool_gfeatures * features_free = NULL;
|
|
|
|
|
struct ethtool_gfeatures * features;
|
|
|
|
|
gsize features_len;
|
2015-05-02 07:59:59 +02:00
|
|
|
int idx, block, bit, size;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
idx = ethtool_get_stringset_index(&shandle, ETH_SS_FEATURES, "vlan-challenged");
|
2018-07-16 10:43:28 +02:00
|
|
|
if (idx < 0) {
|
2018-07-16 13:08:38 +02:00
|
|
|
nm_log_dbg(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: vlan-challenged ethtool feature does not exist?",
|
|
|
|
|
ifindex);
|
2015-05-02 07:59:59 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
block = idx / 32;
|
|
|
|
|
bit = idx % 32;
|
|
|
|
|
size = block + 1;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
features_len = sizeof(*features) + (size * sizeof(struct ethtool_get_features_block));
|
|
|
|
|
features = nm_malloc0_maybe_a(300, features_len, &features_free);
|
2015-05-02 07:59:59 +02:00
|
|
|
features->cmd = ETHTOOL_GFEATURES;
|
|
|
|
|
features->size = size;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_handle(&shandle, features, features_len) < 0)
|
2015-05-02 07:59:59 +02:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
return !(features->features[block].active & (1 << bit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_get_peer_ifindex(int ifindex)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
|
|
|
|
gsize stats_len;
|
|
|
|
|
gs_free struct ethtool_stats * stats_free = NULL;
|
|
|
|
|
struct ethtool_stats * stats;
|
2015-05-02 07:59:59 +02:00
|
|
|
int peer_ifindex_stat;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
peer_ifindex_stat = ethtool_get_stringset_index(&shandle, ETH_SS_STATS, "peer_ifindex");
|
2018-07-16 10:43:28 +02:00
|
|
|
if (peer_ifindex_stat < 0) {
|
2018-07-16 13:08:38 +02:00
|
|
|
nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex);
|
2015-05-02 07:59:59 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
stats_len = sizeof(*stats) + (peer_ifindex_stat + 1) * sizeof(guint64);
|
|
|
|
|
stats = nm_malloc0_maybe_a(300, stats_len, &stats_free);
|
2015-05-02 07:59:59 +02:00
|
|
|
stats->cmd = ETHTOOL_GSTATS;
|
|
|
|
|
stats->n_stats = peer_ifindex_stat + 1;
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_handle(&shandle, stats, stats_len) < 0)
|
2015-05-02 07:59:59 +02:00
|
|
|
return 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
return stats->data[peer_ifindex_stat];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_get_wake_on_lan(int ifindex)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
struct ethtool_wolinfo wol = {
|
|
|
|
|
.cmd = ETHTOOL_GWOL,
|
|
|
|
|
};
|
2015-05-02 07:59:59 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2015-05-02 07:59:59 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_once(ifindex, &wol, sizeof(wol)) < 0)
|
2015-05-02 07:59:59 +02:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return wol.wolopts != 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 12:06:12 +02:00
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_get_link_settings(int ifindex,
|
2016-09-10 16:48:01 +02:00
|
|
|
gboolean * out_autoneg,
|
|
|
|
|
guint32 * out_speed,
|
|
|
|
|
NMPlatformLinkDuplexType *out_duplex)
|
2015-06-01 12:06:12 +02:00
|
|
|
{
|
|
|
|
|
struct ethtool_cmd edata = {
|
|
|
|
|
.cmd = ETHTOOL_GSET,
|
|
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0)
|
2015-06-01 12:06:12 +02:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
NM_SET_OUT(out_autoneg, (edata.autoneg == AUTONEG_ENABLE));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
if (out_speed) {
|
|
|
|
|
guint32 speed;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
speed = ethtool_cmd_speed(&edata);
|
|
|
|
|
if (speed == G_MAXUINT16 || speed == G_MAXUINT32)
|
|
|
|
|
speed = 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-06-01 12:06:12 +02:00
|
|
|
*out_speed = speed;
|
2016-09-10 16:48:01 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
if (out_duplex) {
|
|
|
|
|
switch (edata.duplex) {
|
|
|
|
|
case DUPLEX_HALF:
|
|
|
|
|
*out_duplex = NM_PLATFORM_LINK_DUPLEX_HALF;
|
|
|
|
|
break;
|
|
|
|
|
case DUPLEX_FULL:
|
|
|
|
|
*out_duplex = NM_PLATFORM_LINK_DUPLEX_FULL;
|
|
|
|
|
break;
|
|
|
|
|
default: /* DUPLEX_UNKNOWN */
|
|
|
|
|
*out_duplex = NM_PLATFORM_LINK_DUPLEX_UNKNOWN;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-06-01 12:06:12 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-12 12:57:05 +02:00
|
|
|
#define ADVERTISED_INVALID 0
|
|
|
|
|
#define BASET_ALL_MODES \
|
|
|
|
|
(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half \
|
|
|
|
|
| ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full \
|
|
|
|
|
| ADVERTISED_10000baseT_Full)
|
|
|
|
|
|
2019-02-06 09:04:23 +01:00
|
|
|
static guint32
|
2018-06-12 12:57:05 +02:00
|
|
|
get_baset_mode(guint32 speed, NMPlatformLinkDuplexType duplex)
|
|
|
|
|
{
|
|
|
|
|
if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN)
|
|
|
|
|
return ADVERTISED_INVALID;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-06-12 12:57:05 +02:00
|
|
|
if (duplex == NM_PLATFORM_LINK_DUPLEX_HALF) {
|
|
|
|
|
switch (speed) {
|
|
|
|
|
case 10:
|
|
|
|
|
return ADVERTISED_10baseT_Half;
|
|
|
|
|
case 100:
|
|
|
|
|
return ADVERTISED_100baseT_Half;
|
|
|
|
|
case 1000:
|
|
|
|
|
return ADVERTISED_1000baseT_Half;
|
|
|
|
|
default:
|
|
|
|
|
return ADVERTISED_INVALID;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
switch (speed) {
|
|
|
|
|
case 10:
|
|
|
|
|
return ADVERTISED_10baseT_Full;
|
|
|
|
|
case 100:
|
|
|
|
|
return ADVERTISED_100baseT_Full;
|
|
|
|
|
case 1000:
|
|
|
|
|
return ADVERTISED_1000baseT_Full;
|
|
|
|
|
case 10000:
|
|
|
|
|
return ADVERTISED_10000baseT_Full;
|
|
|
|
|
default:
|
|
|
|
|
return ADVERTISED_INVALID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_ethtool_set_link_settings(int ifindex,
|
|
|
|
|
gboolean autoneg,
|
|
|
|
|
guint32 speed,
|
|
|
|
|
NMPlatformLinkDuplexType duplex)
|
2016-09-10 16:48:01 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2016-09-10 16:48:01 +02:00
|
|
|
struct ethtool_cmd edata = {
|
|
|
|
|
.cmd = ETHTOOL_GSET,
|
|
|
|
|
};
|
|
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2018-06-12 12:57:05 +02:00
|
|
|
g_return_val_if_fail((speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN)
|
|
|
|
|
|| (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN),
|
|
|
|
|
FALSE);
|
2016-12-11 22:46:14 +01:00
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
/* retrieve first current settings */
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
if (_ethtool_call_handle(&shandle, &edata, sizeof(edata)) < 0)
|
2016-09-10 16:48:01 +02:00
|
|
|
return FALSE;
|
|
|
|
|
|
2018-09-06 10:29:43 +02:00
|
|
|
/* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API
|
|
|
|
|
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf
|
|
|
|
|
*/
|
|
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
/* then change the needed ones */
|
|
|
|
|
edata.cmd = ETHTOOL_SSET;
|
|
|
|
|
if (autoneg) {
|
|
|
|
|
edata.autoneg = AUTONEG_ENABLE;
|
2018-06-12 12:57:05 +02:00
|
|
|
if (!speed)
|
|
|
|
|
edata.advertising = edata.supported;
|
|
|
|
|
else {
|
|
|
|
|
guint32 mode;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-06-12 12:57:05 +02:00
|
|
|
mode = get_baset_mode(speed, duplex);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-06-12 12:57:05 +02:00
|
|
|
if (!mode) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised",
|
|
|
|
|
ifindex,
|
|
|
|
|
speed,
|
|
|
|
|
nm_platform_link_duplex_type_to_string(duplex));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (!(edata.supported & mode)) {
|
|
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: device does not support %uBASE-T %s duplex mode",
|
|
|
|
|
ifindex,
|
|
|
|
|
speed,
|
|
|
|
|
nm_platform_link_duplex_type_to_string(duplex));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
edata.advertising = (edata.supported & ~BASET_ALL_MODES) | mode;
|
|
|
|
|
}
|
2016-09-10 16:48:01 +02:00
|
|
|
} else {
|
|
|
|
|
edata.autoneg = AUTONEG_DISABLE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
if (speed)
|
|
|
|
|
ethtool_cmd_speed_set(&edata, speed);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-09-10 16:48:01 +02:00
|
|
|
switch (duplex) {
|
|
|
|
|
case NM_PLATFORM_LINK_DUPLEX_HALF:
|
|
|
|
|
edata.duplex = DUPLEX_HALF;
|
|
|
|
|
break;
|
|
|
|
|
case NM_PLATFORM_LINK_DUPLEX_FULL:
|
|
|
|
|
edata.duplex = DUPLEX_FULL;
|
|
|
|
|
break;
|
2016-11-22 12:59:11 +01:00
|
|
|
case NM_PLATFORM_LINK_DUPLEX_UNKNOWN:
|
2016-09-10 16:48:01 +02:00
|
|
|
break;
|
2016-11-22 12:59:11 +01:00
|
|
|
default:
|
|
|
|
|
g_return_val_if_reached(FALSE);
|
2016-09-10 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
return _ethtool_call_handle(&shandle, &edata, sizeof(edata)) >= 0;
|
2016-09-10 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
2015-05-12 14:44:03 +02:00
|
|
|
gboolean
|
2021-01-08 17:01:45 +01:00
|
|
|
nmp_utils_ethtool_set_wake_on_lan(int ifindex,
|
|
|
|
|
_NMSettingWiredWakeOnLan wol,
|
|
|
|
|
const char * wol_password)
|
2015-05-12 14:44:03 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
struct ethtool_wolinfo wol_info = {
|
|
|
|
|
.cmd = ETHTOOL_SWOL,
|
|
|
|
|
.wolopts = 0,
|
|
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-01-08 17:01:45 +01:00
|
|
|
if (wol == _NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE)
|
2015-09-21 18:24:07 +02:00
|
|
|
return TRUE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-07-16 13:08:38 +02:00
|
|
|
nm_log_dbg(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'",
|
|
|
|
|
ifindex,
|
|
|
|
|
(unsigned) wol,
|
|
|
|
|
wol_password);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-01-08 17:01:45 +01:00
|
|
|
if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_PHY))
|
2015-05-12 14:44:03 +02:00
|
|
|
wol_info.wolopts |= WAKE_PHY;
|
2021-01-08 17:01:45 +01:00
|
|
|
if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST))
|
2015-05-12 14:44:03 +02:00
|
|
|
wol_info.wolopts |= WAKE_UCAST;
|
2021-01-08 17:01:45 +01:00
|
|
|
if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST))
|
2015-05-12 14:44:03 +02:00
|
|
|
wol_info.wolopts |= WAKE_MCAST;
|
2021-01-08 17:01:45 +01:00
|
|
|
if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST))
|
2015-05-12 14:44:03 +02:00
|
|
|
wol_info.wolopts |= WAKE_BCAST;
|
2021-01-08 17:01:45 +01:00
|
|
|
if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_ARP))
|
2015-05-12 14:44:03 +02:00
|
|
|
wol_info.wolopts |= WAKE_ARP;
|
2021-01-08 17:01:45 +01:00
|
|
|
if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
|
2015-05-12 14:44:03 +02:00
|
|
|
wol_info.wolopts |= WAKE_MAGIC;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-05-12 14:44:03 +02:00
|
|
|
if (wol_password) {
|
2021-01-08 19:01:28 +01:00
|
|
|
if (!_nm_utils_hwaddr_aton_exact(wol_password, wol_info.sopass, ETH_ALEN)) {
|
2018-07-16 13:08:38 +02:00
|
|
|
nm_log_dbg(LOGD_PLATFORM,
|
|
|
|
|
"ethtool[%d]: couldn't parse Wake-on-LAN password '%s'",
|
|
|
|
|
ifindex,
|
|
|
|
|
wol_password);
|
2015-05-12 14:44:03 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
wol_info.wolopts |= WAKE_MAGICSECURE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
return _ethtool_call_once(ifindex, &wol_info, sizeof(wol_info)) >= 0;
|
2015-05-12 14:44:03 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
/******************************************************************************
|
2015-05-03 12:49:46 +02:00
|
|
|
* mii
|
2018-07-16 10:43:28 +02:00
|
|
|
*****************************************************************************/
|
2015-05-03 12:49:46 +02:00
|
|
|
|
|
|
|
|
gboolean
|
2016-12-11 22:46:14 +01:00
|
|
|
nmp_utils_mii_supports_carrier_detect(int ifindex)
|
2015-05-03 12:49:46 +02:00
|
|
|
{
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
|
2018-07-16 14:11:09 +02:00
|
|
|
int r;
|
2015-05-03 12:49:46 +02:00
|
|
|
struct ifreq ifr;
|
|
|
|
|
struct mii_ioctl_data * mii;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-11 22:46:14 +01:00
|
|
|
g_return_val_if_fail(ifindex > 0, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
r = _ioctl_call("mii",
|
|
|
|
|
"SIOCGMIIPHY",
|
|
|
|
|
SIOCGMIIPHY,
|
|
|
|
|
shandle.ifindex,
|
|
|
|
|
&shandle.fd,
|
|
|
|
|
shandle.ifname,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_NONE,
|
|
|
|
|
NULL,
|
|
|
|
|
0,
|
|
|
|
|
&ifr);
|
|
|
|
|
if (r < 0)
|
2016-12-11 22:46:14 +01:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-05-03 12:49:46 +02:00
|
|
|
/* If we can read the BMSR register, we assume that the card supports MII link detection */
|
|
|
|
|
mii = (struct mii_ioctl_data *) &ifr.ifr_ifru;
|
|
|
|
|
mii->reg_num = MII_BMSR;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
r = _ioctl_call("mii",
|
|
|
|
|
"SIOCGMIIREG",
|
|
|
|
|
SIOCGMIIREG,
|
|
|
|
|
shandle.ifindex,
|
|
|
|
|
&shandle.fd,
|
|
|
|
|
shandle.ifname,
|
|
|
|
|
IOCTL_CALL_DATA_TYPE_IFRU,
|
|
|
|
|
mii,
|
|
|
|
|
sizeof(*mii),
|
|
|
|
|
&ifr);
|
|
|
|
|
if (r < 0)
|
2016-12-11 22:46:14 +01:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform/ethtool,mii: retry ioctl when interface name was renamed for ehttool/mii
ethtool/mii API is based on the ifname. As an interface can be renamed,
this API is inherently racy. We would prefer to use the ifindex instead.
The ifindex of a device cannot change (altough it can repeat, which opens a
different race *sigh*).
Anyway, we were already trying to minimize the race be resolving the
name from ifindex immediately before the call to ethtool/mii.
Do better than that. Now resolve the name before and after the call. If
the name changed in the meantime, we have an indication that a race
might have happend (but we cannot be sure).
Note that this can not catch every possible kind of rename race. If you are very
unlucky a swapping of names cannot be detected.
For getters this is relatively straight forward. Just retry when we
have an indication to fall victim to a race (up to a few times). Yes, we
still cannot be 100% sure, but this should be very reliable in practice.
For setters (that modify the device) we also retry. We do so under the
assumption that setting the same options multiple times has no bad effect.
Note that for setters the race of swapping interface names is particularly
bad. If we hit a very unlucky race condition, we might set the setting on
the wrong interface and there is nothing we can do about it. The retry only
ensures that eventually we will set it on the right interface.
Note that this involves one more if_indextoname() call for each operation (in
the common case when there is no renaming race). In cases where we make
multiple ioctl calls, we cache and reuse the information though. So, for such
calls the overhead is even smaller.
2019-05-02 17:13:51 +02:00
|
|
|
mii = (struct mii_ioctl_data *) &ifr.ifr_ifru;
|
2018-07-16 14:11:09 +02:00
|
|
|
nm_log_trace(LOGD_PLATFORM,
|
|
|
|
|
"mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X",
|
|
|
|
|
ifindex,
|
|
|
|
|
shandle.ifname,
|
|
|
|
|
mii->val_out);
|
2016-12-11 22:46:14 +01:00
|
|
|
return TRUE;
|
2015-05-03 12:49:46 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-16 10:43:28 +02:00
|
|
|
/******************************************************************************
|
2015-05-02 07:59:59 +02:00
|
|
|
* udev
|
2018-07-16 10:43:28 +02:00
|
|
|
*****************************************************************************/
|
2015-05-02 07:59:59 +02:00
|
|
|
|
|
|
|
|
const char *
|
2017-03-12 15:54:02 +01:00
|
|
|
nmp_utils_udev_get_driver(struct udev_device *udevice)
|
2015-05-02 07:59:59 +02:00
|
|
|
{
|
2017-03-12 15:54:02 +01:00
|
|
|
struct udev_device *parent = NULL, *grandparent = NULL;
|
2015-05-02 07:59:59 +02:00
|
|
|
const char * driver, *subsys;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-03-12 15:54:02 +01:00
|
|
|
driver = udev_device_get_driver(udevice);
|
2015-05-02 07:59:59 +02:00
|
|
|
if (driver)
|
|
|
|
|
goto out;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2015-05-02 07:59:59 +02:00
|
|
|
/* Try the parent */
|
2017-03-12 15:54:02 +01:00
|
|
|
parent = udev_device_get_parent(udevice);
|
2015-05-02 07:59:59 +02:00
|
|
|
if (parent) {
|
2017-03-12 15:54:02 +01:00
|
|
|
driver = udev_device_get_driver(parent);
|
2015-05-02 07:59:59 +02:00
|
|
|
if (!driver) {
|
|
|
|
|
/* Try the grandparent if it's an ibmebus device or if the
|
|
|
|
|
* subsys is NULL which usually indicates some sort of
|
|
|
|
|
* platform device like a 'gadget' net interface.
|
|
|
|
|
*/
|
2017-03-12 15:54:02 +01:00
|
|
|
subsys = udev_device_get_subsystem(parent);
|
2015-05-02 07:59:59 +02:00
|
|
|
if ((g_strcmp0(subsys, "ibmebus") == 0) || (subsys == NULL)) {
|
2017-03-12 15:54:02 +01:00
|
|
|
grandparent = udev_device_get_parent(parent);
|
2015-05-02 07:59:59 +02:00
|
|
|
if (grandparent)
|
2017-03-12 15:54:02 +01:00
|
|
|
driver = udev_device_get_driver(grandparent);
|
2015-05-02 07:59:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
/* Intern the string so we don't have to worry about memory
|
|
|
|
|
* management in NMPlatformLink. */
|
|
|
|
|
return g_intern_string(driver);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-10 12:01:51 +02:00
|
|
|
/******************************************************************************
|
|
|
|
|
* utils
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
NMIPConfigSource
|
|
|
|
|
nmp_utils_ip_config_source_from_rtprot(guint8 rtprot)
|
2016-04-10 12:01:51 +02:00
|
|
|
{
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
return ((int) rtprot) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMIPConfigSource
|
|
|
|
|
nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source)
|
|
|
|
|
{
|
|
|
|
|
/* when adding a route to kernel for a give @source, the resulting route
|
|
|
|
|
* will be put into the cache with a source of NM_IP_CONFIG_SOURCE_RTPROT_*.
|
|
|
|
|
* This function returns that. */
|
|
|
|
|
return nmp_utils_ip_config_source_from_rtprot(
|
|
|
|
|
nmp_utils_ip_config_source_coerce_to_rtprot(source));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
guint8
|
|
|
|
|
nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source)
|
|
|
|
|
{
|
|
|
|
|
/* when adding a route to kernel, we coerce the @source field
|
|
|
|
|
* to rtm_protocol. This is not lossless as we map different
|
|
|
|
|
* source values to the same RTPROT uint8 value. */
|
|
|
|
|
if (source <= NM_IP_CONFIG_SOURCE_UNKNOWN)
|
2016-04-10 12:01:51 +02:00
|
|
|
return RTPROT_UNSPEC;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-05-16 13:16:49 +02:00
|
|
|
if (source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST)
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
return source - 1;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
switch (source) {
|
2016-04-10 12:01:51 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_KERNEL:
|
|
|
|
|
return RTPROT_KERNEL;
|
2018-03-08 13:34:54 +01:00
|
|
|
case NM_IP_CONFIG_SOURCE_IP6LL:
|
|
|
|
|
return RTPROT_KERNEL;
|
2016-04-10 12:01:51 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_DHCP:
|
|
|
|
|
return RTPROT_DHCP;
|
2016-10-13 11:06:25 +00:00
|
|
|
case NM_IP_CONFIG_SOURCE_NDISC:
|
2016-04-10 12:01:51 +02:00
|
|
|
return RTPROT_RA;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-04-10 12:01:51 +02:00
|
|
|
default:
|
|
|
|
|
return RTPROT_STATIC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMIPConfigSource
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source)
|
2016-04-10 12:01:51 +02:00
|
|
|
{
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
/* When we receive a route from kernel and put it into the platform cache,
|
|
|
|
|
* we preserve the protocol field by converting it to a NMIPConfigSource
|
|
|
|
|
* via nmp_utils_ip_config_source_from_rtprot().
|
|
|
|
|
*
|
|
|
|
|
* However, that is not the inverse of nmp_utils_ip_config_source_coerce_to_rtprot().
|
|
|
|
|
* Instead, to go back to the original value, you need another step:
|
|
|
|
|
* nmp_utils_ip_config_source_coerce_from_rtprot (nmp_utils_ip_config_source_from_rtprot (rtprot)).
|
|
|
|
|
*
|
|
|
|
|
* This might partly restore the original source value, but of course that
|
|
|
|
|
* is not really possible because nmp_utils_ip_config_source_coerce_to_rtprot()
|
|
|
|
|
* is not injective.
|
|
|
|
|
* */
|
|
|
|
|
switch (source) {
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC:
|
2016-04-10 12:01:51 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_UNKNOWN;
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT:
|
2016-04-10 12:01:51 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_KERNEL;
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_RA:
|
2016-10-13 11:06:25 +00:00
|
|
|
return NM_IP_CONFIG_SOURCE_NDISC;
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_DHCP:
|
2016-04-10 12:01:51 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_DHCP;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return NM_IP_CONFIG_SOURCE_USER;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
const char *
|
|
|
|
|
nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len)
|
|
|
|
|
{
|
|
|
|
|
const char *s = NULL;
|
|
|
|
|
nm_utils_to_string_buffer_init(&buf, &len);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
if (!len)
|
|
|
|
|
return buf;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
switch (source) {
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_UNKNOWN:
|
|
|
|
|
s = "unknown";
|
|
|
|
|
break;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC:
|
|
|
|
|
s = "rt-unspec";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT:
|
|
|
|
|
s = "rt-redirect";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
|
|
|
|
|
s = "rt-kernel";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_BOOT:
|
|
|
|
|
s = "rt-boot";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_STATIC:
|
|
|
|
|
s = "rt-static";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_DHCP:
|
|
|
|
|
s = "rt-dhcp";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_RTPROT_RA:
|
|
|
|
|
s = "rt-ra";
|
|
|
|
|
break;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_KERNEL:
|
|
|
|
|
s = "kernel";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_SHARED:
|
|
|
|
|
s = "shared";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_IP4LL:
|
|
|
|
|
s = "ipv4ll";
|
2018-03-08 13:34:54 +01:00
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_IP6LL:
|
|
|
|
|
s = "ipv6ll";
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_PPP:
|
|
|
|
|
s = "ppp";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_WWAN:
|
|
|
|
|
s = "wwan";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_VPN:
|
|
|
|
|
s = "vpn";
|
|
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_DHCP:
|
|
|
|
|
s = "dhcp";
|
2016-10-13 11:06:25 +00:00
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_NDISC:
|
|
|
|
|
s = "ndisc";
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
break;
|
|
|
|
|
case NM_IP_CONFIG_SOURCE_USER:
|
|
|
|
|
s = "user";
|
2020-09-28 16:03:33 +02:00
|
|
|
break;
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
platform: extend NMIPConfigSource to preserve the rtm_protocol field
For addresses (NMPlatformIPAddress) the @addr_source field is ignored
on a platform level. That is, all addresses inside the platform cache
have this value set to NM_IP_CONFIG_SOURCE_KERNEL. Maybe, for that reason,
the source should not be a part of the NMPlatformIPAddress structure, but
it is convenient for users to piggy back the source inside the platform
address structure.
For routes, the source is stored in NMPlatformIPRoute's @rt_source
field. When adding a route to kernel, we set the @rtm_protocol of the
route depending on the source. However, we want to map different source
values to the same protocol value.
On the other hand, when kernel sends us a route that gets put inside
the cache, we must preserve the protocol value and must not map
different protocol values to the same source.
The reason is, that a user can add two routes that only differ by
@rtm_protocol. In that sense, the @rtm_protocol fields is part of the
unique ID of a kernel route, and thus different values must map to
different sources.
Fix this, by extending the range of NMIPConfigSource to contain
a range of protocol fields.
2016-04-11 17:35:29 +02:00
|
|
|
if (source >= 1 && source <= 0x100) {
|
|
|
|
|
if (s)
|
|
|
|
|
g_snprintf(buf, len, "%s", s);
|
|
|
|
|
else
|
|
|
|
|
g_snprintf(buf, len, "rt-%d", ((int) source) - 1);
|
|
|
|
|
} else {
|
|
|
|
|
if (s)
|
|
|
|
|
g_strlcpy(buf, s, len);
|
|
|
|
|
else
|
|
|
|
|
g_snprintf(buf, len, "(%d)", source);
|
|
|
|
|
}
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
2016-04-11 14:29:17 +02:00
|
|
|
|
2016-12-08 13:55:17 +01:00
|
|
|
/**
|
|
|
|
|
* 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,
|
2018-09-14 23:49:20 -04:00
|
|
|
* it saves an additional if_indextoname() call.
|
2016-12-08 13:55:17 +01:00
|
|
|
* @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.
|
|
|
|
|
*/
|
2016-12-07 18:15:13 +08:00
|
|
|
int
|
2016-12-08 13:55:17 +01:00
|
|
|
nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname)
|
2016-12-07 18:15:13 +08:00
|
|
|
{
|
|
|
|
|
#define SYS_CLASS_NET "/sys/class/net/"
|
2016-12-08 13:55:17 +01:00
|
|
|
const char *ifname = ifname_guess;
|
|
|
|
|
char ifname_buf_last_try[IFNAMSIZ];
|
2016-12-07 18:15:13 +08:00
|
|
|
char ifname_buf[IFNAMSIZ];
|
|
|
|
|
guint try_count = 0;
|
2016-12-08 13:55:17 +01:00
|
|
|
char sysdir[NM_STRLEN(SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET;
|
2016-12-07 18:15:13 +08:00
|
|
|
char fd_buf[256];
|
|
|
|
|
ssize_t nn;
|
|
|
|
|
|
2016-12-08 13:55:17 +01:00
|
|
|
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;
|
|
|
|
|
|
2016-12-07 18:15:13 +08:00
|
|
|
if (!ifname) {
|
2016-12-26 12:00:08 +01:00
|
|
|
ifname = nmp_utils_if_indextoname(ifindex, ifname_buf);
|
2016-12-07 18:15:13 +08:00
|
|
|
if (!ifname)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 18:01:13 +01:00
|
|
|
nm_assert(nm_utils_ifname_valid_kernel(ifname, NULL));
|
2016-12-07 18:15:13 +08:00
|
|
|
|
2016-12-08 13:55:17 +01:00
|
|
|
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';
|
|
|
|
|
|
2018-12-23 11:28:42 +01:00
|
|
|
if (ifindex != (int) _nm_utils_ascii_str_to_int64(fd_buf, 10, 1, G_MAXINT, -1))
|
2016-12-08 13:55:17 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (out_ifname)
|
|
|
|
|
strcpy(out_ifname, ifname);
|
|
|
|
|
|
2018-12-23 11:28:42 +01:00
|
|
|
return nm_steal_fd(&fd_dir);
|
2016-12-07 18:15:13 +08:00
|
|
|
}
|
2016-12-08 13:55:17 +01:00
|
|
|
|
2016-12-07 18:15:13 +08:00
|
|
|
return -1;
|
|
|
|
|
}
|