From a9d2d037ee108327f7ac928139a0feb9a990c546 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Jul 2022 06:26:57 +0900 Subject: [PATCH] network: do not try to bring up wifi interface if rfkill is active 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 | 11 ++- src/network/networkd-wiphy.c | 124 +++++++++++++++++++++++++++++++++ src/network/networkd-wiphy.h | 14 ++++ 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 97c68487172..e2b5acc0864 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -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; } diff --git a/src/network/networkd-wiphy.c b/src/network/networkd-wiphy.c index 1c258e24169..38d1ceeba1e 100644 --- a/src/network/networkd-wiphy.c +++ b/src/network/networkd-wiphy.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #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; diff --git a/src/network/networkd-wiphy.h b/src/network/networkd-wiphy.h index 5f0f47a2306..b9056e802c7 100644 --- a/src/network/networkd-wiphy.h +++ b/src/network/networkd-wiphy.h @@ -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); -- 2.47.3