network: make IgnoreCarrierLoss= also take timespan

Fixes #18738 and #20887.

Replaces #18746.
This commit is contained in:
Yu Watanabe 2021-11-13 10:50:03 +09:00
parent b7ac128430
commit 6706ce2fd2
7 changed files with 145 additions and 24 deletions

View file

@ -952,18 +952,21 @@ Table=1234</programlisting></para>
<term><varname>ConfigureWithoutCarrier=</varname></term>
<listitem>
<para>Takes a boolean. Allows networkd to configure a specific link even if it has no carrier.
Defaults to false. If <option>IgnoreCarrierLoss=</option> is not explicitly set, it will
default to this value.
Defaults to false. If enabled, and the <varname>IgnoreCarrierLoss=</varname> setting is not
explicitly set, then it is enabled as well.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IgnoreCarrierLoss=</varname></term>
<listitem>
<para>Takes a boolean. Allows networkd to retain both the static and dynamic configuration
of the interface even if its carrier is lost. When unset, the value specified with
<option>ConfigureWithoutCarrier=</option> is used.
</para>
<para>Takes a boolean or a timespan. When true, networkd retains both the static and dynamic
configuration of the interface even if its carrier is lost. When a timespan is specified,
networkd waits for the specified timespan, and ignores the carrier loss if the link regain
its carrier within the timespan. Setting a finite timespan may be useful for a wireless
interface connecting to a network which has multiple access points with the same SSID, or an
interface which is reset on changing MTU. When unset, the value specified with
<varname>ConfigureWithoutCarrier=</varname> is used.</para>
<para>When <varname>ActivationPolicy=</varname> is set to <literal>always-up</literal>, this
is forced to <literal>true</literal>.
@ -1805,6 +1808,9 @@ Table=1234</programlisting></para>
<para>When true, the interface maximum transmission unit from the DHCP server will be used on the
current link. If <varname>MTUBytes=</varname> is set, then this setting is ignored. Defaults to
false.</para>
<para>Note, some drivers will reset the interfaces if the MTU is changed. For such
interfaces, please try to use <varname>IgnoreCarrierLoss=</varname> with a short timespan,
e.g. <literal>3 seconds</literal>.</para>
</listitem>
</varlistentry>

View file

