libnm,core: fix handling miimon and arp_interval as conflicting kernel options

We use sysfs API for setting bond options. Note that the miimon and
arp_interval settings conflict each other, and whenever setting one
of these sysfs values, the other one gets reset. That means,
NetworkManager needs to mediate and handle a profile which has both
these options set.

Before 1.24, the libnm API nm_setting_bond_add_option() API would mangle
the content of the bond settings, to clear the respective other fields
when setting miimon/arp_interval. That also had the effect that the
settings plugins, weren't able to read such (conflicting) settings
back from disk (but they would write them to disk). If a keyfile
specified both miimon and arp_interval keys, then it would depend on
their order in the keyfile which wins.
It is wrong that a libnm property setter mangles the option in such a way,
especially, because you still could set the NM_SETTING_BOND_OPTIONS
property directly and bypass this. So, since 1.24, you can create
profiles that have conflicting options.

Also, we can now not start to reject such settings as invalid, because that
would be an API break. Instead, just make sure that when one of the
settings is set, that the other one consistently gets deactivated.

Also, before 1.24 already, NMDeviceBond would mediate whether to either set
miimon or arp_interval settings. Despite that the keyfile reader would
mangle the settings, it would also prefer miimon over arp_interval,
if both were set.

This mechanism was broken since we switch to _bond_get_option_normalized()
for that. As a consequence, NetworkManager would try to set both the
conflicting options. Fix that.
This commit is contained in:
Thomas Haller
2020-06-30 21:17:08 +02:00
parent 1543f8a1a1
commit 4aa46328ca

View File

@@ -251,9 +251,7 @@ _bond_get_option_normalized (NMSettingBond* self,
const char* option, const char* option,
gboolean get_default_only) gboolean get_default_only)
{ {
const char *arp_interval_str;
const char *mode_str; const char *mode_str;
gint64 arp_interval;
NMBondMode mode; NMBondMode mode;
const char *value = NULL; const char *value = NULL;
@@ -262,6 +260,7 @@ _bond_get_option_normalized (NMSettingBond* self,
mode_str = _bond_get_option_or_default (self, NM_SETTING_BOND_OPTION_MODE); mode_str = _bond_get_option_or_default (self, NM_SETTING_BOND_OPTION_MODE);
mode = _nm_setting_bond_mode_from_string (mode_str); mode = _nm_setting_bond_mode_from_string (mode_str);
if (mode == NM_BOND_MODE_UNKNOWN) { if (mode == NM_BOND_MODE_UNKNOWN) {
/* the mode is unknown, consequently, there is no normalized/default /* the mode is unknown, consequently, there is no normalized/default
* value either. */ * value either. */
@@ -274,46 +273,44 @@ _bond_get_option_normalized (NMSettingBond* self,
/* Apply custom NetworkManager policies here */ /* Apply custom NetworkManager policies here */
if (!get_default_only) { if (!get_default_only) {
if (NM_IN_STRSET (option, if (NM_IN_STRSET (option,
NM_SETTING_BOND_OPTION_UPDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL,
NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) {
NM_SETTING_BOND_OPTION_MIIMON)) { int miimon;
/* if arp_interval is explicitly set and miimon is not, then disable miimon /* if arp_interval is explicitly set and miimon is not, then disable miimon
* (and related updelay and downdelay) as recommended by the kernel docs */ * (and related updelay and downdelay) as recommended by the kernel docs */
arp_interval_str = _bond_get_option (self, NM_SETTING_BOND_OPTION_ARP_INTERVAL); miimon = _nm_utils_ascii_str_to_int64 (_bond_get_option (self, NM_SETTING_BOND_OPTION_MIIMON),
arp_interval = _nm_utils_ascii_str_to_int64 (arp_interval_str, 10, 0, G_MAXINT, 0); 10, 0, G_MAXINT, 0);
if (miimon != 0) {
if (!arp_interval || _bond_get_option (self, NM_SETTING_BOND_OPTION_MIIMON)) { /* miimon is enabled. arp_interval values are unset. */
value = _bond_get_option (self, option); if (nm_streq (option, NM_SETTING_BOND_OPTION_ARP_INTERVAL))
} else { return "0";
return NULL; return "";
} }
value = _bond_get_option (self, option);
} else if (NM_IN_STRSET (option, } else if (NM_IN_STRSET (option,
NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP,
NM_SETTING_BOND_OPTION_NUM_UNSOL_NA)) { NM_SETTING_BOND_OPTION_NUM_UNSOL_NA)) {
/* just get one of the 2, at kernel level they're the same bond option */ /* just get one of the 2, at kernel level they're the same bond option */
value = _bond_get_option (self, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP); value = _bond_get_option (self, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
if (!value) { if (!value)
value = _bond_get_option (self, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA); value = _bond_get_option (self, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA);
} } else
} else {
value = _bond_get_option (self, option); value = _bond_get_option (self, option);
}
if (value)
return value;
} }
if (!value) { /* Apply rules that change the default value of an option */
/* Apply rules that change the default value of an option */ if (nm_streq (option, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)) {
if (nm_streq (option, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)) { /* The default value depends on the current mode */
/* The default value depends on the current mode */ if (mode == NM_BOND_MODE_8023AD)
if (NM_IN_STRSET (mode_str, "4", "802.3ad")) return "00:00:00:00:00:00";
return "00:00:00:00:00:00"; return "";
else
return "";
} else {
return _bond_get_option_or_default (self, option);
}
} }
return value; return _bond_get_option_or_default (self, option);
} }
const char* const char*
@@ -686,9 +683,8 @@ nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name)
{ {
g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL);
if (!name) { if (!name)
return NULL; return NULL;
}
return _bond_get_option_normalized (setting, return _bond_get_option_normalized (setting,
name, name,