Fixes #18738 and #20887.
Replaces #18746.
<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>.
<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>
#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"
strv_free(link->alternative_names);
free(link->kind);
free(link->ssid);
+ free(link->previous_ssid);
free(link->driver);
unlink_and_free(link->lease_file);
network_unref(link->network);
+ sd_event_source_disable_unref(link->carrier_lost_timer);
+
return mfree(link);
}
}
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;
}
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;
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;
- }
-
- return link_drop_config(link);
+ if (link->network->ignore_carrier_loss_usec == 0)
+ return link_carrier_lost_impl(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) {
/* 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;
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)
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 ||
.allmulticast = -1,
.promiscuous = -1,
- .ignore_carrier_loss = -1,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
.dhcp_duid.type = _DUID_TYPE_INVALID,
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");
/* 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;
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);
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: