summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-wifi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-wifi.c')
-rw-r--r--src/network/networkd-wifi.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c
new file mode 100644
index 0000000..98e7a72
--- /dev/null
+++ b/src/network/networkd-wifi.c
@@ -0,0 +1,345 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <net/ethernet.h>
+#include <linux/nl80211.h>
+
+#include "ether-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-wifi.h"
+#include "networkd-wiphy.h"
+#include "string-util.h"
+#include "wifi-util.h"
+
+int link_get_wlan_interface(Link *link) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ int r;
+
+ assert(link);
+
+ 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");
+
+ 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;
+ }
+ if (IN_SET(cmd, NL80211_CMD_NEW_WIPHY, NL80211_CMD_DEL_WIPHY))
+ return manager_genl_process_nl80211_wiphy(genl, message, manager);
+ 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;
+ }
+
+ 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);
+
+ /* The NL80211_CMD_NEW_INTERFACE message might arrive before RTM_NEWLINK, in which case a
+ * link will not have been created yet. Store the interface index such that the wireless
+ * properties of the link (such as wireless interface type) are queried again after the link
+ * is created.
+ */
+ if (cmd == NL80211_CMD_NEW_INTERFACE) {
+ r = set_ensure_put(&manager->new_wlan_ifindices, NULL, INT_TO_PTR(ifindex));
+ if (r < 0)
+ log_warning_errno(r, "Failed to add new wireless interface index to set, ignoring: %m");
+ } else if (cmd == NL80211_CMD_DEL_INTERFACE)
+ set_remove(manager->new_wlan_ifindices, INT_TO_PTR(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(link, "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 it: %m");
+ ssid = mfree(ssid);
+ } else if (strlen_ptr(ssid) != len) {
+ log_link_debug(link, "nl80211: received SSID contains NUL characters, ignoring it.");
+ ssid = mfree(ssid);
+ }
+ }
+
+ 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)), strna(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)
+ 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;
+ }
+
+ 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));
+
+ 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 (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));
+
+ /* Sometimes, RTM_NEWLINK message with carrier is received earlier than NL80211_CMD_CONNECT.
+ * To make SSID= or other WiFi related settings in [Match] section work, let's try to
+ * reconfigure the interface. */
+ if (link->ssid && link_has_carrier(link)) {
+ r = link_reconfigure_impl(link, /* force = */ false);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+ link_enter_failed(link);
+ return 0;
+ }
+ }
+ 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;
+ free_and_replace(link->previous_ssid, link->ssid);
+ break;
+
+ case NL80211_CMD_START_AP: {
+ log_link_debug(link, "nl80211: received %s(%u) message.",
+ strna(nl80211_cmd_to_string(cmd)), cmd);
+
+ /* No need to reconfigure during enumeration */
+ if (manager->enumerating)
+ break;
+
+ /* If there is no carrier, let the link get configured on
+ * carrier gain instead */
+ if (!link_has_carrier(link))
+ break;
+
+ /* AP start event may indicate different properties (e.g. SSID) */
+ 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 necessary, reconfigure based on those new properties */
+ r = link_reconfigure_impl(link, /* force = */ false);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+ link_enter_failed(link);
+ return 0;
+ }
+
+ break;
+ }
+
+ default:
+ log_link_debug(link, "nl80211: received %s(%u) message.",
+ strna(nl80211_cmd_to_string(cmd)), cmd);
+ }
+
+ return 0;
+}