From 96f5f9ef9a1ba5146d3357c1548fb675d3bd5b68 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 21 Sep 2021 04:57:43 +0900 Subject: [PATCH] network: receive genl multicast messages about wlan connections --- src/network/networkd-link.c | 51 ++---- src/network/networkd-manager.c | 67 ++++++++ src/network/networkd-wifi.c | 279 +++++++++++++++++++++++++++++---- src/network/networkd-wifi.h | 7 +- 4 files changed, 332 insertions(+), 72 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 327815def51..43428182e6d 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -53,7 +53,6 @@ #include "networkd-sriov.h" #include "networkd-state-file.h" #include "networkd-sysctl.h" -#include "networkd-wifi.h" #include "set.h" #include "socket-util.h" #include "stat-util.h" @@ -1295,28 +1294,15 @@ static int link_reconfigure_impl(Link *link, bool force) { return 1; } -static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force, bool update_wifi) { - bool link_was_lower_up; +static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) { int r; assert(link); - link_was_lower_up = link->flags & IFF_LOWER_UP; - r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state"); if (r <= 0) return r; - if (update_wifi && link_was_lower_up && link->flags & IFF_LOWER_UP) { - /* If the interface's L1 was not up, then wifi_get_info() is already called in - * link_update_flags(). So, it is not necessary to re-call here. */ - r = wifi_get_info(link); - if (r < 0) { - link_enter_failed(link); - return 0; - } - } - r = link_reconfigure_impl(link, force); if (r < 0) { link_enter_failed(link); @@ -1327,11 +1313,11 @@ static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_messag } static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ false); + return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false); } static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true, /* update_wifi = */ false); + return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true); } static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1339,7 +1325,7 @@ static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_mes assert(link); - r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ true); + r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false); if (r != 0) return r; @@ -1421,10 +1407,6 @@ static int link_initialized_and_synced(Link *link) { return r; if (!link->network) { - r = wifi_get_info(link); - if (r < 0) - return r; - r = link_get_network(link, &network); if (r == -ENOENT) { link_set_state(link, LINK_STATE_UNMANAGED); @@ -1893,7 +1875,7 @@ void link_update_operstate(Link *link, bool also_update_master) { : "") static int link_update_flags(Link *link, sd_netlink_message *message) { - bool link_was_lower_up, link_was_admin_up, had_carrier; + bool link_was_admin_up, had_carrier; uint8_t operstate; unsigned flags; int r; @@ -1955,7 +1937,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) { log_link_debug(link, "Unknown link flags lost, ignoring: %#.5x", unknown_flags_removed); } - link_was_lower_up = link->flags & IFF_LOWER_UP; link_was_admin_up = link->flags & IFF_UP; had_carrier = link_has_carrier(link); @@ -1964,19 +1945,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) { link_update_operstate(link, true); - if (!link_was_lower_up && (link->flags & IFF_LOWER_UP)) { - r = wifi_get_info(link); - if (r < 0) - return r; - if (r > 0) { - /* All link information is up-to-date. So, it is not necessary to call - * RTM_GETLINK netlink method again. */ - r = link_reconfigure_impl(link, /* force = */ false); - if (r < 0) - return r; - } - } - if (!link_was_admin_up && (link->flags & IFF_UP)) { log_link_info(link, "Link UP"); @@ -1994,6 +1962,15 @@ 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-manager.c b/src/network/networkd-manager.c index 0f21a427eea..f9cfb645128 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "sd-daemon.h" #include "sd-netlink.h" @@ -38,6 +39,7 @@ #include "networkd-routing-policy-rule.h" #include "networkd-speed-meter.h" #include "networkd-state-file.h" +#include "networkd-wifi.h" #include "ordered-set.h" #include "path-lookup.h" #include "path-util.h" @@ -246,6 +248,16 @@ static int manager_connect_genl(Manager *m) { if (r < 0) return r; + r = genl_add_match(m->genl, NULL, NL80211_GENL_NAME, NL80211_MULTICAST_GROUP_CONFIG, 0, + &manager_genl_process_nl80211_config, NULL, m, "network-genl_process_nl80211_config"); + if (r < 0 && r != -EOPNOTSUPP) + return r; + + r = genl_add_match(m->genl, NULL, NL80211_GENL_NAME, NL80211_MULTICAST_GROUP_MLME, 0, + &manager_genl_process_nl80211_mlme, NULL, m, "network-genl_process_nl80211_mlme"); + if (r < 0 && r != -EOPNOTSUPP) + return r; + return 0; } @@ -690,6 +702,49 @@ static int manager_enumerate_nexthop(Manager *m) { return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_nexthop); } +static int manager_enumerate_nl80211_config(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(m); + assert(m->genl); + + r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_config); +} + +static int manager_enumerate_nl80211_mlme(Manager *m) { + Link *link; + int r; + + assert(m); + assert(m->genl); + + HASHMAP_FOREACH(link, m->links_by_index) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + + if (link->wlan_iftype != NL80211_IFTYPE_STATION) + continue; + + r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &req); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex); + if (r < 0) + return r; + + r = manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_mlme); + if (r < 0) + return r; + } + + return 0; +} + int manager_enumerate(Manager *m) { int r; @@ -724,6 +779,18 @@ int manager_enumerate(Manager *m) { else if (r < 0) return log_error_errno(r, "Could not enumerate routing policy rules: %m"); + r = manager_enumerate_nl80211_config(m); + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Could not enumerate wireless LAN interfaces, ignoring: %m"); + else if (r < 0) + return log_error_errno(r, "Could not enumerate wireless LAN interfaces: %m"); + + r = manager_enumerate_nl80211_mlme(m); + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Could not enumerate wireless LAN stations, ignoring: %m"); + else if (r < 0) + return log_error_errno(r, "Could not enumerate wireless LAN stations: %m"); + return 0; } diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c index 37700afb812..14752182860 100644 --- a/src/network/networkd-wifi.c +++ b/src/network/networkd-wifi.c @@ -3,11 +3,7 @@ #include #include -#include "sd-bus.h" - -#include "bus-util.h" #include "ether-addr-util.h" -#include "netlink-internal.h" #include "netlink-util.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -15,56 +11,275 @@ #include "string-util.h" #include "wifi-util.h" -int wifi_get_info(Link *link) { - _cleanup_free_ char *ssid = NULL; - enum nl80211_iftype iftype; - bool updated = false; - const char *type; +static int link_get_wlan_interface(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; int r; assert(link); - if (!link->sd_device) + r = sd_genl_message_new(link->manager->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to create generic netlink message: %m"); + + r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex); + if (r < 0) + return log_link_debug_errno(link, r, "Could not append NL80211_ATTR_IFINDEX attribute: %m"); + + r = sd_netlink_call(link->manager->genl, req, 0, &reply); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to request information about wlan interface: %m"); + if (!reply) { + log_link_debug(link, "No reply received to request for information about wifi interface, ignoring."); return 0; + } + + return manager_genl_process_nl80211_config(link->manager->genl, reply, link->manager); +} + +int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *message, Manager *manager) { + _cleanup_free_ char *ssid = NULL; + uint32_t ifindex, wlan_iftype; + const char *family, *ifname; + uint8_t cmd; + size_t len; + Link *link; + int r; + + assert(genl); + assert(message); + assert(manager); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "nl80211: received error message, ignoring"); - r = sd_device_get_devtype(link->sd_device, &type); - if (r == -ENOENT) return 0; - else if (r < 0) - return r; + } - if (!streq(type, "wlan")) + r = sd_genl_message_get_family_name(genl, message, &family); + if (r < 0) { + log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m"); return 0; + } + if (!streq(family, NL80211_GENL_NAME)) { + log_debug("nl80211: received message of unexpected genl family '%s', ignoring.", family); + return 0; + } - r = wifi_get_interface(link->manager->genl, link->ifindex, &iftype, &ssid); - if (r < 0) - return r; - if (r == 0) - iftype = link->wlan_iftype; /* Assume iftype is not changed. */ + r = sd_genl_message_get_command(genl, message, &cmd); + if (r < 0) { + log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m"); + return 0; + } + if (!IN_SET(cmd, NL80211_CMD_SET_INTERFACE, NL80211_CMD_NEW_INTERFACE, NL80211_CMD_DEL_INTERFACE)) { + log_debug("nl80211: ignoring nl80211 %s(%u) message.", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } - if (iftype == NL80211_IFTYPE_STATION) { - struct ether_addr bssid; + r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = link_get_by_index(manager, ifindex, &link); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.", + strna(nl80211_cmd_to_string(cmd)), cmd, ifindex); + return 0; + } + + r = sd_netlink_message_read_string(message, NL80211_ATTR_IFNAME, &ifname); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid interface name, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + if (!streq(ifname, link->ifname)) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message with invalid interface name '%s', ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd, ifname); + return 0; + } + + r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFTYPE, &wlan_iftype); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid wlan interface type, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = sd_netlink_message_read_data_suffix0(message, NL80211_ATTR_SSID, &len, (void**) &ssid); + if (r < 0 && r != -ENODATA) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid SSID, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + if (r >= 0) { + if (len == 0) { + log_link_debug(link, "nl80211: received SSID has zero length, ignoring the received SSID: %m"); + ssid = mfree(ssid); + } else if (strlen_ptr(ssid) != len) { + log_link_debug(link, "nl80211: received SSID contains NUL character(s), ignoring the received SSID."); + ssid = mfree(ssid); + } + } - r = wifi_get_station(link->manager->genl, link->ifindex, &bssid); + log_link_debug(link, "nl80211: received %s(%u) message: iftype=%s, ssid=%s", + strna(nl80211_cmd_to_string(cmd)), cmd, + strna(nl80211_iftype_to_string(wlan_iftype)), ssid); + + switch(cmd) { + case NL80211_CMD_SET_INTERFACE: + case NL80211_CMD_NEW_INTERFACE: + link->wlan_iftype = wlan_iftype; + free_and_replace(link->ssid, ssid); + break; + + case NL80211_CMD_DEL_INTERFACE: + link->wlan_iftype = NL80211_IFTYPE_UNSPECIFIED; + link->ssid = mfree(link->ssid); + break; + + default: + assert_not_reached(); + } + + return 0; +} + +int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager) { + const char *family; + uint32_t ifindex; + uint8_t cmd; + Link *link; + int r; + + assert(genl); + assert(message); + assert(manager); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); if (r < 0) - return r; + log_message_warning_errno(message, r, "nl80211: received error message, ignoring"); + + return 0; + } + + r = sd_genl_message_get_family_name(genl, message, &family); + if (r < 0) { + log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m"); + return 0; + } + if (!streq(family, NL80211_GENL_NAME)) { + log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family); + return 0; + } + + r = sd_genl_message_get_command(genl, message, &cmd); + if (r < 0) { + log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = link_get_by_index(manager, ifindex, &link); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.", + strna(nl80211_cmd_to_string(cmd)), cmd, ifindex); + return 0; + } + + switch(cmd) { + case NL80211_CMD_NEW_STATION: + case NL80211_CMD_DEL_STATION: { + struct ether_addr bssid; + + r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + log_link_debug(link, "nl80211: received %s(%u) message: bssid=%s", + strna(nl80211_cmd_to_string(cmd)), cmd, ETHER_ADDR_TO_STR(&bssid)); + + if (cmd == NL80211_CMD_DEL_STATION) { + link->bssid = ETHER_ADDR_NULL; + return 0; + } - updated = !ether_addr_equal(&link->bssid, &bssid); link->bssid = bssid; + + if (manager->enumerating && + link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid) + log_link_info(link, "Connected WiFi access point: %s (%s)", + link->ssid, ETHER_ADDR_TO_STR(&link->bssid)); + break; } + case NL80211_CMD_CONNECT: { + struct ether_addr bssid; + uint16_t status_code; + + r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid); + if (r < 0 && r != -ENODATA) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = sd_netlink_message_read_u16(message, NL80211_ATTR_STATUS_CODE, &status_code); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid status code, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + log_link_debug(link, "nl80211: received %s(%u) message: status=%u, bssid=%s", + strna(nl80211_cmd_to_string(cmd)), cmd, status_code, ETHER_ADDR_TO_STR(&bssid)); - updated = updated || link->wlan_iftype != iftype; - link->wlan_iftype = iftype; - updated = updated || !streq_ptr(link->ssid, ssid); - free_and_replace(link->ssid, ssid); + if (status_code != 0) + return 0; + + link->bssid = bssid; + + if (!manager->enumerating) { + r = link_get_wlan_interface(link); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to update wireless LAN interface: %m"); + link_enter_failed(link); + return 0; + } + } - if (updated) { if (link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid) log_link_info(link, "Connected WiFi access point: %s (%s)", link->ssid, ETHER_ADDR_TO_STR(&link->bssid)); + break; + } + case NL80211_CMD_DISCONNECT: + log_link_debug(link, "nl80211: received %s(%u) message.", + strna(nl80211_cmd_to_string(cmd)), cmd); + + link->bssid = ETHER_ADDR_NULL; + link->ssid = mfree(link->ssid); + break; - return 1; /* Some information is updated. */ + default: + log_link_debug(link, "nl80211: received %s(%u) message.", + strna(nl80211_cmd_to_string(cmd)), cmd); } - return 0; /* No new information. */ + return 0; } diff --git a/src/network/networkd-wifi.h b/src/network/networkd-wifi.h index ab868eba27e..a4ca21e42d0 100644 --- a/src/network/networkd-wifi.h +++ b/src/network/networkd-wifi.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "sd-bus.h" +#include "sd-netlink.h" -typedef struct Link Link; +typedef struct Manager Manager; -int wifi_get_info(Link *link); +int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *message, Manager *manager); +int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager); -- 2.47.3