summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2021-11-16 02:27:01 +0100
committerGitHub <noreply@github.com>2021-11-16 02:27:01 +0100
commit7777024dab402b780472daf1ceb6afb3460b388d (patch)
treef08cd702577d8962e25fbe8f7a6d3699da16656f
parentMerge pull request #21235 from bacher09/slava/supress_ifgroup (diff)
parentnetwork: make IgnoreCarrierLoss= also take timespan (diff)
downloadsystemd-7777024dab402b780472daf1ceb6afb3460b388d.tar.xz
systemd-7777024dab402b780472daf1ceb6afb3460b388d.zip
Merge pull request #21344 from yuwata/network-ignore-carrier-loss-timespan
network: make IgnoreCarrierLoss= also take a timespan
-rw-r--r--man/systemd.network.xml18
-rw-r--r--src/libsystemd/sd-event/event-util.c24
-rw-r--r--src/libsystemd/sd-event/event-util.h26
-rw-r--r--src/network/networkd-link.c96
-rw-r--r--src/network/networkd-link.h3
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c61
-rw-r--r--src/network/networkd-network.h4
-rw-r--r--src/network/networkd-wifi.c2
9 files changed, 200 insertions, 36 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 0fbefa6e76..a8fff46fe2 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -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>.
@@ -1813,6 +1816,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>
diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c
index 132796fc6c..0e53406a94 100644
--- a/src/libsystemd/sd-event/event-util.c
+++ b/src/libsystemd/sd-event/event-util.c
@@ -84,6 +84,30 @@ int event_reset_time(
return created;
}
+int event_reset_time_relative(
+ sd_event *e,
+ sd_event_source **s,
+ clockid_t clock,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ int64_t priority,
+ const char *description,
+ bool force_reset) {
+
+ usec_t usec_now;
+ int r;
+
+ assert(e);
+
+ r = sd_event_now(e, clock, &usec_now);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
+
+ return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
+}
+
int event_source_disable(sd_event_source *s) {
if (!s)
return 0;
diff --git a/src/libsystemd/sd-event/event-util.h b/src/libsystemd/sd-event/event-util.h
index c8f97bc8d6..64a4199244 100644
--- a/src/libsystemd/sd-event/event-util.h
+++ b/src/libsystemd/sd-event/event-util.h
@@ -5,9 +5,27 @@
#include "sd-event.h"
-int event_reset_time(sd_event *e, sd_event_source **s,
- clockid_t clock, uint64_t usec, uint64_t accuracy,
- sd_event_time_handler_t callback, void *userdata,
- int64_t priority, const char *description, bool force_reset);
+int event_reset_time(
+ sd_event *e,
+ sd_event_source **s,
+ clockid_t clock,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ int64_t priority,
+ const char *description,
+ bool force_reset);
+int event_reset_time_relative(
+ sd_event *e,
+ sd_event_source **s,
+ clockid_t clock,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ int64_t priority,
+ const char *description,
+ bool force_reset);
int event_source_disable(sd_event_source *s);
int event_source_is_enabled(sd_event_source *s);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index f03e5c6dbf..e0e7cd8cd7 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -21,6 +21,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"
@@ -244,6 +245,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);
@@ -259,6 +261,8 @@ static Link *link_free(Link *link) {
network_unref(link->network);
+ sd_event_source_disable_unref(link->carrier_lost_timer);
+
return mfree(link);
}
@@ -1581,10 +1585,34 @@ 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_reconfigure);
+ if (r != 0)
+ return r;
+ }
+
r = link_handle_bound_by_list(link);
if (r < 0)
return r;
@@ -1606,6 +1634,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;
@@ -1622,16 +1689,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) {
@@ -1958,15 +2031,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
if (!had_carrier && link_has_carrier(link)) {
log_link_info(link, "Gained carrier");
- 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(). */
- r = link_reconfigure_impl(link, /* force = */ false);
- if (r < 0)
- return r;
- }
-
r = link_carrier_gained(link);
if (r < 0)
return r;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 7006f3df65..edf93d720e 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -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;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 525d0158bc..2fdfb16689 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -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)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 90da1670f0..82094a49b1 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -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,
@@ -1285,6 +1287,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");
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index fb79ee8509..626b7710f0 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -109,7 +109,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;
@@ -383,6 +384,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);
diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c
index 1475218286..f1d5c7d8d4 100644
--- a/src/network/networkd-wifi.c
+++ b/src/network/networkd-wifi.c
@@ -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: