summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-07-16 23:37:55 +0200
committerThomas Haller <thaller@redhat.com>2018-08-10 10:38:19 +0200
commitdf30651b8906cfe6a5cb7aef01a220d1f21b80f3 (patch)
tree6642ae4c01d7c8b5e0d8275a06f676adef4b34e9
parent4e0f1b16b9472318b2e96b7357ce97ab58d7873f (diff)
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.
-rw-r--r--Makefile.am8
-rw-r--r--clients/cli/connections.c3
-rw-r--r--clients/common/meson.build2
-rw-r--r--clients/common/nm-meta-setting-desc.c126
-rw-r--r--clients/common/nm-meta-setting-desc.h4
-rw-r--r--docs/libnm/libnm-docs.xml1
-rw-r--r--libnm-core/meson.build2
-rw-r--r--libnm-core/nm-core-internal.h5
-rw-r--r--libnm-core/nm-core-types.h1
-rw-r--r--libnm-core/nm-setting-ethtool.c342
-rw-r--r--libnm-core/nm-setting-ethtool.h83
-rw-r--r--libnm-core/nm-setting.c14
-rw-r--r--libnm-core/tests/test-general.c20
-rw-r--r--libnm-core/tests/test-setting.c77
-rw-r--r--libnm/NetworkManager.h1
-rw-r--r--libnm/libnm.ver5
-rw-r--r--libnm/nm-autoptr.h1
-rw-r--r--po/POTFILES.in1
-rw-r--r--shared/meson.build2
-rw-r--r--shared/nm-ethtool-utils.c143
-rw-r--r--shared/nm-ethtool-utils.h79
-rw-r--r--shared/nm-meta-setting.c7
-rw-r--r--shared/nm-meta-setting.h1
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c139
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c60
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h17
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c154
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c19
29 files changed, 1241 insertions, 91 deletions
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())]]></programlisting></in
<xi:include href="xml/nm-setting-cdma.xml"/>
<xi:include href="xml/nm-setting-dcb.xml"/>
<xi:include href="xml/nm-setting-dummy.xml"/>
+ <xi:include href="xml/nm-setting-ethtool.xml"/>
<xi:include href="xml/nm-setting-generic.xml"/>
<xi:include href="xml/nm-setting-gsm.xml"/>
<xi:include href="xml/nm-setting-infiniband.xml"/>
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 <NetworkManager.h> 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"
@@ -1265,6 +1267,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)
{
NMSriovVF *vf1, *vf2;
@@ -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", &ethtool_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;
-
- 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);
+ else {
+ gs_free const char **opts = NULL;
+ const char *const *iter;
+
+ 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);
+ 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;
+ }
- /* 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);
+ g_string_append (str, " ; ");
+ g_string_append (str, "-K ");
+ g_string_append (str, iface ?: "net0");
+
+ 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;
+
+ nm_assert (nms_ifcfg_rh_utils_get_ethtool_name (ethtool_id));
+
+ val = nm_setting_ethtool_get_feature (s_ethtool, ed->optname);
+ if (val == NM_TERNARY_DEFAULT)
+ continue;
+
+ 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