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.
This commit is contained in:
Yu Watanabe
2022-07-23 20:55:03 +09:00
parent 8642e04b78
commit a9d2d037ee
3 changed files with 147 additions and 2 deletions
+9 -2
View 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;
}
+124
View 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;
+14
View 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);