]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: do not try to bring up wifi interface if rfkill is active 24020/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 14 Jul 2022 21:26:57 +0000 (06:26 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 23 Jul 2022 11:55:03 +0000 (20:55 +0900)
This fixes the following error:
---
systemd-networkd[465]: wlan0: Could not bring up interface: Operation not possible due to RF-kill
---

Fixes #23649.

src/network/networkd-setlink.c
src/network/networkd-wiphy.c
src/network/networkd-wiphy.h

index 97c68487172a930a78af5a46bbf43e139b94cd50..e2b5acc0864f9b10345e7a24dcd8f11249135be7 100644 (file)
@@ -14,6 +14,7 @@
 #include "networkd-manager.h"
 #include "networkd-queue.h"
 #include "networkd-setlink.h"
+#include "networkd-wiphy.h"
 
 static int get_link_default_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         return link_getlink_handler_internal(rtnl, m, link, "Failed to sync link information");
@@ -998,7 +999,7 @@ static int link_up_or_down(Link *link, bool up, Request *req) {
         return request_call_netlink_async(link->manager->rtnl, m, req);
 }
 
-static bool link_is_ready_to_activate(Link *link) {
+static bool link_is_ready_to_activate(Link *link, bool up) {
         assert(link);
 
         if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
@@ -1007,6 +1008,9 @@ static bool link_is_ready_to_activate(Link *link) {
         if (link->set_link_messages > 0)
                 return false;
 
+        if (up && link_rfkilled(link) > 0)
+                return false;
+
         return true;
 }
 
@@ -1017,7 +1021,7 @@ static int link_process_activation(Request *req, Link *link, void *userdata) {
         assert(req);
         assert(link);
 
-        if (!link_is_ready_to_activate(link))
+        if (!link_is_ready_to_activate(link, up))
                 return 0;
 
         r = link_up_or_down(link, up, req);
@@ -1097,6 +1101,9 @@ static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) {
         if (!link->activated)
                 return false;
 
+        if (up && link_rfkilled(link) > 0)
+                return false;
+
         return true;
 }
 
index 1c258e24169bae3e88de3b7b8a955df569a73597..38d1ceeba1eae016cd7ceb2f5cf0758d5c679471 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <net/if_arp.h>
 #include <linux/nl80211.h>
 
 #include "device-private.h"
@@ -103,6 +104,129 @@ int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) {
         return 0;
 }
 
+static int link_get_wiphy(Link *link, Wiphy **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *phy = NULL;
+        const char *s;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        if (link->iftype != ARPHRD_ETHER)
+                return -EOPNOTSUPP;
+
+        if (!link->dev)
+                return -EOPNOTSUPP;
+
+        r = sd_device_get_devtype(link->dev, &s);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(s, "wlan"))
+                return -EOPNOTSUPP;
+
+        r = sd_device_get_syspath(link->dev, &s);
+        if (r < 0)
+                return r;
+
+        s = strjoina(s, "/phy80211");
+        r = sd_device_new_from_syspath(&phy, s);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_sysname(phy, &s);
+        if (r < 0)
+                return r;
+
+        /* TODO:
+         * Maybe, it is better to cache the found Wiphy object in the Link object.
+         * To support that, we need to investigate what happens when the _phy_ is renamed. */
+
+        return wiphy_get_by_name(link->manager, s, ret);
+}
+
+static int rfkill_get_state(sd_device *dev) {
+        int r;
+
+        assert(dev);
+
+        /* The previous values may be outdated. Let's clear cache and re-read the values. */
+        device_clear_sysattr_cache(dev);
+
+        r = device_get_sysattr_bool(dev, "soft");
+        if (r < 0 && r != -ENOENT)
+                return r;
+        if (r > 0)
+                return RFKILL_SOFT;
+
+        r = device_get_sysattr_bool(dev, "hard");
+        if (r < 0 && r != -ENOENT)
+                return r;
+        if (r > 0)
+                return RFKILL_HARD;
+
+        return RFKILL_UNBLOCKED;
+}
+
+static int wiphy_rfkilled(Wiphy *w) {
+        int r;
+
+        assert(w);
+
+        if (!udev_available()) {
+                if (w->rfkill_state != RFKILL_UNBLOCKED) {
+                        log_wiphy_debug(w, "Running in container, assuming the radio transmitter is unblocked.");
+                        w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
+                }
+                return false;
+        }
+
+        if (!w->rfkill) {
+                if (w->rfkill_state != RFKILL_UNBLOCKED) {
+                        log_wiphy_debug(w, "No rfkill device found, assuming the radio transmitter is unblocked.");
+                        w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
+                }
+                return false;
+        }
+
+        r = rfkill_get_state(w->rfkill);
+        if (r < 0)
+                return log_wiphy_debug_errno(w, r, "Could not get rfkill state: %m");
+
+        if (w->rfkill_state != r)
+                switch (r) {
+                case RFKILL_UNBLOCKED:
+                        log_wiphy_debug(w, "The radio transmitter is unblocked.");
+                        break;
+                case RFKILL_SOFT:
+                        log_wiphy_debug(w, "The radio transmitter is turned off by software.");
+                        break;
+                case RFKILL_HARD:
+                        log_wiphy_debug(w, "The radio transmitter is forced off by something outside of the driver's control.");
+                        break;
+                default:
+                        assert_not_reached();
+                }
+
+        w->rfkill_state = r; /* Cache the state to suppress the above log messages. */
+        return r != RFKILL_UNBLOCKED;
+}
+
+int link_rfkilled(Link *link) {
+        Wiphy *w;
+        int r;
+
+        assert(link);
+
+        r = link_get_wiphy(link, &w);
+        if (IN_SET(r, -EOPNOTSUPP, -ENODEV))
+                return false; /* Typically, non-wifi interface or running in container */
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Could not get phy: %m");
+
+        return wiphy_rfkilled(w);
+}
+
 static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
         const char *name;
         int r;
index 5f0f47a2306db94e0fb3c7fa9a12fbc58fc62fee..b9056e802c7c29bd889d71c3c74edfacb1ed32f8 100644 (file)
@@ -7,8 +7,19 @@
 
 #include "macro.h"
 
+typedef struct Link Link;
 typedef struct Manager Manager;
 
+/* The following values are different from the ones defined in linux/rfkill.h. */
+typedef enum RFKillState {
+        RFKILL_UNKNOWN,
+        RFKILL_UNBLOCKED,
+        RFKILL_SOFT,
+        RFKILL_HARD,
+        _RFKILL_STATE_MAX,
+        _RFKILL_STATE_INVALID = -EINVAL,
+} RFKillState;
+
 typedef struct Wiphy {
         Manager *manager;
 
@@ -17,6 +28,7 @@ typedef struct Wiphy {
 
         sd_device *dev;
         sd_device *rfkill;
+        RFKillState rfkill_state;
 } Wiphy;
 
 Wiphy *wiphy_free(Wiphy *w);
@@ -25,6 +37,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Wiphy*, wiphy_free);
 int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret);
 int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret);
 
+int link_rfkilled(Link *link);
+
 int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager);
 int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action);
 int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action);