@ -20,6 +20,7 @@
#include "dhcp-lease-internal.h"
#include "env-file.h"
#include "ethtool-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@ -243,6 +244,7 @@ static Link *link_free(Link *link) {
strv_free(link->alternative_names);
free(link->kind);
free(link->ssid);
free(link->previous_ssid);
free(link->driver);
unlink_and_free(link->lease_file);
@ -258,6 +260,8 @@ static Link *link_free(Link *link) {
network_unref(link->network);
sd_event_source_disable_unref(link->carrier_lost_timer);
return mfree(link);
}
@ -1580,16 +1584,30 @@ int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, voi
}
static int link_carrier_gained(Link *link) {
bool force_reconfigure;
int r;
assert(link);
r = event_source_disable(link->carrier_lost_timer);
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable carrier lost timer, ignoring: %m");
/* If the SSID is changed, then the connected wireless network could be changed. So, always
* reconfigure the link. Which means e.g. the DHCP client will be restarted, and the correct
* network information will be gained.
* For non-wireless interfaces, we have no way to detect the connected network change. So,
* setting force_reconfigure = false. Note, both ssid and previous_ssid should be NULL for
* non-wireless interfaces, and streq_ptr() returns true. */
force_reconfigure = !streq_ptr(link->previous_ssid, link->ssid);
link->previous_ssid = mfree(link->previous_ssid);
if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
/* At this stage, both wlan and link information should be up-to-date. Hence,
* it is not necessary to call RTM_GETLINK, NL80211_CMD_GET_INTERFACE, or
* NL80211_CMD_GET_STATION commands, and simply call link_reconfigure_impl().
* Note, link_reconfigure_impl() returns 1 when the link is reconfigured. */
r = link_reconfigure_impl(link, /* force = */ false);
r = link_reconfigure_impl(link, force_reconfigure);
if (r != 0)
return r;
}
@ -1615,6 +1633,45 @@ static int link_carrier_gained(Link *link) {
return 0;
}
static int link_carrier_lost_impl(Link *link) {
int r, ret = 0;
assert(link);
link->previous_ssid = mfree(link->previous_ssid);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0;
if (!link->network)
return 0;
r = link_stop_engines(link, false);
if (r < 0)
ret = r;
r = link_drop_config(link);
if (r < 0 && ret >= 0)
ret = r;
return ret;
}
static int link_carrier_lost_handler(sd_event_source *s, uint64_t usec, void *userdata) {
Link *link = userdata;
int r;
assert(link);
r = link_carrier_lost_impl(link);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to process carrier lost event: %m");
link_enter_failed(link);
}
return 0;
}
static int link_carrier_lost(Link *link) {
int r;
@ -1631,16 +1688,22 @@ static int link_carrier_lost(Link *link) {
if (!link->network)
return 0;
if (link->network->ignore_carrier_loss)
if (link->network->ignore_carrier_loss_usec == USEC_INFINITY)
return 0;
r = link_stop_engines(link, false);
if (r < 0) {
link_enter_failed(link);
return r;
}
if (link->network->ignore_carrier_loss_usec == 0)
return link_carrier_lost_impl(link);
return link_drop_config(link);
return event_reset_time_relative(link->manager->event,
&link->carrier_lost_timer,
clock_boottime_or_monotonic(),
link->network->ignore_carrier_loss_usec,
0,
link_carrier_lost_handler,
link,
0,
"link-carrier-loss",
true);
}
static int link_admin_state_up(Link *link) {

View file

@ -67,11 +67,14 @@ typedef struct Link {
/* wlan */
enum nl80211_iftype wlan_iftype;
char *ssid;
char *previous_ssid;
struct ether_addr bssid;
unsigned flags;
uint8_t kernel_operstate;
sd_event_source *carrier_lost_timer;
Network *network;
LinkState state;

View file

@ -135,7 +135,7 @@ Network.ProxyARP, config_parse_tristate,
Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier)
Network.IgnoreCarrierLoss, config_parse_tristate, 0, offsetof(Network, ignore_carrier_loss)
Network.IgnoreCarrierLoss, config_parse_ignore_carrier_loss, 0, 0
Network.KeepConfiguration, config_parse_keep_configuration, 0, offsetof(Network, keep_configuration)
Network.IPv6SendRA, config_parse_router_prefix_delegation, 0, offsetof(Network, router_prefix_delegation)
Network.DHCPv6PrefixDelegation, config_parse_tristate, 0, offsetof(Network, dhcp6_pd)

View file

@ -271,14 +271,17 @@ int network_verify(Network *network) {
network->activation_policy = ACTIVATION_POLICY_UP;
if (network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
if (network->ignore_carrier_loss == false)
log_warning("%s: IgnoreCarrierLoss=false conflicts with ActivationPolicy=always-up. "
"Setting IgnoreCarrierLoss=true.", network->filename);
network->ignore_carrier_loss = true;
if (network->ignore_carrier_loss_set && network->ignore_carrier_loss_usec < USEC_INFINITY)
log_warning("%s: IgnoreCarrierLoss=no or finite timespan conflicts with ActivationPolicy=always-up. "
"Setting IgnoreCarrierLoss=yes.", network->filename);
network->ignore_carrier_loss_set = true;
network->ignore_carrier_loss_usec = USEC_INFINITY;
}
if (network->ignore_carrier_loss < 0)
network->ignore_carrier_loss = network->configure_without_carrier;
if (!network->ignore_carrier_loss_set) {
network->ignore_carrier_loss_set = true;
network->ignore_carrier_loss_usec = network->configure_without_carrier ? USEC_INFINITY : 0;
}
if (IN_SET(network->activation_policy, ACTIVATION_POLICY_DOWN, ACTIVATION_POLICY_ALWAYS_DOWN, ACTIVATION_POLICY_MANUAL)) {
if (network->required_for_online < 0 ||
@ -379,7 +382,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.allmulticast = -1,
.promiscuous = -1,
.ignore_carrier_loss = -1,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
.dhcp_duid.type = _DUID_TYPE_INVALID,
@ -1279,6 +1281,51 @@ int config_parse_link_group(
return 0;
}
int config_parse_ignore_carrier_loss(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *network = userdata;
usec_t usec;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
if (isempty(rvalue)) {
network->ignore_carrier_loss_set = false;
return 0;
}
r = parse_boolean(rvalue);
if (r >= 0) {
network->ignore_carrier_loss_set = true;
network->ignore_carrier_loss_usec = r > 0 ? USEC_INFINITY : 0;
return 0;
}
r = parse_sec(rvalue, &usec);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
network->ignore_carrier_loss_set = true;
network->ignore_carrier_loss_usec = usec;
return 0;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
"Failed to parse RequiredFamilyForOnline= setting");

View file

@ -110,7 +110,8 @@ struct Network {
/* misc settings */
bool configure_without_carrier;
int ignore_carrier_loss;
bool ignore_carrier_loss_set;
usec_t ignore_carrier_loss_usec; /* timespan */
KeepConfiguration keep_configuration;
char **bind_carrier;
bool default_route_on_device;
@ -384,6 +385,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_link_group);
CONFIG_PARSER_PROTOTYPE(config_parse_ignore_carrier_loss);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);

View file

@ -273,7 +273,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess
strna(nl80211_cmd_to_string(cmd)), cmd);
link->bssid = ETHER_ADDR_NULL;
link->ssid = mfree(link->ssid);
free_and_replace(link->previous_ssid, link->ssid);
break;
default: