]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: make IgnoreCarrierLoss= also take timespan 21344/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 13 Nov 2021 01:50:03 +0000 (10:50 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 15 Nov 2021 17:04:59 +0000 (02:04 +0900)
Fixes #18738 and #20887.

Replaces #18746.

man/systemd.network.xml
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-wifi.c

index ab08797a9eed0abc8cd74f539d6426f91d782cc5..c701eff0579f371a01d7112b4557f666a5fcd48b 100644 (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>
 
index 62c06af1221839f7228f9bb04ad25d856243377d..5354b73a69f423d0be692cbc9e092e0f202351bb 100644 (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;
-        }
-
-        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) {
index d02c4fbd1e324a0e7a7a2de23a07cb07c81bba54..263174b316c577377d8bf26a3cc84e5a45fd06f8 100644 (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;
index 515a5b5bd951980d126b3422f5239cd2bcf958dd..b6f0d201127df5ba277168fcc4461cd7e2b4d3ff 100644 (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)
index a41bc25cca8168e4d2eda73f3a753bd26f9e676e..bebdb5611260ad50e84530ac74a06e5356a283b8 100644 (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");
 
index c86a492a17285f1b7c8fe466f592bfd9b24f4dc9..0fe1136bf153f1d5d36339358e5b614b07716fdd 100644 (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);
 
index 1475218286031c0a164bd02583bf03b87771d2a1..f1d5c7d8d40dd7a744c05e925011e985e9cadaee 100644 (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: