diff options
| author | Michael Biebl <biebl@debian.org> | 2018-09-23 10:10:27 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-09-23 10:10:27 +0200 |
| commit | e126f3e804c35480c4f075777430419d6ece23da (patch) | |
| tree | 5d5821ebcda8cd6ac34d2483bb3354910e508930 /src/platform/nm-platform-utils.c | |
| parent | c240974325c552cad177c457d6ff04e381fd77a3 (diff) | |
New upstream version 1.12.4 upstream/1.12.4
Diffstat (limited to 'src/platform/nm-platform-utils.c')
| -rw-r--r-- | src/platform/nm-platform-utils.c | 737 |
1 files changed, 119 insertions, 618 deletions
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 216b1547..0d224443 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -36,15 +36,12 @@ #include "nm-utils.h" #include "nm-setting-wired.h" -#include "nm-ethtool-utils.h" #include "nm-core-utils.h" -#define ONOFF(bool_val) ((bool_val) ? "on" : "off") - -/****************************************************************************** +/****************************************************************** * utils - *****************************************************************************/ + ******************************************************************/ extern char *if_indextoname (unsigned __ifindex, char *__ifname); unsigned if_nametoindex (const char *__ifname); @@ -66,45 +63,9 @@ nmp_utils_if_nametoindex (const char *ifname) return if_nametoindex (ifname); } -/*****************************************************************************/ - -typedef struct { - int fd; - int ifindex; - char ifname[IFNAMSIZ]; -} SocketHandle; - -static int -socket_handle_init (SocketHandle *shandle, int ifindex) -{ - if (!nmp_utils_if_indextoname (ifindex, shandle->ifname)) { - shandle->ifindex = 0; - return -ENODEV; - } - - shandle->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (shandle->fd < 0) { - shandle->ifindex = 0; - return -errno; - } - - shandle->ifindex = ifindex; - return 0; -} - -static void -socket_handle_destroy (SocketHandle *shandle) -{ - if (shandle->ifindex) { - shandle->ifindex = 0; - nm_close (shandle->fd); - } -} -#define nm_auto_socket_handle nm_auto(socket_handle_destroy) - -/****************************************************************************** +/****************************************************************** * ethtool - *****************************************************************************/ + ******************************************************************/ NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, NM_UTILS_ENUM2STR (ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), @@ -116,7 +77,6 @@ NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, NM_UTILS_ENUM2STR (ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), NM_UTILS_ENUM2STR (ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), NM_UTILS_ENUM2STR (ETHTOOL_GWOL, "ETHTOOL_GWOL"), - NM_UTILS_ENUM2STR (ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), NM_UTILS_ENUM2STR (ETHTOOL_SSET, "ETHTOOL_SSET"), NM_UTILS_ENUM2STR (ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); @@ -127,8 +87,6 @@ _ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) return _ethtool_cmd_to_string (*((guint32 *) edata), buf, len); } -/*****************************************************************************/ - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) #define ethtool_cmd_speed(pedata) ((pedata)->speed) @@ -136,553 +94,117 @@ _ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) G_STMT_START { (pedata)->speed = (guint16) (speed); } G_STMT_END #endif -static int -ethtool_call_handle (SocketHandle *shandle, gpointer edata) +static gboolean +ethtool_get (int ifindex, gpointer edata) { - struct ifreq ifr = { - .ifr_data = edata, - }; + char ifname[IFNAMSIZ]; char sbuf[50]; - int errsv; - - nm_assert (shandle); - nm_assert (shandle->ifindex); - nm_assert (shandle->ifname[0]); - nm_assert (strlen (shandle->ifname) < IFNAMSIZ); - nm_assert (edata); - - memcpy (ifr.ifr_name, shandle->ifname, IFNAMSIZ); - if (ioctl (shandle->fd, SIOCETHTOOL, &ifr) < 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %s", - shandle->ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - shandle->ifname, - strerror (errsv)); - return -errsv; - } - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", - shandle->ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - shandle->ifname); - return 0; -} + nm_assert (ifindex > 0); -static int -ethtool_call_ifindex (int ifindex, gpointer edata) -{ - nm_auto_socket_handle SocketHandle shandle = { }; - int r; - char sbuf[50]; - - nm_assert (edata); + /* ethtool ioctl API uses the ifname to refer to an interface. That is racy + * as interfaces can be renamed *sigh*. + * + * Note that we anyway have to verify whether the interface exists, before + * calling ioctl for a non-existing ifname. This is to prevent autoloading + * of kernel modules *sigh*. + * Thus, as we anyway verify the existence of ifname before doing the call, + * go one step further and lookup the ifname everytime anew. + * + * This does not solve the renaming race, but it minimizes the time for + * the race to happen as much as possible. */ - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", + if (!nmp_utils_if_indextoname (ifindex, ifname)) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: request fails resolving ifindex: %s", ifindex, _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - g_strerror (-r)); - return r; - } - - return ethtool_call_handle (&shandle, edata); -} - -/*****************************************************************************/ - -static struct ethtool_gstrings * -ethtool_get_stringset (SocketHandle *shandle, int stringset_id) -{ - struct { - struct ethtool_sset_info info; - guint32 sentinel; - } sset_info = { }; - gs_free struct ethtool_gstrings *gstrings = NULL; - guint32 i, len; - - sset_info.info.cmd = ETHTOOL_GSSET_INFO; - sset_info.info.reserved = 0; - sset_info.info.sset_mask = (1ULL << stringset_id); - - if (ethtool_call_handle (shandle, &sset_info) < 0) - return NULL; - if (!sset_info.info.sset_mask) - return NULL; - - len = sset_info.info.data[0]; - - gstrings = g_malloc0 (sizeof (*gstrings) + (len * ETH_GSTRING_LEN)); - gstrings->cmd = ETHTOOL_GSTRINGS; - gstrings->string_set = stringset_id; - gstrings->len = len; - if (gstrings->len > 0) { - if (ethtool_call_handle (shandle, gstrings) < 0) - return NULL; - for (i = 0; i < gstrings->len; i++) { - /* ensure NUL terminated */ - gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0'; - } - } - - return g_steal_pointer (&gstrings); -} - -static int -ethtool_gstrings_find (const struct ethtool_gstrings *gstrings, const char *needle) -{ - guint32 i; - - /* 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); - - for (i = 0; i < gstrings->len; i++) { - if (nm_streq ((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle)) - return i; - } - return -1; -} - -static int -ethtool_get_stringset_index (SocketHandle *shandle, int stringset_id, const char *needle) -{ - 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); - - gstrings = ethtool_get_stringset (shandle, stringset_id); - if (gstrings) - return ethtool_gstrings_find (gstrings, needle); - return -1; -} - -/*****************************************************************************/ - -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__ }), \ - } - - /* 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. */ - 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"), - - /* names from ethtool utility, which are aliases for one feature. */ - 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"), - - /* names of features, as known by kernel. */ - 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"), - 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"), - 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"), - 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"), - 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"), - 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"), -}; - -/* 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); - - g_assert (!found[inf->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]); - found[inf->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = TRUE; - - 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++) { - g_assert (nm_utils_strv_find_first ((char **) inf->kernel_names, - k, - inf->kernel_names[k]) < 0); - } - } - - 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; - - _ASSERT_ethtool_feature_infos (); - - ss_features = ethtool_get_stringset (shandle, ETH_SS_FEATURES); - if (!ss_features) - return NULL; - - if (ss_features->len > 0) { - gs_free struct ethtool_gfeatures *gfeatures = NULL; - guint idx; - const NMEthtoolFeatureState *states_list0 = NULL; - const NMEthtoolFeatureState *const*states_plist0 = NULL; - guint states_plist_n = 0; - - gfeatures = g_malloc0 ( sizeof (struct ethtool_gfeatures) - + (NM_DIV_ROUND_UP (ss_features->len, 32u) * sizeof(gfeatures->features[0]))); - - gfeatures->cmd = ETHTOOL_GFEATURES; - gfeatures->size = NM_DIV_ROUND_UP (ss_features->len, 32u); - if (ethtool_call_handle (shandle, gfeatures) < 0) - return NULL; - - for (idx = 0; idx < G_N_ELEMENTS (_ethtool_feature_infos); idx++) { - const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx]; - guint idx_kernel_name; - - 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; - - i_feature = ethtool_gstrings_find (ss_features, kernel_name); - if (i_feature < 0) - continue; - - i_block = ((guint) i_feature) / 32u; - i_flag = (guint32) (1u << (((guint) i_feature) % 32u)); - - 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; - } - - nm_assert (states->n_states < N_ETHTOOL_KERNEL_FEATURES); - kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states]; - states->n_states++; - - 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); - - nm_assert (states_plist_n < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS (_ethtool_feature_infos)); - - if (!states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]) - states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = &states_plist0[states_plist_n]; - ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; - states_plist_n++; - } - - if (states && states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]) { - 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++; - } - } - } - - return g_steal_pointer (&states); -} - -NMEthtoolFeatureStates * -nmp_utils_ethtool_get_features (int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = { }; - NMEthtoolFeatureStates *features; - int r; - - g_return_val_if_fail (ifindex > 0, 0); - - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "get-features", - g_strerror (-r)); + g_strerror (errno)); return FALSE; } - features = ethtool_get_features (&shandle); + { + nm_auto_close int fd = -1; + struct ifreq ifr = { + .ifr_data = edata, + }; - if (!features) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure getting features", - ifindex, - "get-features"); - return NULL; - } - - 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; - - 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; -} + memcpy (ifr.ifr_name, ifname, sizeof (ifname)); -gboolean -nmp_utils_ethtool_set_features (int ifindex, - const NMEthtoolFeatureStates *features, - const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */) -{ - nm_auto_socket_handle SocketHandle shandle = { }; - gs_free struct ethtool_sfeatures *sfeatures = NULL; - int r; - guint i, j; - struct { - const NMEthtoolFeatureState *f_state; - NMTernary requested; - } set_states[N_ETHTOOL_KERNEL_FEATURES]; - guint set_states_n = 0; - gboolean success = TRUE; - - g_return_val_if_fail (ifindex > 0, 0); - g_return_val_if_fail (features, 0); - g_return_val_if_fail (requested, 0); - - nm_assert (features->n_states <= N_ETHTOOL_KERNEL_FEATURES); - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { - const NMEthtoolFeatureState *const*states_indexed; - - if (requested[i] == NM_TERNARY_DEFAULT) - continue; - - 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; + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed creating socket for ioctl: %s", + ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + ifname, + g_strerror (errno)); + return FALSE; } - for (j = 0; states_indexed[j]; j++) { - const NMEthtoolFeatureState *s = states_indexed[j]; - char sbuf[255]; - - if (set_states_n >= G_N_ELEMENTS (set_states)) - g_return_val_if_reached (FALSE); - - 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], - ONOFF (do_set ? requested[i] == NM_TERNARY_TRUE : s->active), - _ethtool_feature_state_to_string (sbuf, sizeof (sbuf), s, do_set ? " currently:" : " before:")); - continue; - } - - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: %s feature %s (%s): %s, %s", + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %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], - ONOFF (do_set ? requested[i] == NM_TERNARY_TRUE : s->active), - _ethtool_feature_state_to_string (sbuf, sizeof (sbuf), s, do_set ? " currently:" : " before:")); - - if ( do_set - && (!s->available || s->never_changed) - && (s->active != (requested[i] == NM_TERNARY_TRUE))) { - /* 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; - } - - set_states[set_states_n].f_state = s; - set_states[set_states_n].requested = requested[i]; - set_states_n++; + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + ifname, + strerror (errno)); + return FALSE; } - } - if (set_states_n == 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: no feature requested", + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", ifindex, - "set-features"); + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + ifname); return TRUE; } +} - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "set-features", - g_strerror (-r)); - return FALSE; - } +static int +ethtool_get_stringset_index (int ifindex, int stringset_id, const char *string) +{ + gs_free struct ethtool_sset_info *info = NULL; + gs_free struct ethtool_gstrings *strings = NULL; + guint32 len, i; - sfeatures = g_malloc0 (sizeof (struct ethtool_sfeatures) - + (NM_DIV_ROUND_UP (features->n_ss_features, 32U) * sizeof(sfeatures->features[0]))); - sfeatures->cmd = ETHTOOL_SFEATURES; - sfeatures->size = NM_DIV_ROUND_UP (features->n_ss_features, 32U); + g_return_val_if_fail (ifindex > 0, -1); - 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; + info = g_malloc0 (sizeof (*info) + sizeof (guint32)); + info->cmd = ETHTOOL_GSSET_INFO; + info->reserved = 0; + info->sset_mask = 1ULL << stringset_id; - i_block = s->idx_ss_features / 32u; - i_flag = (guint32) (1u << (s->idx_ss_features % 32u)); + if (!ethtool_get (ifindex, info)) + return -1; + if (!info->sset_mask) + return -1; - sfeatures->features[i_block].valid |= i_flag; + len = info->data[0]; - if (do_set) - is_requested = (set_states[i].requested == NM_TERNARY_TRUE); - else - is_requested = s->active; + strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = stringset_id; + strings->len = len; + if (!ethtool_get (ifindex, strings)) + return -1; - if (is_requested) - sfeatures->features[i_block].requested |= i_flag; - else - sfeatures->features[i_block].requested &= ~i_flag; - } - - if ((r = ethtool_call_handle (&shandle, sfeatures)) < 0) { - success = FALSE; - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting features (%s)", - ifindex, - "set-features", - g_strerror (-r)); - return FALSE; + for (i = 0; i < len; i++) { + if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) + return i; } - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: %s", - ifindex, - "set-features", - success - ? "successfully setting features" - : "at least some of the features were not successfuly set"); - return success; + return -1; } -/*****************************************************************************/ - gboolean nmp_utils_ethtool_get_driver_info (int ifindex, NMPUtilsEthtoolDriverInfo *data) { struct ethtool_drvinfo *drvinfo; - - 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)); + G_STATIC_ASSERT (sizeof (*data) == sizeof (*drvinfo)); + G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, driver) == offsetof (struct ethtool_drvinfo, driver)); + G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, version) == offsetof (struct ethtool_drvinfo, version)); + G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, fw_version) == offsetof (struct ethtool_drvinfo, fw_version)); + G_STATIC_ASSERT (sizeof (data->driver) == sizeof (drvinfo->driver)); + G_STATIC_ASSERT (sizeof (data->version) == sizeof (drvinfo->version)); + G_STATIC_ASSERT (sizeof (data->fw_version) == sizeof (drvinfo->fw_version)); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (data, FALSE); @@ -691,7 +213,7 @@ nmp_utils_ethtool_get_driver_info (int ifindex, memset (drvinfo, 0, sizeof (*drvinfo)); drvinfo->cmd = ETHTOOL_GDRVINFO; - return ethtool_call_ifindex (ifindex, drvinfo) >= 0; + return ethtool_get (ifindex, drvinfo); } gboolean @@ -711,7 +233,7 @@ nmp_utils_ethtool_get_permanent_address (int ifindex, edata.e.cmd = ETHTOOL_GPERMADDR; edata.e.size = NM_UTILS_HWADDR_LEN_MAX; - if (ethtool_call_ifindex (ifindex, &edata.e) < 0) + if (!ethtool_get (ifindex, &edata.e)) return FALSE; if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) @@ -730,8 +252,8 @@ nmp_utils_ethtool_get_permanent_address (int ifindex, } return FALSE; } - not_all_0or1: + memcpy (buf, edata.e.data, edata.e.size); *length = edata.e.size; return TRUE; @@ -748,30 +270,20 @@ nmp_utils_ethtool_supports_carrier_detect (int ifindex) * assume the device supports carrier-detect, otherwise we assume it * doesn't. */ - return ethtool_call_ifindex (ifindex, &edata) >= 0; + return ethtool_get (ifindex, &edata); } gboolean nmp_utils_ethtool_supports_vlans (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; gs_free struct ethtool_gfeatures *features = NULL; int idx, block, bit, size; g_return_val_if_fail (ifindex > 0, FALSE); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "support-vlans", - g_strerror (-r)); - return FALSE; - } - - idx = ethtool_get_stringset_index (&shandle, ETH_SS_FEATURES, "vlan-challenged"); - if (idx < 0) { - nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: vlan-challenged ethtool feature does not exist?", ifindex); + idx = ethtool_get_stringset_index (ifindex, ETH_SS_FEATURES, "vlan-challenged"); + if (idx == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %d?", ifindex); return FALSE; } @@ -783,7 +295,7 @@ nmp_utils_ethtool_supports_vlans (int ifindex) features->cmd = ETHTOOL_GFEATURES; features->size = size; - if (ethtool_call_handle (&shandle, features) < 0) + if (!ethtool_get (ifindex, features)) return FALSE; return !(features->features[block].active & (1 << bit)); @@ -792,32 +304,21 @@ nmp_utils_ethtool_supports_vlans (int ifindex) int nmp_utils_ethtool_get_peer_ifindex (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; - gs_free struct ethtool_stats *stats = NULL; int peer_ifindex_stat; g_return_val_if_fail (ifindex > 0, 0); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "get-peer-ifindex", - g_strerror (-r)); - return FALSE; - } - - peer_ifindex_stat = ethtool_get_stringset_index (&shandle, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat < 0) { - nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); + peer_ifindex_stat = ethtool_get_stringset_index (ifindex, ETH_SS_STATS, "peer_ifindex"); + if (peer_ifindex_stat == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: peer_ifindex stat for %d does not exist?", ifindex); return FALSE; } stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); stats->cmd = ETHTOOL_GSTATS; stats->n_stats = peer_ifindex_stat + 1; - if (ethtool_call_ifindex (ifindex, stats) < 0) + if (!ethtool_get (ifindex, stats)) return 0; return stats->data[peer_ifindex_stat]; @@ -832,7 +333,7 @@ nmp_utils_ethtool_get_wake_on_lan (int ifindex) memset (&wol, 0, sizeof (wol)); wol.cmd = ETHTOOL_GWOL; - if (ethtool_call_ifindex (ifindex, &wol) < 0) + if (!ethtool_get (ifindex, &wol)) return FALSE; return wol.wolopts != 0; @@ -850,7 +351,7 @@ nmp_utils_ethtool_get_link_settings (int ifindex, g_return_val_if_fail (ifindex > 0, FALSE); - if (ethtool_call_ifindex (ifindex, &edata) < 0) + if (!ethtool_get (ifindex, &edata)) return FALSE; if (out_autoneg) @@ -932,13 +433,9 @@ nmp_utils_ethtool_set_link_settings (int ifindex, || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), FALSE); /* retrieve first current settings */ - if (ethtool_call_ifindex (ifindex, &edata) < 0) + if (!ethtool_get (ifindex, &edata)) return FALSE; - /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API - * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf - */ - /* then change the needed ones */ edata.cmd = ETHTOOL_SSET; if (autoneg) { @@ -988,7 +485,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, } } - return ethtool_call_ifindex (ifindex, &edata) >= 0; + return ethtool_get (ifindex, &edata); } gboolean @@ -1003,8 +500,8 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) return TRUE; - nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", - ifindex, (unsigned) wol, wol_password); + nm_log_dbg (LOGD_PLATFORM, "setting Wake-on-LAN options 0x%x, password '%s'", + (unsigned) wol, wol_password); wol_info.cmd = ETHTOOL_SWOL; wol_info.wolopts = 0; @@ -1024,41 +521,45 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, if (wol_password) { if (!nm_utils_hwaddr_aton (wol_password, wol_info.sopass, ETH_ALEN)) { - nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: couldn't parse Wake-on-LAN password '%s'", ifindex, wol_password); + nm_log_dbg (LOGD_PLATFORM, "couldn't parse Wake-on-LAN password '%s'", wol_password); return FALSE; } wol_info.wolopts |= WAKE_MAGICSECURE; } - return ethtool_call_ifindex (ifindex, &wol_info) >= 0; + return ethtool_get (ifindex, &wol_info); } -/****************************************************************************** +/****************************************************************** * mii - *****************************************************************************/ + ******************************************************************/ gboolean nmp_utils_mii_supports_carrier_detect (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; + char ifname[IFNAMSIZ]; + nm_auto_close int fd = -1; struct ifreq ifr; struct mii_ioctl_data *mii; g_return_val_if_fail (ifindex > 0, FALSE); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: failed creating ethtool socket: %s", - ifindex, - g_strerror (-r)); + if (!nmp_utils_if_indextoname (ifindex, ifname)) { + nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: request fails resolving ifindex: %s", ifindex, g_strerror (errno)); + return FALSE; + } + + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: couldn't open control socket: %s", ifindex, ifname, g_strerror (errno)); return FALSE; } memset (&ifr, 0, sizeof (struct ifreq)); - memcpy (ifr.ifr_name, shandle.ifname, IFNAMSIZ); + memcpy (ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl (shandle.fd, SIOCGMIIPHY, &ifr) < 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, shandle.ifname, strerror (errno)); + if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, ifname, strerror (errno)); return FALSE; } @@ -1066,18 +567,18 @@ nmp_utils_mii_supports_carrier_detect (int ifindex) mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; mii->reg_num = MII_BMSR; - if (ioctl (shandle.fd, SIOCGMIIREG, &ifr) != 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, shandle.ifname, strerror (errno)); + if (ioctl (fd, SIOCGMIIREG, &ifr) != 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, ifname, strerror (errno)); return FALSE; } - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, shandle.ifname, mii->val_out); + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, ifname, mii->val_out); return TRUE; } -/****************************************************************************** +/****************************************************************** * udev - *****************************************************************************/ + ******************************************************************/ const char * nmp_utils_udev_get_driver (struct udev_device *udevice) |