1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <net/ethernet.h>
4 #include <linux/nl80211.h>
6 #include "ether-addr-util.h"
7 #include "netlink-util.h"
8 #include "networkd-link.h"
9 #include "networkd-manager.h"
10 #include "networkd-wifi.h"
11 #include "networkd-wiphy.h"
12 #include "string-util.h"
13 #include "wifi-util.h"
15 int link_get_wlan_interface(Link
*link
) {
16 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
21 r
= sd_genl_message_new(link
->manager
->genl
, NL80211_GENL_NAME
, NL80211_CMD_GET_INTERFACE
, &req
);
23 return log_link_debug_errno(link
, r
, "Failed to create generic netlink message: %m");
25 r
= sd_netlink_message_append_u32(req
, NL80211_ATTR_IFINDEX
, link
->ifindex
);
27 return log_link_debug_errno(link
, r
, "Could not append NL80211_ATTR_IFINDEX attribute: %m");
29 r
= sd_netlink_call(link
->manager
->genl
, req
, 0, &reply
);
31 return log_link_debug_errno(link
, r
, "Failed to request information about wlan interface: %m");
33 log_link_debug(link
, "No reply received to request for information about wifi interface, ignoring.");
37 return manager_genl_process_nl80211_config(link
->manager
->genl
, reply
, link
->manager
);
40 int manager_genl_process_nl80211_config(sd_netlink
*genl
, sd_netlink_message
*message
, Manager
*manager
) {
41 _cleanup_free_
char *ssid
= NULL
;
42 uint32_t ifindex
, wlan_iftype
;
43 const char *family
, *ifname
;
53 if (sd_netlink_message_is_error(message
)) {
54 r
= sd_netlink_message_get_errno(message
);
56 log_message_warning_errno(message
, r
, "nl80211: received error message, ignoring");
61 r
= sd_genl_message_get_family_name(genl
, message
, &family
);
63 log_debug_errno(r
, "nl80211: failed to determine genl family, ignoring: %m");
66 if (!streq(family
, NL80211_GENL_NAME
)) {
67 log_debug("nl80211: received message of unexpected genl family '%s', ignoring.", family
);
71 r
= sd_genl_message_get_command(genl
, message
, &cmd
);
73 log_debug_errno(r
, "nl80211: failed to determine genl message command, ignoring: %m");
76 if (IN_SET(cmd
, NL80211_CMD_NEW_WIPHY
, NL80211_CMD_DEL_WIPHY
))
77 return manager_genl_process_nl80211_wiphy(genl
, message
, manager
);
78 if (!IN_SET(cmd
, NL80211_CMD_SET_INTERFACE
, NL80211_CMD_NEW_INTERFACE
, NL80211_CMD_DEL_INTERFACE
)) {
79 log_debug("nl80211: ignoring nl80211 %s(%u) message.",
80 strna(nl80211_cmd_to_string(cmd
)), cmd
);
84 r
= sd_netlink_message_read_u32(message
, NL80211_ATTR_IFINDEX
, &ifindex
);
86 log_debug_errno(r
, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
87 strna(nl80211_cmd_to_string(cmd
)), cmd
);
91 r
= link_get_by_index(manager
, ifindex
, &link
);
93 log_debug_errno(r
, "nl80211: received %s(%u) message for link '%"PRIu32
"' we don't know about, ignoring.",
94 strna(nl80211_cmd_to_string(cmd
)), cmd
, ifindex
);
96 /* The NL80211_CMD_NEW_INTERFACE message might arrive before RTM_NEWLINK, in which case a
97 * link will not have been created yet. Store the interface index such that the wireless
98 * properties of the link (such as wireless interface type) are queried again after the link
101 if (cmd
== NL80211_CMD_NEW_INTERFACE
) {
102 r
= set_ensure_put(&manager
->new_wlan_ifindices
, NULL
, INT_TO_PTR(ifindex
));
104 log_warning_errno(r
, "Failed to add new wireless interface index to set, ignoring: %m");
105 } else if (cmd
== NL80211_CMD_DEL_INTERFACE
)
106 set_remove(manager
->new_wlan_ifindices
, INT_TO_PTR(ifindex
));
111 r
= sd_netlink_message_read_string(message
, NL80211_ATTR_IFNAME
, &ifname
);
113 log_link_debug_errno(link
, r
, "nl80211: received %s(%u) message without valid interface name, ignoring: %m",
114 strna(nl80211_cmd_to_string(cmd
)), cmd
);
118 if (!streq(ifname
, link
->ifname
)) {
119 log_link_debug(link
, "nl80211: received %s(%u) message with invalid interface name '%s', ignoring: %m",
120 strna(nl80211_cmd_to_string(cmd
)), cmd
, ifname
);
124 r
= sd_netlink_message_read_u32(message
, NL80211_ATTR_IFTYPE
, &wlan_iftype
);
126 log_link_debug_errno(link
, r
, "nl80211: received %s(%u) message without valid wlan interface type, ignoring: %m",
127 strna(nl80211_cmd_to_string(cmd
)), cmd
);
131 r
= sd_netlink_message_read_data(message
, NL80211_ATTR_SSID
, &len
, (void**) &ssid
);
132 if (r
< 0 && r
!= -ENODATA
) {
133 log_link_debug_errno(link
, r
, "nl80211: received %s(%u) message without valid SSID, ignoring: %m",
134 strna(nl80211_cmd_to_string(cmd
)), cmd
);
139 log_link_debug(link
, "nl80211: received SSID has zero length, ignoring it: %m");
141 } else if (strlen_ptr(ssid
) != len
) {
142 log_link_debug(link
, "nl80211: received SSID contains NUL characters, ignoring it.");
147 log_link_debug(link
, "nl80211: received %s(%u) message: iftype=%s, ssid=%s",
148 strna(nl80211_cmd_to_string(cmd
)), cmd
,
149 strna(nl80211_iftype_to_string(wlan_iftype
)), strna(ssid
));
152 case NL80211_CMD_SET_INTERFACE
:
153 case NL80211_CMD_NEW_INTERFACE
:
154 link
->wlan_iftype
= wlan_iftype
;
155 free_and_replace(link
->ssid
, ssid
);
158 case NL80211_CMD_DEL_INTERFACE
:
159 link
->wlan_iftype
= NL80211_IFTYPE_UNSPECIFIED
;
160 link
->ssid
= mfree(link
->ssid
);
164 assert_not_reached();
170 int manager_genl_process_nl80211_mlme(sd_netlink
*genl
, sd_netlink_message
*message
, Manager
*manager
) {
181 if (sd_netlink_message_is_error(message
)) {
182 r
= sd_netlink_message_get_errno(message
);
184 log_message_warning_errno(message
, r
, "nl80211: received error message, ignoring");
189 r
= sd_genl_message_get_family_name(genl
, message
, &family
);
191 log_debug_errno(r
, "nl80211: failed to determine genl family, ignoring: %m");
194 if (!streq(family
, NL80211_GENL_NAME
)) {
195 log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family
);
199 r
= sd_genl_message_get_command(genl
, message
, &cmd
);
201 log_debug_errno(r
, "nl80211: failed to determine genl message command, ignoring: %m");
205 r
= sd_netlink_message_read_u32(message
, NL80211_ATTR_IFINDEX
, &ifindex
);
207 log_debug_errno(r
, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
208 strna(nl80211_cmd_to_string(cmd
)), cmd
);
212 r
= link_get_by_index(manager
, ifindex
, &link
);
214 log_debug_errno(r
, "nl80211: received %s(%u) message for link '%"PRIu32
"' we don't know about, ignoring.",
215 strna(nl80211_cmd_to_string(cmd
)), cmd
, ifindex
);
220 case NL80211_CMD_NEW_STATION
:
221 case NL80211_CMD_DEL_STATION
: {
222 struct ether_addr bssid
;
224 r
= sd_netlink_message_read_ether_addr(message
, NL80211_ATTR_MAC
, &bssid
);
226 log_link_debug_errno(link
, r
, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m",
227 strna(nl80211_cmd_to_string(cmd
)), cmd
);
231 log_link_debug(link
, "nl80211: received %s(%u) message: bssid=%s",
232 strna(nl80211_cmd_to_string(cmd
)), cmd
, ETHER_ADDR_TO_STR(&bssid
));
234 if (cmd
== NL80211_CMD_DEL_STATION
) {
235 link
->bssid
= ETHER_ADDR_NULL
;
241 if (manager
->enumerating
&&
242 link
->wlan_iftype
== NL80211_IFTYPE_STATION
&& link
->ssid
)
243 log_link_info(link
, "Connected WiFi access point: %s (%s)",
244 link
->ssid
, ETHER_ADDR_TO_STR(&link
->bssid
));
247 case NL80211_CMD_CONNECT
: {
248 struct ether_addr bssid
;
249 uint16_t status_code
;
251 r
= sd_netlink_message_read_ether_addr(message
, NL80211_ATTR_MAC
, &bssid
);
252 if (r
< 0 && r
!= -ENODATA
) {
253 log_link_debug_errno(link
, r
, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m",
254 strna(nl80211_cmd_to_string(cmd
)), cmd
);
258 r
= sd_netlink_message_read_u16(message
, NL80211_ATTR_STATUS_CODE
, &status_code
);
260 log_link_debug_errno(link
, r
, "nl80211: received %s(%u) message without valid status code, ignoring: %m",
261 strna(nl80211_cmd_to_string(cmd
)), cmd
);
265 log_link_debug(link
, "nl80211: received %s(%u) message: status=%u, bssid=%s",
266 strna(nl80211_cmd_to_string(cmd
)), cmd
, status_code
, ETHER_ADDR_TO_STR(&bssid
));
268 if (status_code
!= 0)
273 if (!manager
->enumerating
) {
274 r
= link_get_wlan_interface(link
);
276 log_link_warning_errno(link
, r
, "Failed to update wireless LAN interface: %m");
277 link_enter_failed(link
);
282 if (link
->wlan_iftype
== NL80211_IFTYPE_STATION
&& link
->ssid
)
283 log_link_info(link
, "Connected WiFi access point: %s (%s)",
284 link
->ssid
, ETHER_ADDR_TO_STR(&link
->bssid
));
286 /* Sometimes, RTM_NEWLINK message with carrier is received earlier than NL80211_CMD_CONNECT.
287 * To make SSID= or other WiFi related settings in [Match] section work, let's try to
288 * reconfigure the interface. */
289 if (link
->ssid
&& link_has_carrier(link
)) {
290 r
= link_reconfigure_impl(link
, /* force = */ false);
292 log_link_warning_errno(link
, r
, "Failed to reconfigure interface: %m");
293 link_enter_failed(link
);
299 case NL80211_CMD_DISCONNECT
:
300 log_link_debug(link
, "nl80211: received %s(%u) message.",
301 strna(nl80211_cmd_to_string(cmd
)), cmd
);
303 link
->bssid
= ETHER_ADDR_NULL
;
304 free_and_replace(link
->previous_ssid
, link
->ssid
);
307 case NL80211_CMD_START_AP
: {
308 log_link_debug(link
, "nl80211: received %s(%u) message.",
309 strna(nl80211_cmd_to_string(cmd
)), cmd
);
311 /* No need to reconfigure during enumeration */
312 if (manager
->enumerating
)
315 /* If there is no carrier, let the link get configured on
316 * carrier gain instead */
317 if (!link_has_carrier(link
))
320 /* AP start event may indicate different properties (e.g. SSID) */
321 r
= link_get_wlan_interface(link
);
323 log_link_warning_errno(link
, r
, "Failed to update wireless LAN interface: %m");
324 link_enter_failed(link
);
328 /* If necessary, reconfigure based on those new properties */
329 r
= link_reconfigure_impl(link
, /* force = */ false);
331 log_link_warning_errno(link
, r
, "Failed to reconfigure interface: %m");
332 link_enter_failed(link
);
340 log_link_debug(link
, "nl80211: received %s(%u) message.",
341 strna(nl80211_cmd_to_string(cmd
)), cmd
);