]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-wifi.c
Merge pull request #32784 from YHNdnzj/release-version
[thirdparty/systemd.git] / src / network / networkd-wifi.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <net/ethernet.h>
4 #include <linux/nl80211.h>
5
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"
14
15 int link_get_wlan_interface(Link *link) {
16 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
17 int r;
18
19 assert(link);
20
21 r = sd_genl_message_new(link->manager->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req);
22 if (r < 0)
23 return log_link_debug_errno(link, r, "Failed to create generic netlink message: %m");
24
25 r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex);
26 if (r < 0)
27 return log_link_debug_errno(link, r, "Could not append NL80211_ATTR_IFINDEX attribute: %m");
28
29 r = sd_netlink_call(link->manager->genl, req, 0, &reply);
30 if (r < 0)
31 return log_link_debug_errno(link, r, "Failed to request information about wlan interface: %m");
32 if (!reply) {
33 log_link_debug(link, "No reply received to request for information about wifi interface, ignoring.");
34 return 0;
35 }
36
37 return manager_genl_process_nl80211_config(link->manager->genl, reply, link->manager);
38 }
39
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;
44 uint8_t cmd;
45 size_t len;
46 Link *link;
47 int r;
48
49 assert(genl);
50 assert(message);
51 assert(manager);
52
53 if (sd_netlink_message_is_error(message)) {
54 r = sd_netlink_message_get_errno(message);
55 if (r < 0)
56 log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
57
58 return 0;
59 }
60
61 r = sd_genl_message_get_family_name(genl, message, &family);
62 if (r < 0) {
63 log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
64 return 0;
65 }
66 if (!streq(family, NL80211_GENL_NAME)) {
67 log_debug("nl80211: received message of unexpected genl family '%s', ignoring.", family);
68 return 0;
69 }
70
71 r = sd_genl_message_get_command(genl, message, &cmd);
72 if (r < 0) {
73 log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
74 return 0;
75 }
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);
81 return 0;
82 }
83
84 r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex);
85 if (r < 0) {
86 log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
87 strna(nl80211_cmd_to_string(cmd)), cmd);
88 return 0;
89 }
90
91 r = link_get_by_index(manager, ifindex, &link);
92 if (r < 0) {
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);
95
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
99 * is created.
100 */
101 if (cmd == NL80211_CMD_NEW_INTERFACE) {
102 r = set_ensure_put(&manager->new_wlan_ifindices, NULL, INT_TO_PTR(ifindex));
103 if (r < 0)
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));
107
108 return 0;
109 }
110
111 r = sd_netlink_message_read_string(message, NL80211_ATTR_IFNAME, &ifname);
112 if (r < 0) {
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);
115 return 0;
116 }
117
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);
121 return 0;
122 }
123
124 r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFTYPE, &wlan_iftype);
125 if (r < 0) {
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);
128 return 0;
129 }
130
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);
135 return 0;
136 }
137 if (r >= 0) {
138 if (len == 0) {
139 log_link_debug(link, "nl80211: received SSID has zero length, ignoring it: %m");
140 ssid = mfree(ssid);
141 } else if (strlen_ptr(ssid) != len) {
142 log_link_debug(link, "nl80211: received SSID contains NUL characters, ignoring it.");
143 ssid = mfree(ssid);
144 }
145 }
146
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));
150
151 switch (cmd) {
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);
156 break;
157
158 case NL80211_CMD_DEL_INTERFACE:
159 link->wlan_iftype = NL80211_IFTYPE_UNSPECIFIED;
160 link->ssid = mfree(link->ssid);
161 break;
162
163 default:
164 assert_not_reached();
165 }
166
167 return 0;
168 }
169
170 int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
171 const char *family;
172 uint32_t ifindex;
173 uint8_t cmd;
174 Link *link;
175 int r;
176
177 assert(genl);
178 assert(message);
179 assert(manager);
180
181 if (sd_netlink_message_is_error(message)) {
182 r = sd_netlink_message_get_errno(message);
183 if (r < 0)
184 log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
185
186 return 0;
187 }
188
189 r = sd_genl_message_get_family_name(genl, message, &family);
190 if (r < 0) {
191 log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
192 return 0;
193 }
194 if (!streq(family, NL80211_GENL_NAME)) {
195 log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family);
196 return 0;
197 }
198
199 r = sd_genl_message_get_command(genl, message, &cmd);
200 if (r < 0) {
201 log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
202 return 0;
203 }
204
205 r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex);
206 if (r < 0) {
207 log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
208 strna(nl80211_cmd_to_string(cmd)), cmd);
209 return 0;
210 }
211
212 r = link_get_by_index(manager, ifindex, &link);
213 if (r < 0) {
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);
216 return 0;
217 }
218
219 switch (cmd) {
220 case NL80211_CMD_NEW_STATION:
221 case NL80211_CMD_DEL_STATION: {
222 struct ether_addr bssid;
223
224 r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid);
225 if (r < 0) {
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);
228 return 0;
229 }
230
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));
233
234 if (cmd == NL80211_CMD_DEL_STATION) {
235 link->bssid = ETHER_ADDR_NULL;
236 return 0;
237 }
238
239 link->bssid = bssid;
240
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));
245 break;
246 }
247 case NL80211_CMD_CONNECT: {
248 struct ether_addr bssid;
249 uint16_t status_code;
250
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);
255 return 0;
256 }
257
258 r = sd_netlink_message_read_u16(message, NL80211_ATTR_STATUS_CODE, &status_code);
259 if (r < 0) {
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);
262 return 0;
263 }
264
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));
267
268 if (status_code != 0)
269 return 0;
270
271 link->bssid = bssid;
272
273 if (!manager->enumerating) {
274 r = link_get_wlan_interface(link);
275 if (r < 0) {
276 log_link_warning_errno(link, r, "Failed to update wireless LAN interface: %m");
277 link_enter_failed(link);
278 return 0;
279 }
280 }
281
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));
285
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);
291 if (r < 0) {
292 log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
293 link_enter_failed(link);
294 return 0;
295 }
296 }
297 break;
298 }
299 case NL80211_CMD_DISCONNECT:
300 log_link_debug(link, "nl80211: received %s(%u) message.",
301 strna(nl80211_cmd_to_string(cmd)), cmd);
302
303 link->bssid = ETHER_ADDR_NULL;
304 free_and_replace(link->previous_ssid, link->ssid);
305 break;
306
307 case NL80211_CMD_START_AP: {
308 log_link_debug(link, "nl80211: received %s(%u) message.",
309 strna(nl80211_cmd_to_string(cmd)), cmd);
310
311 /* No need to reconfigure during enumeration */
312 if (manager->enumerating)
313 break;
314
315 /* If there is no carrier, let the link get configured on
316 * carrier gain instead */
317 if (!link_has_carrier(link))
318 break;
319
320 /* AP start event may indicate different properties (e.g. SSID) */
321 r = link_get_wlan_interface(link);
322 if (r < 0) {
323 log_link_warning_errno(link, r, "Failed to update wireless LAN interface: %m");
324 link_enter_failed(link);
325 return 0;
326 }
327
328 /* If necessary, reconfigure based on those new properties */
329 r = link_reconfigure_impl(link, /* force = */ false);
330 if (r < 0) {
331 log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
332 link_enter_failed(link);
333 return 0;
334 }
335
336 break;
337 }
338
339 default:
340 log_link_debug(link, "nl80211: received %s(%u) message.",
341 strna(nl80211_cmd_to_string(cmd)), cmd);
342 }
343
344 return 0;
345 }