From df30651b8906cfe6a5cb7aef01a220d1f21b80f3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 16 Jul 2018 23:37:55 +0200 Subject: [PATCH] libnm, cli, ifcfg-rh: add NMSettingEthtool setting Note that in NetworkManager API (D-Bus, libnm, and nmcli), the features are called "feature-xyz". The "feature-" prefix is used, because NMSettingEthtool possibly will gain support for options that are not only -K|--offload|--features, for example -C|--coalesce. The "xzy" suffix is either how ethtool utility calls the feature ("tso", "rx"). Or, if ethtool utility specifies no alias for that feature, it's the name from kernel's ETH_SS_FEATURES ("tx-tcp6-segmentation"). If possible, we prefer ethtool utility's naming. Also note, how the features "feature-sg", "feature-tso", and "feature-tx" actually refer to multiple underlying kernel features at once. This too follows what ethtool utility does. The functionality is not yet implemented server-side. --- Makefile.am | 8 + clients/cli/connections.c | 3 +- clients/common/meson.build | 2 +- clients/common/nm-meta-setting-desc.c | 126 +++++++ clients/common/nm-meta-setting-desc.h | 4 + docs/libnm/libnm-docs.xml | 1 + libnm-core/meson.build | 2 + libnm-core/nm-core-internal.h | 5 + libnm-core/nm-core-types.h | 1 + libnm-core/nm-setting-ethtool.c | 342 ++++++++++++++++++ libnm-core/nm-setting-ethtool.h | 83 +++++ libnm-core/nm-setting.c | 14 + libnm-core/tests/test-general.c | 20 +- libnm-core/tests/test-setting.c | 77 ++++ libnm/NetworkManager.h | 1 + libnm/libnm.ver | 5 + libnm/nm-autoptr.h | 1 + po/POTFILES.in | 1 + shared/meson.build | 2 + shared/nm-ethtool-utils.c | 143 ++++++++ shared/nm-ethtool-utils.h | 79 ++++ shared/nm-meta-setting.c | 7 + shared/nm-meta-setting.h | 1 + .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 137 +++++-- .../plugins/ifcfg-rh/nms-ifcfg-rh-utils.c | 60 +++ .../plugins/ifcfg-rh/nms-ifcfg-rh-utils.h | 17 + .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 144 +++++--- ...st_write_wired_auto_negotiate_on.cexpected | 15 + .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 19 +- 29 files changed, 1235 insertions(+), 85 deletions(-) create mode 100644 libnm-core/nm-setting-ethtool.c create mode 100644 libnm-core/nm-setting-ethtool.h create mode 100644 shared/nm-ethtool-utils.c create mode 100644 shared/nm-ethtool-utils.h create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected diff --git a/Makefile.am b/Makefile.am index 516e9acbf2..fe156f9284 100644 --- a/Makefile.am +++ b/Makefile.am @@ -453,6 +453,7 @@ libnm_core_lib_h_pub_real = \ libnm-core/nm-setting-connection.h \ libnm-core/nm-setting-dcb.h \ libnm-core/nm-setting-dummy.h \ + libnm-core/nm-setting-ethtool.h \ libnm-core/nm-setting-generic.h \ libnm-core/nm-setting-gsm.h \ libnm-core/nm-setting-infiniband.h \ @@ -502,6 +503,7 @@ libnm_core_lib_h_priv = \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-random-utils.h \ shared/nm-utils/nm-udev-utils.h \ + shared/nm-ethtool-utils.h \ shared/nm-meta-setting.h \ libnm-core/crypto.h \ libnm-core/nm-connection-private.h \ @@ -524,6 +526,7 @@ libnm_core_lib_c_settings_real = \ libnm-core/nm-setting-connection.c \ libnm-core/nm-setting-dcb.c \ libnm-core/nm-setting-dummy.c \ + libnm-core/nm-setting-ethtool.c \ libnm-core/nm-setting-generic.c \ libnm-core/nm-setting-gsm.c \ libnm-core/nm-setting-infiniband.c \ @@ -565,6 +568,7 @@ libnm_core_lib_c_real = \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-random-utils.c \ shared/nm-utils/nm-udev-utils.c \ + shared/nm-ethtool-utils.c \ shared/nm-meta-setting.c \ libnm-core/crypto.c \ libnm-core/nm-connection.c \ @@ -2390,6 +2394,7 @@ EXTRA_DIST += \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3.expected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected \ + src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-dynamic-wep-leap \ src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap \ src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep \ @@ -3425,6 +3430,9 @@ clients_common_libnmc_la_SOURCES = \ shared/nm-meta-setting.c \ shared/nm-meta-setting.h \ \ + shared/nm-ethtool-utils.c \ + shared/nm-ethtool-utils.h \ + \ clients/common/nm-meta-setting-desc.c \ clients/common/nm-meta-setting-desc.h \ clients/common/nm-meta-setting-access.c \ diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 5d8c3daff7..5361e11fb1 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -770,7 +770,8 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO NM_SETTING_6LOWPAN_SETTING_NAME","\ NM_SETTING_PROXY_SETTING_NAME"," \ NM_SETTING_TC_CONFIG_SETTING_NAME"," \ - NM_SETTING_SRIOV_SETTING_NAME + NM_SETTING_SRIOV_SETTING_NAME"," \ + NM_SETTING_ETHTOOL_SETTING_NAME // NM_SETTING_DUMMY_SETTING_NAME // NM_SETTING_WIMAX_SETTING_NAME diff --git a/clients/common/meson.build b/clients/common/meson.build index 1f0bf17b01..0db2868e7c 100644 --- a/clients/common/meson.build +++ b/clients/common/meson.build @@ -59,7 +59,7 @@ libnmc = static_library( sources: files( 'nm-meta-setting-access.c', 'nm-meta-setting-desc.c' - ) + shared_nm_meta_setting_c + [settings_docs_source], + ) + shared_nm_meta_setting_c + shared_nm_ethtool_utils_c + [settings_docs_source], dependencies: deps, c_args: cflags, link_with: libnmc_base, diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 5dbbc50384..a2833be1d3 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -4823,6 +4823,84 @@ _validate_fcn_wireless_security_psk (const char *value, char **out_to_free, GErr /*****************************************************************************/ +static gconstpointer +_get_fcn_ethtool (ARGS_GET_FCN) +{ + const char *s; + NMTernary val; + NMEthtoolID ethtool_id = property_info->property_typ_data->subtype.ethtool.ethtool_id; + + RETURN_UNSUPPORTED_GET_TYPE (); + + val = nm_setting_ethtool_get_feature (NM_SETTING_ETHTOOL (setting), + nm_ethtool_data[ethtool_id]->optname); + + if (val == NM_TERNARY_TRUE) + s = N_("on"); + else if (val == NM_TERNARY_FALSE) + s = N_("off"); + else { + s = NULL; + NM_SET_OUT (out_is_default, TRUE); + } + + if (s && get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) + s = gettext (s); + return s; +} + +static gboolean +_set_fcn_ethtool (ARGS_SET_FCN) +{ + gs_free char *value_clone = NULL; + NMTernary val; + NMEthtoolID ethtool_id = property_info->property_typ_data->subtype.ethtool.ethtool_id; + + value = nm_strstrip_avoid_copy (value, &value_clone); + + if (NM_IN_STRSET (value, "1", "yes", "true", "on")) + val = NM_TERNARY_TRUE; + else if (NM_IN_STRSET (value, "0", "no", "false", "off")) + val = NM_TERNARY_FALSE; + else if (NM_IN_STRSET (value, "", "ignore", "default")) + val = NM_TERNARY_DEFAULT; + else { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is not valid; use 'on', 'off', or 'ignore'"), + value); + return FALSE; + } + + nm_setting_ethtool_set_feature (NM_SETTING_ETHTOOL (setting), + nm_ethtool_data[ethtool_id]->optname, + val); + return TRUE; +} + +static const char *const* +_complete_fcn_ethtool (ARGS_COMPLETE_FCN) +{ + static const char *const v[] = { + "true", + "false", + "1", + "0", + "yes", + "no", + "default", + "on", + "off", + "ignore", + NULL, + }; + + if (!text || !text[0]) + return &v[7]; + return v; +} + +/*****************************************************************************/ + static const NMMetaPropertyInfo property_info_BOND_OPTIONS; #define NESTED_PROPERTY_INFO_BOND(...) \ @@ -4979,6 +5057,12 @@ static const NMMetaPropertyType _pt_gobject_devices = { .complete_fcn = _complete_fcn_gobject_devices, }; +static const NMMetaPropertyType _pt_ethtool = { + .get_fcn = _get_fcn_ethtool, + .set_fcn = _set_fcn_ethtool, + .complete_fcn = _complete_fcn_ethtool, +}; + /*****************************************************************************/ #include "settings-docs.h" @@ -5775,6 +5859,31 @@ static const NMMetaPropertyInfo *const property_infos_DCB[] = { NULL }; +#define PROPERTY_INFO_ETHTOOL(xname) \ + PROPERTY_INFO (NM_ETHTOOL_OPTNAME_##xname, NULL, \ + .property_type = &_pt_ethtool, \ + .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (ethtool, \ + .ethtool_id = NM_ETHTOOL_ID_##xname, \ + ), \ + ) + +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_ETHTOOL +static const NMMetaPropertyInfo *const property_infos_ETHTOOL[] = { + PROPERTY_INFO_ETHTOOL (FEATURE_GRO), + PROPERTY_INFO_ETHTOOL (FEATURE_GSO), + PROPERTY_INFO_ETHTOOL (FEATURE_LRO), + PROPERTY_INFO_ETHTOOL (FEATURE_NTUPLE), + PROPERTY_INFO_ETHTOOL (FEATURE_RX), + PROPERTY_INFO_ETHTOOL (FEATURE_RXHASH), + PROPERTY_INFO_ETHTOOL (FEATURE_RXVLAN), + PROPERTY_INFO_ETHTOOL (FEATURE_SG), + PROPERTY_INFO_ETHTOOL (FEATURE_TSO), + PROPERTY_INFO_ETHTOOL (FEATURE_TX), + PROPERTY_INFO_ETHTOOL (FEATURE_TXVLAN), + NULL, +}; + #undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GSM static const NMMetaPropertyInfo *const property_infos_GSM[] = { @@ -7740,6 +7849,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_CONNECTION N_("General settings") #define SETTING_PRETTY_NAME_DCB N_("DCB settings") #define SETTING_PRETTY_NAME_DUMMY N_("Dummy settings") +#define SETTING_PRETTY_NAME_ETHTOOL N_("Ethtool settings") #define SETTING_PRETTY_NAME_GENERIC N_("Generic settings") #define SETTING_PRETTY_NAME_GSM N_("GSM mobile broadband connection") #define SETTING_PRETTY_NAME_INFINIBAND N_("InfiniBand connection") @@ -7827,6 +7937,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (BOND, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (BRIDGE, @@ -7834,6 +7945,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (BRIDGE, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (BRIDGE_PORT), @@ -7848,11 +7960,13 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { ), SETTING_INFO (CONNECTION), SETTING_INFO (DCB), + SETTING_INFO (ETHTOOL), SETTING_INFO_EMPTY (DUMMY, .valid_parts = NM_META_SETTING_VALID_PARTS ( NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (DUMMY, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO_EMPTY (GENERIC, @@ -7875,6 +7989,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (INFINIBAND, TRUE), NM_META_SETTING_VALID_PART_ITEM (SRIOV, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), .setting_init_fcn = _setting_init_fcn_infiniband, ), @@ -7889,6 +8004,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (IP_TUNNEL, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (MACSEC, @@ -7897,6 +8013,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (MACSEC, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), NM_META_SETTING_VALID_PART_ITEM (802_1X, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (MACVLAN, @@ -7904,6 +8021,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (MACVLAN, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (OLPC_MESH, @@ -7928,6 +8046,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG, FALSE), NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG, FALSE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (OVS_PATCH), @@ -7947,6 +8066,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (WIRED, TRUE), NM_META_SETTING_VALID_PART_ITEM (PPP, FALSE), NM_META_SETTING_VALID_PART_ITEM (802_1X, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (PPP), @@ -7961,6 +8081,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (TEAM, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (TEAM_PORT), @@ -7969,6 +8090,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (TUN, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), .setting_init_fcn = _setting_init_fcn_tun, ), @@ -7978,6 +8100,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (VLAN, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), .setting_init_fcn = _setting_init_fcn_vlan, ), @@ -7992,6 +8115,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (VXLAN, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (WIMAX, @@ -8008,6 +8132,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (802_1X, FALSE), NM_META_SETTING_VALID_PART_ITEM (DCB, FALSE), NM_META_SETTING_VALID_PART_ITEM (SRIOV, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), SETTING_INFO (WIRELESS, @@ -8017,6 +8142,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (WIRELESS, TRUE), NM_META_SETTING_VALID_PART_ITEM (WIRELESS_SECURITY, FALSE), NM_META_SETTING_VALID_PART_ITEM (802_1X, FALSE), + NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), .setting_init_fcn = _setting_init_fcn_wireless, ), diff --git a/clients/common/nm-meta-setting-desc.h b/clients/common/nm-meta-setting-desc.h index 2aa27f1b73..acb0a223b1 100644 --- a/clients/common/nm-meta-setting-desc.h +++ b/clients/common/nm-meta-setting-desc.h @@ -22,6 +22,7 @@ #include "nm-utils/nm-obj.h" #include "nm-meta-setting.h" +#include "nm-ethtool-utils.h" struct _NMDevice; @@ -263,6 +264,9 @@ struct _NMMetaPropertyTypData { struct { NMMetaPropertyTypeMacMode mode; } mac; + struct { + NMEthtoolID ethtool_id; + } ethtool; } subtype; const char *const*values_static; const NMMetaPropertyTypDataNested *nested; diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index cba1661045..94ef406a1d 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -202,6 +202,7 @@ print ("NetworkManager version " + client.get_version())]]> + diff --git a/libnm-core/meson.build b/libnm-core/meson.build index ab740cac0a..0a0406dfab 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -69,6 +69,7 @@ libnm_core_settings_sources = files( 'nm-setting-connection.c', 'nm-setting-dcb.c', 'nm-setting-dummy.c', + 'nm-setting-ethtool.c', 'nm-setting-generic.c', 'nm-setting-gsm.c', 'nm-setting-infiniband.c', @@ -154,6 +155,7 @@ endif libnm_core_sources_all = libnm_core_sources libnm_core_sources_all += libnm_core_enum libnm_core_sources_all += shared_nm_meta_setting_c +libnm_core_sources_all += shared_nm_ethtool_utils_c libnm_core_sources_all += shared_files_libnm_core libnm_core_sources_all += [version_header] diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 7338cd27af..0c999c8c84 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -201,6 +201,11 @@ GVariant *const*nm_setting_gendata_get_all_values (NMSetting *setting); /*****************************************************************************/ +guint nm_setting_ethtool_init_features (NMSettingEthtool *setting, + NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */); + +/*****************************************************************************/ + #define NM_UTILS_HWADDR_LEN_MAX_STR (NM_UTILS_HWADDR_LEN_MAX * 3) guint8 *_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length); diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index 622b104f60..4282fdfe5c 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -40,6 +40,7 @@ typedef struct _NMSettingCdma NMSettingCdma; typedef struct _NMSettingConnection NMSettingConnection; typedef struct _NMSettingDcb NMSettingDcb; typedef struct _NMSettingDummy NMSettingDummy; +typedef struct _NMSettingEthtool NMSettingEthtool; typedef struct _NMSettingGeneric NMSettingGeneric; typedef struct _NMSettingGsm NMSettingGsm; typedef struct _NMSettingInfiniband NMSettingInfiniband; diff --git a/libnm-core/nm-setting-ethtool.c b/libnm-core/nm-setting-ethtool.c new file mode 100644 index 0000000000..7bdbcb1a22 --- /dev/null +++ b/libnm-core/nm-setting-ethtool.c @@ -0,0 +1,342 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-ethtool.h" + +#include "nm-setting-private.h" +#include "nm-ethtool-utils.h" + +/*****************************************************************************/ + +/** + * SECTION:nm-setting-ethtool + * @short_description: Describes connection properties for ethtool related options + * + * The #NMSettingEthtool object is a #NMSetting subclass that describes properties + * to control network driver and hardware settings. + **/ + +/*****************************************************************************/ + +/** + * nm_ethtool_optname_is_feature: + * @optname: the option name to check + * + * Checks whether @optname is a valid option name for an offload feature. + * + * %Returns: %TRUE, if @optname is valid + * + * Since: 1.14 + */ +gboolean +nm_ethtool_optname_is_feature (const char *optname) +{ + return optname && nm_ethtool_id_is_feature (nm_ethtool_id_get_by_name (optname)); +} + +/*****************************************************************************/ + +/** + * NMSettingEthtool: + * + * Ethtool Ethernet Settings + * + * Since: 1.14 + */ +struct _NMSettingEthtool { + NMSetting parent; +}; + +struct _NMSettingEthtoolClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE (NMSettingEthtool, nm_setting_ethtool, NM_TYPE_SETTING) + +#define NM_SETTING_ETHTOOL_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettingEthtool, NM_IS_SETTING_ETHTOOL, NMSetting) + +/*****************************************************************************/ + +static void +_notify_attributes (NMSettingEthtool *self) +{ + _nm_setting_gendata_notify (NM_SETTING (self), TRUE); +} + +/*****************************************************************************/ + +/** + * nm_setting_ethtool_get_feature: + * @setting: the #NMSettingEthtool + * @optname: option name of the offload feature to get + * + * Gets and offload feature setting. Returns %NM_TERNARY_DEFAULT if the + * feature is not set. + * + * Returns: a #NMTernary value indicating whether the offload feature + * is enabled, disabled, or left untouched. + * + * Since: 1.14 + */ +NMTernary +nm_setting_ethtool_get_feature (NMSettingEthtool *setting, + const char *optname) +{ + GVariant *v; + + g_return_val_if_fail (NM_IS_SETTING_ETHTOOL (setting), NM_TERNARY_DEFAULT); + g_return_val_if_fail (optname && nm_ethtool_optname_is_feature (optname), NM_TERNARY_DEFAULT); + + v = nm_setting_gendata_get (NM_SETTING (setting), optname); + if ( v + && g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) { + return g_variant_get_boolean (v) + ? NM_TERNARY_TRUE + : NM_TERNARY_FALSE; + } + return NM_TERNARY_DEFAULT; +} + +/** + * nm_setting_ethtool_set_feature: + * @setting: the #NMSettingEthtool + * @optname: option name of the offload feature to get + * @value: the new value to set. The special value %NM_TERNARY_DEFAULT + * means to clear the offload feature setting. + * + * Sets and offload feature setting. + * + * Since: 1.14 + */ +void +nm_setting_ethtool_set_feature (NMSettingEthtool *setting, + const char *optname, + NMTernary value) +{ + GHashTable *hash; + GVariant *v; + + g_return_if_fail (NM_IS_SETTING_ETHTOOL (setting)); + g_return_if_fail (optname && nm_ethtool_optname_is_feature (optname)); + g_return_if_fail (NM_IN_SET (value, NM_TERNARY_DEFAULT, + NM_TERNARY_FALSE, + NM_TERNARY_TRUE)); + + hash = _nm_setting_gendata_hash (NM_SETTING (setting), + value != NM_TERNARY_DEFAULT); + + if (value == NM_TERNARY_DEFAULT) { + if (hash) { + if (g_hash_table_remove (hash, optname)) + _notify_attributes (setting); + } + return; + } + + v = g_hash_table_lookup (hash, optname); + if ( v + && g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) { + if (g_variant_get_boolean (v)) { + if (value == NM_TERNARY_TRUE) + return; + } else { + if (value == NM_TERNARY_FALSE) + return; + } + } + + v = g_variant_ref_sink (g_variant_new_boolean (value != NM_TERNARY_FALSE)); + g_hash_table_insert (hash, + g_strdup (optname), + v); + _notify_attributes (setting); +} + +/** + * nm_setting_ethtool_clear_features: + * @setting: the #NMSettingEthtool + * + * Clears all offload features settings + * + * Since: 1.14 + */ +void +nm_setting_ethtool_clear_features (NMSettingEthtool *setting) +{ + GHashTable *hash; + GHashTableIter iter; + const char *name; + gboolean changed = FALSE; + + g_return_if_fail (NM_IS_SETTING_ETHTOOL (setting)); + + hash = _nm_setting_gendata_hash (NM_SETTING (setting), FALSE); + if (!hash) + return; + + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, NULL)) { + if (nm_ethtool_optname_is_feature (name)) { + g_hash_table_iter_remove (&iter); + changed = TRUE; + } + } + + if (changed) + _notify_attributes (setting); +} + +guint +nm_setting_ethtool_init_features (NMSettingEthtool *setting, + NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */) +{ + GHashTable *hash; + GHashTableIter iter; + guint i; + guint n_req = 0; + const char *name; + GVariant *variant; + + nm_assert (NM_IS_SETTING_ETHTOOL (setting)); + nm_assert (requested); + + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) + requested[i] = NM_TERNARY_DEFAULT; + + hash = _nm_setting_gendata_hash (NM_SETTING (setting), FALSE); + if (!hash) + return 0; + + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) { + NMEthtoolID ethtool_id = nm_ethtool_id_get_by_name (name); + + if (!nm_ethtool_id_is_feature (ethtool_id)) + continue; + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) + continue; + + requested[ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = g_variant_get_boolean (variant) + ? NM_TERNARY_TRUE + : NM_TERNARY_FALSE; + n_req++; + } + + return n_req; +} + +/*****************************************************************************/ + +static gboolean +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + GHashTable *hash; + GHashTableIter iter; + const char *optname; + GVariant *variant; + + hash = _nm_setting_gendata_hash (setting, FALSE); + + if (!hash) + goto out; + + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &optname, (gpointer *) &variant)) { + if (!nm_ethtool_optname_is_feature (optname)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("unsupported offload feature")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ETHTOOL_SETTING_NAME, optname); + return FALSE; + } + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("offload feature has invalid variant type")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ETHTOOL_SETTING_NAME, optname); + return FALSE; + } + } + +out: + return TRUE; +} + +/*****************************************************************************/ + +static const GVariantType * +get_variant_type (const NMSettInfoSetting *sett_info, + const char *name, + GError **error) +{ + if (nm_ethtool_optname_is_feature (name)) + return G_VARIANT_TYPE_BOOLEAN; + + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("unknown ethtool option '%s'"), + name); + return NULL; +} + +/*****************************************************************************/ + +static void +nm_setting_ethtool_init (NMSettingEthtool *setting) +{ +} + +/** + * nm_setting_ethtool_new: + * + * Creates a new #NMSettingEthtool object with default values. + * + * Returns: (transfer full): the new empty #NMSettingEthtool object + * + * Since: 1.14 + **/ +NMSetting * +nm_setting_ethtool_new (void) +{ + return g_object_new (NM_TYPE_SETTING_ETHTOOL, NULL); +} + +static void +nm_setting_ethtool_class_init (NMSettingEthtoolClass *klass) +{ + NMSettingClass *setting_class = NM_SETTING_CLASS (klass); + + setting_class->verify = verify; + + _nm_setting_class_commit_full (setting_class, + NM_META_SETTING_TYPE_ETHTOOL, + NM_SETT_INFO_SETT_DETAIL ( + .gendata_info = NM_SETT_INFO_SETT_GENDATA ( + .get_variant_type = get_variant_type, + ), + ), + NULL); +} diff --git a/libnm-core/nm-setting-ethtool.h b/libnm-core/nm-setting-ethtool.h new file mode 100644 index 0000000000..763d2691ee --- /dev/null +++ b/libnm-core/nm-setting-ethtool.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_ETHTOOL_H__ +#define __NM_SETTING_ETHTOOL_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +/*****************************************************************************/ + +#define NM_ETHTOOL_OPTNAME_FEATURE_GRO "feature-gro" +#define NM_ETHTOOL_OPTNAME_FEATURE_GSO "feature-gso" +#define NM_ETHTOOL_OPTNAME_FEATURE_LRO "feature-lro" +#define NM_ETHTOOL_OPTNAME_FEATURE_NTUPLE "feature-ntuple" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX "feature-rx" +#define NM_ETHTOOL_OPTNAME_FEATURE_RXHASH "feature-rxhash" +#define NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN "feature-rxvlan" +#define NM_ETHTOOL_OPTNAME_FEATURE_SG "feature-sg" +#define NM_ETHTOOL_OPTNAME_FEATURE_TSO "feature-tso" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX "feature-tx" +#define NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN "feature-txvlan" + +gboolean nm_ethtool_optname_is_feature (const char *optname); + +/*****************************************************************************/ + +#define NM_TYPE_SETTING_ETHTOOL (nm_setting_ethtool_get_type ()) +#define NM_SETTING_ETHTOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_ETHTOOL, NMSettingEthtool)) +#define NM_SETTING_ETHTOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_ETHTOOL, NMSettingEthtoolClass)) +#define NM_IS_SETTING_ETHTOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_ETHTOOL)) +#define NM_IS_SETTING_ETHTOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_ETHTOOL)) +#define NM_SETTING_ETHTOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_ETHTOOL, NMSettingEthtoolClass)) + +#define NM_SETTING_ETHTOOL_SETTING_NAME "ethtool" + +/*****************************************************************************/ + +typedef struct _NMSettingEthtoolClass NMSettingEthtoolClass; + +NM_AVAILABLE_IN_1_14 +GType nm_setting_ethtool_get_type (void); + +NM_AVAILABLE_IN_1_14 +NMSetting *nm_setting_ethtool_new (void); + +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_14 +NMTernary nm_setting_ethtool_get_feature (NMSettingEthtool *setting, + const char *optname); +NM_AVAILABLE_IN_1_14 +void nm_setting_ethtool_set_feature (NMSettingEthtool *setting, + const char *optname, + NMTernary value); +NM_AVAILABLE_IN_1_14 +void nm_setting_ethtool_clear_features (NMSettingEthtool *setting); + +G_END_DECLS + +#endif /* __NM_SETTING_ETHTOOL_H__ */ diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c index 53a2a22a16..d59e3df79d 100644 --- a/libnm-core/nm-setting.c +++ b/libnm-core/nm-setting.c @@ -2095,6 +2095,20 @@ _nm_setting_gendata_notify (NMSetting *setting, * values cache. Otherwise, the names cache must be invalidated too. */ nm_clear_g_free (&gendata->names); } + + /* Note, that currently there is now way to notify the subclass when gendata changed. + * gendata is only changed in two situations: + * 1) from within NMSetting itself, for example when creating a NMSetting instance + * from keyfile or a D-Bus GVariant. + * 2) actively from the subclass itself + * For 2), we don't need the notification, because the subclass knows that something + * changed. + * For 1), we currently don't need the notification either, because all that the subclass + * currently would do, is emit a g_object_notify() signal. However, 1) only happens when + * the setting instance is newly created, at that point, nobody listens to the signal. + * + * If we ever need it, then we would need to call a virtual function to notify the subclass + * that gendata changed. */ } GVariant * diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 39e2cf90e4..db1d1c1447 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -43,6 +43,7 @@ #include "nm-setting-bridge-port.h" #include "nm-setting-cdma.h" #include "nm-setting-connection.h" +#include "nm-setting-ethtool.h" #include "nm-setting-generic.h" #include "nm-setting-gsm.h" #include "nm-setting-infiniband.h" @@ -65,6 +66,7 @@ #include "nm-simple-connection.h" #include "nm-keyfile-internal.h" #include "nm-utils/nm-dedup-multi.h" +#include "nm-ethtool-utils.h" #include "test-general-enums.h" @@ -7058,6 +7060,22 @@ test_nm_va_args_macros (void) /*****************************************************************************/ +static void +test_ethtool_offload (void) +{ + const NMEthtoolData *d; + + g_assert_cmpint (nm_ethtool_id_get_by_name ("invalid"), ==, NM_ETHTOOL_ID_UNKNOWN); + g_assert_cmpint (nm_ethtool_id_get_by_name ("feature-rx"), ==, NM_ETHTOOL_ID_FEATURE_RX); + + d = nm_ethtool_data_get_by_optname (NM_ETHTOOL_OPTNAME_FEATURE_RXHASH); + g_assert (d); + g_assert_cmpint (d->id, ==, NM_ETHTOOL_ID_FEATURE_RXHASH); + g_assert_cmpstr (d->optname, ==, NM_ETHTOOL_OPTNAME_FEATURE_RXHASH); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -7212,8 +7230,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/route_attributes/format", test_route_attributes_format); g_test_add_func ("/core/general/get_start_time_for_pid", test_get_start_time_for_pid); - g_test_add_func ("/core/general/test_nm_va_args_macros", test_nm_va_args_macros); + g_test_add_func ("/core/general/test_ethtool_offload", test_ethtool_offload); return g_test_run (); } diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index cb02d1c4a5..5526ad8a7e 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -28,6 +28,7 @@ #include "nm-setting-8021x.h" #include "nm-setting-bond.h" #include "nm-setting-dcb.h" +#include "nm-setting-ethtool.h" #include "nm-setting-team.h" #include "nm-setting-team-port.h" #include "nm-setting-tc-config.h" @@ -36,6 +37,7 @@ #include "nm-simple-connection.h" #include "nm-setting-connection.h" #include "nm-errors.h" +#include "nm-keyfile-internal.h" #include "nm-utils/nm-test-utils.h" @@ -1264,6 +1266,79 @@ test_team_port_full_config (void) /*****************************************************************************/ +static void +test_ethtool_1 (void) +{ + gs_unref_object NMConnection *con = NULL; + gs_unref_object NMConnection *con2 = NULL; + gs_unref_object NMConnection *con3 = NULL; + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + NMSettingConnection *s_con; + NMSettingEthtool *s_ethtool; + NMSettingEthtool *s_ethtool2; + NMSettingEthtool *s_ethtool3; + + con = nmtst_create_minimal_connection ("ethtool-1", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ethtool)); + + nm_setting_ethtool_set_feature (s_ethtool, + NM_ETHTOOL_OPTNAME_FEATURE_RX, + NM_TERNARY_TRUE); + nm_setting_ethtool_set_feature (s_ethtool, + NM_ETHTOOL_OPTNAME_FEATURE_LRO, + NM_TERNARY_FALSE); + + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_SG), ==, NM_TERNARY_DEFAULT); + + nmtst_connection_normalize (con); + + variant = nm_connection_to_dbus (con, NM_CONNECTION_SERIALIZE_ALL); + + con2 = nm_simple_connection_new_from_dbus (variant, &error); + nmtst_assert_success (con2, error); + + s_ethtool2 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con2, NM_TYPE_SETTING_ETHTOOL)); + + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_SG), ==, NM_TERNARY_DEFAULT); + + nmtst_assert_connection_verifies_without_normalization (con2); + + nmtst_assert_connection_equals (con, FALSE, con2, FALSE); + + keyfile = nm_keyfile_write (con, NULL, NULL, &error); + nmtst_assert_success (keyfile, error); + + con3 = nm_keyfile_read (keyfile, + "ethtool-keyfile-name", + NULL, + NULL, + NULL, + &error); + nmtst_assert_success (con3, error); + + nmtst_connection_normalize (con3); + + nmtst_assert_connection_equals (con, FALSE, con3, FALSE); + + s_ethtool3 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con3, NM_TYPE_SETTING_ETHTOOL)); + + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_SG), ==, NM_TERNARY_DEFAULT); +} + +/*****************************************************************************/ + static void test_sriov_vf (void) { @@ -1899,6 +1974,8 @@ main (int argc, char **argv) g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid); g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums); + g_test_add_func ("/libnm/settings/ethtool/1", test_ethtool_1); + g_test_add_func ("/libnm/settings/sriov/vf", test_sriov_vf); g_test_add_func ("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup); g_test_add_func ("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan); diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h index 070a02a9af..a4af9b0d8e 100644 --- a/libnm/NetworkManager.h +++ b/libnm/NetworkManager.h @@ -72,6 +72,7 @@ #include "nm-setting-connection.h" #include "nm-setting-dcb.h" #include "nm-setting-dummy.h" +#include "nm-setting-ethtool.h" #include "nm-setting-generic.h" #include "nm-setting-gsm.h" #include "nm-setting-infiniband.h" diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 15f7283fea..042453daa4 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1394,6 +1394,11 @@ global: nm_device_wpan_get_type; nm_setting_6lowpan_get_type; nm_setting_connection_get_multi_connect; + nm_setting_ethtool_clear_features; + nm_setting_ethtool_get_feature; + nm_setting_ethtool_get_type; + nm_setting_ethtool_new; + nm_setting_ethtool_set_feature; nm_setting_sriov_add_vf; nm_setting_sriov_clear_vfs; nm_setting_sriov_get_autoprobe_drivers; diff --git a/libnm/nm-autoptr.h b/libnm/nm-autoptr.h index c56f1ecfae..665c28c0e4 100644 --- a/libnm/nm-autoptr.h +++ b/libnm/nm-autoptr.h @@ -47,6 +47,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingCdma, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingConnection, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingDcb, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingDummy, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingEthtool, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingGeneric, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingGsm, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (NMSettingInfiniband, g_object_unref) diff --git a/po/POTFILES.in b/po/POTFILES.in index fafc4225d2..ee78d29344 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -67,6 +67,7 @@ libnm-core/nm-setting-bridge.c libnm-core/nm-setting-cdma.c libnm-core/nm-setting-connection.c libnm-core/nm-setting-dcb.c +libnm-core/nm-setting-ethtool.c libnm-core/nm-setting-gsm.c libnm-core/nm-setting-infiniband.c libnm-core/nm-setting-ip-config.c diff --git a/shared/meson.build b/shared/meson.build index 26ce317495..8faec8765b 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -36,6 +36,8 @@ version_header = configure_file( configuration: version_conf, ) +shared_nm_ethtool_utils_c = files('nm-ethtool-utils.c') + shared_nm_meta_setting_c = files('nm-meta-setting.c') shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c') diff --git a/shared/nm-ethtool-utils.c b/shared/nm-ethtool-utils.c new file mode 100644 index 0000000000..1e6d405a88 --- /dev/null +++ b/shared/nm-ethtool-utils.c @@ -0,0 +1,143 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-ethtool-utils.h" + +#include "nm-setting-ethtool.h" + +/*****************************************************************************/ + +#define ETHT_DATA(xname) \ + [NM_ETHTOOL_ID_##xname] = (&((const NMEthtoolData) { \ + .optname = NM_ETHTOOL_OPTNAME_##xname, \ + .id = NM_ETHTOOL_ID_##xname, \ + })) + +const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = { + /* indexed by NMEthtoolID */ + ETHT_DATA (FEATURE_GRO), + ETHT_DATA (FEATURE_GSO), + ETHT_DATA (FEATURE_LRO), + ETHT_DATA (FEATURE_NTUPLE), + ETHT_DATA (FEATURE_RX), + ETHT_DATA (FEATURE_RXHASH), + ETHT_DATA (FEATURE_RXVLAN), + ETHT_DATA (FEATURE_SG), + ETHT_DATA (FEATURE_TSO), + ETHT_DATA (FEATURE_TX), + ETHT_DATA (FEATURE_TXVLAN), + [_NM_ETHTOOL_ID_NUM] = NULL, +}; + +const guint8 const _by_name[_NM_ETHTOOL_ID_NUM] = { + /* sorted by optname. */ + NM_ETHTOOL_ID_FEATURE_GRO, + NM_ETHTOOL_ID_FEATURE_GSO, + NM_ETHTOOL_ID_FEATURE_LRO, + NM_ETHTOOL_ID_FEATURE_NTUPLE, + NM_ETHTOOL_ID_FEATURE_RX, + NM_ETHTOOL_ID_FEATURE_RXHASH, + NM_ETHTOOL_ID_FEATURE_RXVLAN, + NM_ETHTOOL_ID_FEATURE_SG, + NM_ETHTOOL_ID_FEATURE_TSO, + NM_ETHTOOL_ID_FEATURE_TX, + NM_ETHTOOL_ID_FEATURE_TXVLAN, +}; + +/*****************************************************************************/ + +static void +_ASSERT_data (void) +{ +#if NM_MORE_ASSERTS > 10 + int i; + + G_STATIC_ASSERT_EXPR (_NM_ETHTOOL_ID_FIRST == 0); + G_STATIC_ASSERT_EXPR (_NM_ETHTOOL_ID_LAST == _NM_ETHTOOL_ID_NUM - 1); + G_STATIC_ASSERT_EXPR (_NM_ETHTOOL_ID_NUM > 0); + + nm_assert (NM_PTRARRAY_LEN (nm_ethtool_data) == _NM_ETHTOOL_ID_NUM); + nm_assert (G_N_ELEMENTS (_by_name) == _NM_ETHTOOL_ID_NUM); + nm_assert (G_N_ELEMENTS (nm_ethtool_data) == _NM_ETHTOOL_ID_NUM + 1); + + for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { + const NMEthtoolData *d = nm_ethtool_data[i]; + + nm_assert (d); + nm_assert (d->id == (NMEthtoolID) i); + nm_assert (d->optname && d->optname[0]); + } + + for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { + NMEthtoolID id = _by_name[i]; + const NMEthtoolData *d; + + nm_assert (id >= 0); + nm_assert (id < _NM_ETHTOOL_ID_NUM); + + d = nm_ethtool_data[id]; + if (i > 0) { + /* since we assert that all optnames are sorted strictly monotonically increasing, + * it also follows that there are no duplicates in the _by_name. + * It also follows, that all names in nm_ethtool_data are unique. */ + if (strcmp (nm_ethtool_data[_by_name[i - 1]]->optname, d->optname) >= 0) { + g_error ("nm_ethtool_data is not sorted asciibetically: %u/%s should be after %u/%s", + i - 1, nm_ethtool_data[_by_name[i - 1]]->optname, + i, d->optname); + } + } + } +#endif +} + +static int +_by_name_cmp (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const guint8 *p_id = a; + const char *optname = b; + + nm_assert (p_id && p_id >= _by_name && p_id <= &_by_name[_NM_ETHTOOL_ID_NUM]); + nm_assert (*p_id < _NM_ETHTOOL_ID_NUM); + + return strcmp (nm_ethtool_data[*p_id]->optname, optname); +} + +const NMEthtoolData * +nm_ethtool_data_get_by_optname (const char *optname) +{ + gssize idx; + + nm_assert (optname); + + _ASSERT_data (); + + idx = nm_utils_array_find_binary_search ((gconstpointer *) _by_name, + sizeof (_by_name[0]), + _NM_ETHTOOL_ID_NUM, + optname, + _by_name_cmp, + NULL); + return (idx < 0) ? NULL : nm_ethtool_data[_by_name[idx]]; +} diff --git a/shared/nm-ethtool-utils.h b/shared/nm-ethtool-utils.h new file mode 100644 index 0000000000..06956d0462 --- /dev/null +++ b/shared/nm-ethtool-utils.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_ETHTOOL_UTILS_H__ +#define __NM_ETHTOOL_UTILS_H__ + +/*****************************************************************************/ + +typedef enum { + NM_ETHTOOL_ID_UNKNOWN = -1, + + _NM_ETHTOOL_ID_FIRST = 0, + + _NM_ETHTOOL_ID_FEATURE_FIRST = _NM_ETHTOOL_ID_FIRST, + NM_ETHTOOL_ID_FEATURE_GRO = _NM_ETHTOOL_ID_FEATURE_FIRST, + NM_ETHTOOL_ID_FEATURE_GSO, + NM_ETHTOOL_ID_FEATURE_LRO, + NM_ETHTOOL_ID_FEATURE_NTUPLE, + NM_ETHTOOL_ID_FEATURE_RX, + NM_ETHTOOL_ID_FEATURE_RXHASH, + NM_ETHTOOL_ID_FEATURE_RXVLAN, + NM_ETHTOOL_ID_FEATURE_SG, + NM_ETHTOOL_ID_FEATURE_TSO, + NM_ETHTOOL_ID_FEATURE_TX, + NM_ETHTOOL_ID_FEATURE_TXVLAN, + _NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TXVLAN, + _NM_ETHTOOL_ID_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), + + _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_FEATURE_LAST, + + _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), +} NMEthtoolID; + +typedef struct { + const char *optname; + NMEthtoolID id; +} NMEthtoolData; + +extern const NMEthtoolData *const nm_ethtool_data[/*_NM_ETHTOOL_ID_NUM + NULL-terminated*/]; + +const NMEthtoolData *nm_ethtool_data_get_by_optname (const char *optname); + +/****************************************************************************/ + +static inline NMEthtoolID +nm_ethtool_id_get_by_name (const char *optname) +{ + const NMEthtoolData *d; + + d = nm_ethtool_data_get_by_optname (optname); + return d ? d->id : NM_ETHTOOL_ID_UNKNOWN; +} + +static inline gboolean +nm_ethtool_id_is_feature (NMEthtoolID id) +{ + return id >= _NM_ETHTOOL_ID_FEATURE_FIRST && id <= _NM_ETHTOOL_ID_FEATURE_LAST; +} + +/****************************************************************************/ + +#endif /* __NM_ETHTOOL_UTILS_H__ */ diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index 4ef07d85e5..c565e55f38 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -34,6 +34,7 @@ #include "nm-setting-connection.h" #include "nm-setting-dcb.h" #include "nm-setting-dummy.h" +#include "nm-setting-ethtool.h" #include "nm-setting-generic.h" #include "nm-setting-gsm.h" #include "nm-setting-infiniband.h" @@ -213,6 +214,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_DUMMY_SETTING_NAME, .get_setting_gtype = nm_setting_dummy_get_type, }, + [NM_META_SETTING_TYPE_ETHTOOL] = { + .meta_type = NM_META_SETTING_TYPE_ETHTOOL, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_ETHTOOL_SETTING_NAME, + .get_setting_gtype = nm_setting_ethtool_get_type, + }, [NM_META_SETTING_TYPE_GENERIC] = { .meta_type = NM_META_SETTING_TYPE_GENERIC, .setting_priority = NM_SETTING_PRIORITY_HW_BASE, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index 677ae4c00d..c76a4b7008 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -118,6 +118,7 @@ typedef enum { NM_META_SETTING_TYPE_CONNECTION, NM_META_SETTING_TYPE_DCB, NM_META_SETTING_TYPE_DUMMY, + NM_META_SETTING_TYPE_ETHTOOL, NM_META_SETTING_TYPE_GENERIC, NM_META_SETTING_TYPE_GSM, NM_META_SETTING_TYPE_INFINIBAND, diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index ee9e28571d..804139e41d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -41,6 +41,7 @@ #include "nm-setting-ip6-config.h" #include "nm-setting-wired.h" #include "nm-setting-wireless.h" +#include "nm-setting-ethtool.h" #include "nm-setting-8021x.h" #include "nm-setting-bond.h" #include "nm-setting-team.h" @@ -53,6 +54,7 @@ #include "nm-setting-generic.h" #include "nm-core-internal.h" #include "nm-utils.h" +#include "nm-ethtool-utils.h" #include "platform/nm-platform.h" #include "NetworkManagerUtils.h" @@ -4051,15 +4053,66 @@ parse_ethtool_option (const char *value, char **out_password, gboolean *out_autoneg, guint32 *out_speed, - const char **out_duplex) + const char **out_duplex, + NMSettingEthtool **out_s_ethtool) { gs_free const char **words = NULL; guint i; - words = nm_utils_strsplit_set (value, "\t \n"); + words = nm_utils_strsplit_set (value, NULL); if (!words) return; + if (words[0] && words[0][0] == '-') { + /* /sbin/ethtool $opts */ + if (NM_IN_STRSET (words[0], "-K", "--features", "--offload")) { + if (!words[1]) { + /* first argument must be the interface name. This is invalid. */ + return; + } + + if (!*out_s_ethtool) + *out_s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ()); + + for (i = 2; words[i]; ) { + const char *opt = words[i]; + const char *opt_val = words[++i]; + const NMEthtoolData *d = NULL; + NMTernary onoff = NM_TERNARY_DEFAULT; + + if (nm_streq0 (opt_val, "on")) + onoff = NM_TERNARY_TRUE; + else if (nm_streq0 (opt_val, "off")) + onoff = NM_TERNARY_FALSE; + + d = nms_ifcfg_rh_utils_get_ethtool_by_name (opt); + + if (!d) { + if (onoff != NM_TERNARY_DEFAULT) { + /* the next value is just the on/off argument. Skip it too. */ + i++; + } + + /* silently ignore unsupported offloading features. */ + continue; + } + + i++; + + if (onoff == NM_TERNARY_DEFAULT) { + PARSE_WARNING ("Expects on/off argument for feature '%s'", opt); + continue; + } + + nm_setting_ethtool_set_feature (*out_s_ethtool, + d->optname, + onoff); + } + } + return; + } + + /* /sbin/ethtool -s ${REALDEVICE} $opts */ for (i = 0; words[i]; ) { const char *opt = words[i]; const char *opt_val = words[++i]; @@ -4176,54 +4229,72 @@ parse_ethtool_option (const char *value, } static void -parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *value) +parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) { + NMSettingWired *s_wired; + gs_unref_object NMSettingEthtool *s_ethtool = NULL; NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT; + gs_free char *ethtool_opts_free = NULL; + const char *ethtool_opts; gs_free char *wol_password = NULL; - gs_free char *wol_value = NULL; - gboolean ignore_wol_password = FALSE; + gs_free char *wol_value_free = NULL; + const char *tmp; gboolean autoneg = FALSE; guint32 speed = 0; const char *duplex = NULL; - if (value) { - gs_free const char **opts = NULL; - const char *const *iter; - + ethtool_opts = svGetValue (ifcfg, "ETHTOOL_OPTS", ðtool_opts_free); + if (ethtool_opts) { /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ - if (!value[0]) + if (!ethtool_opts[0]) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; + else { + gs_free const char **opts = NULL; + const char *const *iter; - opts = nm_utils_strsplit_set (value, ";"); - for (iter = opts; iter && iter[0]; iter++) { - /* in case of repeated wol_passwords, parse_ethtool_option() - * will do the right thing and clear wol_password before resetting. */ - parse_ethtool_option (iter[0], &wol_flags, &wol_password, &autoneg, &speed, &duplex); + opts = nm_utils_strsplit_set (ethtool_opts, ";"); + for (iter = opts; iter && iter[0]; iter++) { + /* in case of repeated wol_passwords, parse_ethtool_option() + * will do the right thing and clear wol_password before resetting. */ + parse_ethtool_option (iter[0], + &wol_flags, + &wol_password, + &autoneg, + &speed, + &duplex, + &s_ethtool); + } } } /* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */ - wol_value = svGetValueStr_cp (ifcfg, "ETHTOOL_WAKE_ON_LAN"); - if (wol_value) { - if (strcmp (wol_value, "ignore") == 0) - wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; - else - PARSE_WARNING ("invalid ETHTOOL_WAKE_ON_LAN value '%s'", wol_value); - } + tmp = svGetValueStr (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + if (nm_streq0 (tmp, "ignore")) + wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; + else if (tmp) + PARSE_WARNING ("invalid ETHTOOL_WAKE_ON_LAN value '%s'", tmp); if ( wol_password && !NM_FLAGS_HAS (wol_flags, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) { PARSE_WARNING ("Wake-on-LAN password not expected"); - ignore_wol_password = TRUE; + nm_clear_g_free (&wol_password); } - g_object_set (s_wired, - NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, - NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, ignore_wol_password ? NULL : wol_password, - NM_SETTING_WIRED_AUTO_NEGOTIATE, autoneg, - NM_SETTING_WIRED_SPEED, speed, - NM_SETTING_WIRED_DUPLEX, duplex, - NULL); + s_wired = nm_connection_get_setting_wired (connection); + if (s_wired) { + g_object_set (s_wired, + NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, + NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, wol_password, + NM_SETTING_WIRED_AUTO_NEGOTIATE, autoneg, + NM_SETTING_WIRED_SPEED, speed, + NM_SETTING_WIRED_DUPLEX, duplex, + NULL); + } + + if (s_ethtool) { + nm_connection_add_setting (connection, + NM_SETTING (g_steal_pointer (&s_ethtool))); + } } static NMSetting * @@ -4365,10 +4436,6 @@ make_wired_setting (shvarFile *ifcfg, nm_clear_g_free (&value); } - parse_ethtool_options (ifcfg, s_wired, - svGetValue (ifcfg, "ETHTOOL_OPTS", &value)); - nm_clear_g_free (&value); - return (NMSetting *) g_steal_pointer (&s_wired); } @@ -5595,6 +5662,8 @@ connection_from_file_full (const char *filename, if (!connection) return NULL; + parse_ethtool_options (parsed, connection); + has_complex_routes_v4 = utils_has_complex_routes (filename, AF_INET); has_complex_routes_v6 = utils_has_complex_routes (filename, AF_INET6); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 862e640e02..fecce50e29 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -470,3 +470,63 @@ nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer) return TRUE; } + +/*****************************************************************************/ + +const char *const _nm_ethtool_ifcfg_names[] = { +#define ETHT_NAME(eid, ename) \ +[eid - _NM_ETHTOOL_ID_FEATURE_FIRST] = ""ename"" + /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */ + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GRO, "gro"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GSO, "gso"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_LRO, "lro"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_NTUPLE, "ntuple"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX, "rx"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXHASH, "rxhash"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXVLAN, "rxvlan"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_SG, "sg"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TSO, "tso"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX, "tx"), + ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TXVLAN, "txvlan"), +}; + +const NMEthtoolData * +nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name) +{ + static const struct { + NMEthtoolID ethtool_id; + const char *kernel_name; + } kernel_names[] = { + { NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro" }, + { NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation" }, + { NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro" }, + { NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter" }, + { NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum" }, + { NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing" }, + { NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse" }, + { NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert" }, + }; + guint i; + + for (i = 0; i < G_N_ELEMENTS (_nm_ethtool_ifcfg_names); i++) { + if (nm_streq (name, _nm_ethtool_ifcfg_names[i])) + return nm_ethtool_data[i]; + } + + /* Option not found. Note that ethtool utility has built-in features and + * NetworkManager's API follows the naming of these built-in features, whenever + * they exist. + * For example, NM's "ethtool.feature-ntuple" corresponds to ethtool utility's "ntuple" + * feature. However the underlying kernel feature is called "rx-ntuple-filter" (as reported + * for ETH_SS_FEATURES). + * + * With ethtool utility, whose command line we attempt to parse here, the user can also + * specify the name of the underlying kernel feature directly. So, check whether that is + * the case and if yes, map them to the corresponding NetworkManager's features. */ + for (i = 0; i < G_N_ELEMENTS (kernel_names); i++) { + if (nm_streq (name, kernel_names[i].kernel_name)) + return nm_ethtool_data[kernel_names[i].ethtool_id]; + } + + return NULL; +} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index 3756af7cc6..d95b1f61cf 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -22,6 +22,7 @@ #define _UTILS_H_ #include "nm-connection.h" +#include "nm-ethtool-utils.h" #include "shvar.h" @@ -80,4 +81,20 @@ _nms_ifcfg_rh_utils_numbered_tag (char *buf, gsize buf_len, const char *tag_name _nms_ifcfg_rh_utils_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \ }) +/*****************************************************************************/ + +extern const char *const _nm_ethtool_ifcfg_names[_NM_ETHTOOL_ID_FEATURE_NUM]; + +static inline const char * +nms_ifcfg_rh_utils_get_ethtool_name (NMEthtoolID ethtool_id) +{ + nm_assert (ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST && ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST); + nm_assert ((ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST) < G_N_ELEMENTS (_nm_ethtool_ifcfg_names)); + nm_assert (_nm_ethtool_ifcfg_names[ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]); + + return _nm_ethtool_ifcfg_names[ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]; +} + +const NMEthtoolData *nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name); + #endif /* _UTILS_H_ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 4fed68a254..a2abe995a8 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -37,6 +37,7 @@ #include "nm-setting-connection.h" #include "nm-setting-wired.h" #include "nm-setting-wireless.h" +#include "nm-setting-ethtool.h" #include "nm-setting-8021x.h" #include "nm-setting-proxy.h" #include "nm-setting-ip4-config.h" @@ -50,6 +51,7 @@ #include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-meta-setting.h" +#include "nm-ethtool-utils.h" #include "nms-ifcfg-rh-common.h" #include "nms-ifcfg-rh-reader.h" @@ -1135,6 +1137,7 @@ static gboolean write_ethtool_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingWired *s_wired; + NMSettingEthtool *s_ethtool; const char *duplex; guint32 speed; GString *str = NULL; @@ -1143,65 +1146,112 @@ write_ethtool_setting (NMConnection *connection, shvarFile *ifcfg, GError **erro const char *wol_password; s_wired = nm_connection_get_setting_wired (connection); + s_ethtool = NM_SETTING_ETHTOOL (nm_connection_get_setting (connection, NM_TYPE_SETTING_ETHTOOL)); - if (!s_wired) + if (!s_wired && !s_ethtool) { + svUnsetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN"); + svUnsetValue (ifcfg, "ETHTOOL_OPTS"); return TRUE; - - auto_negotiate = nm_setting_wired_get_auto_negotiate (s_wired); - speed = nm_setting_wired_get_speed (s_wired); - duplex = nm_setting_wired_get_duplex (s_wired); - - /* autoneg off + speed 0 + duplex NULL, means we want NM - * to skip link configuration which is default. So write - * down link config only if we have auto-negotiate true or - * a valid value for one among speed and duplex. - */ - if (auto_negotiate) { - str = g_string_sized_new (64); - g_string_printf (str, "autoneg on"); - } else if (speed || duplex) { - str = g_string_sized_new (64); - g_string_printf (str, "autoneg off"); } - if (speed) - g_string_append_printf (str, " speed %u", speed); - if (duplex) - g_string_append_printf (str, " duplex %s", duplex); - wol = nm_setting_wired_get_wake_on_lan (s_wired); - wol_password = nm_setting_wired_get_wake_on_lan_password (s_wired); + if (s_wired) { + auto_negotiate = nm_setting_wired_get_auto_negotiate (s_wired); + speed = nm_setting_wired_get_speed (s_wired); + duplex = nm_setting_wired_get_duplex (s_wired); + + /* autoneg off + speed 0 + duplex NULL, means we want NM + * to skip link configuration which is default. So write + * down link config only if we have auto-negotiate true or + * a valid value for one among speed and duplex. + */ + if (auto_negotiate) { + str = g_string_sized_new (64); + g_string_printf (str, "autoneg on"); + } else if (speed || duplex) { + str = g_string_sized_new (64); + g_string_printf (str, "autoneg off"); + } + if (speed) + g_string_append_printf (str, " speed %u", speed); + if (duplex) + g_string_append_printf (str, " duplex %s", duplex); + + wol = nm_setting_wired_get_wake_on_lan (s_wired); + wol_password = nm_setting_wired_get_wake_on_lan_password (s_wired); + + svSetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN", + wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE + ? "ignore" + : NULL); + if (!NM_IN_SET (wol, NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE, + NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)) { + if (!str) + str = g_string_sized_new (30); + else + g_string_append (str, " "); + + g_string_append (str, "wol "); + + if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) + g_string_append (str, "p"); + if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) + g_string_append (str, "u"); + if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) + g_string_append (str, "m"); + if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) + g_string_append (str, "b"); + if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) + g_string_append (str, "a"); + if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) + g_string_append (str, "g"); + + if (!NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ALL)) + g_string_append (str, "d"); + + if (wol_password && NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) + g_string_append_printf (str, "s sopass %s", wol_password); + } + } else + svUnsetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN"); + + if (s_ethtool) { + NMEthtoolID ethtool_id; + NMSettingConnection *s_con; + const char *iface = NULL; + + s_con = nm_connection_get_setting_connection (connection); + if (s_con) { + iface = nm_setting_connection_get_interface_name (s_con); + if ( iface + && ( !iface[0] + || !NM_STRCHAR_ALL (iface, ch, (ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') + || NM_IN_SET (ch, '_')))) + iface = NULL; + } - svSetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN", - wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE - ? "ignore" - : NULL); - if (!NM_IN_SET (wol, NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE, - NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)) { if (!str) str = g_string_sized_new (30); else - g_string_append (str, " "); + g_string_append (str, " ; "); + g_string_append (str, "-K "); + g_string_append (str, iface ?: "net0"); - g_string_append (str, "wol "); + for (ethtool_id = _NM_ETHTOOL_ID_FEATURE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST; ethtool_id++) { + const NMEthtoolData *ed = nm_ethtool_data[ethtool_id]; + NMTernary val; - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) - g_string_append (str, "p"); - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) - g_string_append (str, "u"); - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) - g_string_append (str, "m"); - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) - g_string_append (str, "b"); - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) - g_string_append (str, "a"); - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) - g_string_append (str, "g"); + nm_assert (nms_ifcfg_rh_utils_get_ethtool_name (ethtool_id)); - if (!NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ALL)) - g_string_append (str, "d"); + val = nm_setting_ethtool_get_feature (s_ethtool, ed->optname); + if (val == NM_TERNARY_DEFAULT) + continue; - if (wol_password && NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) - g_string_append_printf (str, "s sopass %s", wol_password); + g_string_append_c (str, ' '); + g_string_append (str, nms_ifcfg_rh_utils_get_ethtool_name (ethtool_id)); + g_string_append (str, val == NM_TERNARY_TRUE ? " on" : " off"); + } } if (str) { diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected new file mode 100644 index 0000000000..426085765c --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected @@ -0,0 +1,15 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +ETHTOOL_OPTS="autoneg on ; -K net0 rxvlan off tx on" +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wired Auto-Negotiate" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 67431c18a4..fc42937d0e 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -43,12 +43,14 @@ #include "nm-setting-pppoe.h" #include "nm-setting-ppp.h" #include "nm-setting-vpn.h" +#include "nm-setting-ethtool.h" #include "nm-setting-gsm.h" #include "nm-setting-cdma.h" #include "nm-setting-serial.h" #include "nm-setting-vlan.h" #include "nm-setting-dcb.h" #include "nm-core-internal.h" +#include "nm-ethtool-utils.h" #include "NetworkManagerUtils.h" @@ -3726,6 +3728,7 @@ test_write_wired_auto_negotiate_on (void) gs_unref_object NMConnection *connection = NULL; gs_unref_object NMConnection *reread = NULL; NMSettingWired *s_wired; + NMSettingEthtool *s_ethtool; char *val; shvarFile *f; @@ -3735,8 +3738,14 @@ test_write_wired_auto_negotiate_on (void) NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE, NULL); - _writer_new_connection (connection, + s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ()); + nm_setting_ethtool_set_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TX, NM_TERNARY_TRUE); + nm_setting_ethtool_set_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN, NM_TERNARY_FALSE); + nm_connection_add_setting (connection, NM_SETTING (s_ethtool)); + + _writer_new_connec_exp (connection, TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-test_write_wired_auto_negotiate_on.cexpected", &testfile); f = _svOpenFile (testfile); @@ -3750,7 +3759,15 @@ test_write_wired_auto_negotiate_on (void) reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_verifies_without_normalization (reread); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); + + s_ethtool = NM_SETTING_ETHTOOL (nm_connection_get_setting (reread, NM_TYPE_SETTING_ETHTOOL)); + g_assert (s_ethtool); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TX), ==, NM_TERNARY_TRUE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN), ==, NM_TERNARY_FALSE); + g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN), ==, NM_TERNARY_DEFAULT); } static void