]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-wiphy.c
Merge pull request #23991 from yuwata/udev-reload
[thirdparty/systemd.git] / src / network / networkd-wiphy.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <net/if_arp.h>
4 #include <linux/nl80211.h>
5
6 #include "device-private.h"
7 #include "device-util.h"
8 #include "networkd-manager.h"
9 #include "networkd-wiphy.h"
10 #include "parse-util.h"
11 #include "path-util.h"
12 #include "udev-util.h"
13 #include "wifi-util.h"
14
15 Wiphy *wiphy_free(Wiphy *w) {
16 if (!w)
17 return NULL;
18
19 if (w->manager) {
20 hashmap_remove_value(w->manager->wiphy_by_index, UINT32_TO_PTR(w->index), w);
21 if (w->name)
22 hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
23 }
24
25 sd_device_unref(w->dev);
26 sd_device_unref(w->rfkill);
27
28 free(w->name);
29 return mfree(w);
30 }
31
32 static int wiphy_new(Manager *manager, sd_netlink_message *message, Wiphy **ret) {
33 _cleanup_(wiphy_freep) Wiphy *w = NULL;
34 _cleanup_free_ char *name = NULL;
35 uint32_t index;
36 int r;
37
38 assert(manager);
39 assert(message);
40
41 r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
42 if (r < 0)
43 return r;
44
45 r = sd_netlink_message_read_string_strdup(message, NL80211_ATTR_WIPHY_NAME, &name);
46 if (r < 0)
47 return r;
48
49 w = new(Wiphy, 1);
50 if (!w)
51 return -ENOMEM;
52
53 *w = (Wiphy) {
54 .manager = manager,
55 .index = index,
56 .name = TAKE_PTR(name),
57 };
58
59 r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w);
60 if (r < 0)
61 return r;
62
63 r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
64 if (r < 0)
65 return r;
66
67 log_wiphy_debug(w, "Saved new wiphy: index=%"PRIu32, w->index);
68
69 if (ret)
70 *ret = w;
71
72 TAKE_PTR(w);
73 return 0;
74 }
75
76 int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret) {
77 Wiphy *w;
78
79 assert(manager);
80
81 w = hashmap_get(manager->wiphy_by_index, UINT32_TO_PTR(index));
82 if (!w)
83 return -ENODEV;
84
85 if (ret)
86 *ret = w;
87
88 return 0;
89 }
90
91 int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) {
92 Wiphy *w;
93
94 assert(manager);
95 assert(name);
96
97 w = hashmap_get(manager->wiphy_by_name, name);
98 if (!w)
99 return -ENODEV;
100
101 if (ret)
102 *ret = w;
103
104 return 0;
105 }
106
107 static int link_get_wiphy(Link *link, Wiphy **ret) {
108 _cleanup_(sd_device_unrefp) sd_device *phy = NULL;
109 const char *s;
110 int r;
111
112 assert(link);
113 assert(link->manager);
114
115 if (link->iftype != ARPHRD_ETHER)
116 return -EOPNOTSUPP;
117
118 if (!link->dev)
119 return -EOPNOTSUPP;
120
121 r = sd_device_get_devtype(link->dev, &s);
122 if (r < 0)
123 return r;
124
125 if (!streq_ptr(s, "wlan"))
126 return -EOPNOTSUPP;
127
128 r = sd_device_get_syspath(link->dev, &s);
129 if (r < 0)
130 return r;
131
132 s = strjoina(s, "/phy80211");
133 r = sd_device_new_from_syspath(&phy, s);
134 if (r < 0)
135 return r;
136
137 r = sd_device_get_sysname(phy, &s);
138 if (r < 0)
139 return r;
140
141 /* TODO:
142 * Maybe, it is better to cache the found Wiphy object in the Link object.
143 * To support that, we need to investigate what happens when the _phy_ is renamed. */
144
145 return wiphy_get_by_name(link->manager, s, ret);
146 }
147
148 static int rfkill_get_state(sd_device *dev) {
149 int r;
150
151 assert(dev);
152
153 /* The previous values may be outdated. Let's clear cache and re-read the values. */
154 device_clear_sysattr_cache(dev);
155
156 r = device_get_sysattr_bool(dev, "soft");
157 if (r < 0 && r != -ENOENT)
158 return r;
159 if (r > 0)
160 return RFKILL_SOFT;
161
162 r = device_get_sysattr_bool(dev, "hard");
163 if (r < 0 && r != -ENOENT)
164 return r;
165 if (r > 0)
166 return RFKILL_HARD;
167
168 return RFKILL_UNBLOCKED;
169 }
170
171 static int wiphy_rfkilled(Wiphy *w) {
172 int r;
173
174 assert(w);
175
176 if (!udev_available()) {
177 if (w->rfkill_state != RFKILL_UNBLOCKED) {
178 log_wiphy_debug(w, "Running in container, assuming the radio transmitter is unblocked.");
179 w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
180 }
181 return false;
182 }
183
184 if (!w->rfkill) {
185 if (w->rfkill_state != RFKILL_UNBLOCKED) {
186 log_wiphy_debug(w, "No rfkill device found, assuming the radio transmitter is unblocked.");
187 w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
188 }
189 return false;
190 }
191
192 r = rfkill_get_state(w->rfkill);
193 if (r < 0)
194 return log_wiphy_debug_errno(w, r, "Could not get rfkill state: %m");
195
196 if (w->rfkill_state != r)
197 switch (r) {
198 case RFKILL_UNBLOCKED:
199 log_wiphy_debug(w, "The radio transmitter is unblocked.");
200 break;
201 case RFKILL_SOFT:
202 log_wiphy_debug(w, "The radio transmitter is turned off by software.");
203 break;
204 case RFKILL_HARD:
205 log_wiphy_debug(w, "The radio transmitter is forced off by something outside of the driver's control.");
206 break;
207 default:
208 assert_not_reached();
209 }
210
211 w->rfkill_state = r; /* Cache the state to suppress the above log messages. */
212 return r != RFKILL_UNBLOCKED;
213 }
214
215 int link_rfkilled(Link *link) {
216 Wiphy *w;
217 int r;
218
219 assert(link);
220
221 r = link_get_wiphy(link, &w);
222 if (IN_SET(r, -EOPNOTSUPP, -ENODEV))
223 return false; /* Typically, non-wifi interface or running in container */
224 if (r < 0)
225 return log_link_debug_errno(link, r, "Could not get phy: %m");
226
227 return wiphy_rfkilled(w);
228 }
229
230 static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
231 const char *name;
232 int r;
233
234 assert(w);
235 assert(w->manager);
236 assert(message);
237
238 r = sd_netlink_message_read_string(message, NL80211_ATTR_WIPHY_NAME, &name);
239 if (r == -ENODATA)
240 return 0;
241 if (r < 0)
242 return r;
243
244 if (streq(w->name, name))
245 return 0;
246
247 log_wiphy_debug(w, "Wiphy name change detected, renamed to %s.", name);
248
249 hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
250
251 r = free_and_strdup(&w->name, name);
252 if (r < 0)
253 return r;
254
255 r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
256 if (r < 0)
257 return r;
258
259 return 1; /* updated */
260 }
261
262 static int wiphy_update_device(Wiphy *w) {
263 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
264 int r;
265
266 assert(w);
267 assert(w->name);
268
269 if (!udev_available())
270 return 0;
271
272 w->dev = sd_device_unref(w->dev);
273
274 r = sd_device_new_from_subsystem_sysname(&dev, "ieee80211", w->name);
275 if (r < 0) {
276 /* The corresponding syspath may not exist yet, and may appear later. */
277 log_wiphy_debug_errno(w, r, "Failed to get wiphy device, ignoring: %m");
278 return 0;
279 }
280
281 if (DEBUG_LOGGING) {
282 const char *s = NULL;
283
284 (void) sd_device_get_syspath(dev, &s);
285 log_wiphy_debug(w, "Found device: %s", strna(s));
286 }
287
288 w->dev = TAKE_PTR(dev);
289 return 0;
290 }
291
292 static int wiphy_update_rfkill(Wiphy *w) {
293 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
294 sd_device *rfkill;
295 int r;
296
297 assert(w);
298
299 if (!udev_available())
300 return 0;
301
302 w->rfkill = sd_device_unref(w->rfkill);
303
304 if (!w->dev)
305 return 0;
306
307 r = sd_device_enumerator_new(&e);
308 if (r < 0)
309 return r;
310
311 r = sd_device_enumerator_allow_uninitialized(e);
312 if (r < 0)
313 return r;
314
315 r = sd_device_enumerator_add_match_subsystem(e, "rfkill", true);
316 if (r < 0)
317 return r;
318
319 r = sd_device_enumerator_add_match_parent(e, w->dev);
320 if (r < 0)
321 return r;
322
323 rfkill = sd_device_enumerator_get_device_first(e);
324 if (!rfkill) {
325 /* rfkill device may not detected by the kernel yet, and may appear later. */
326 log_wiphy_debug_errno(w, SYNTHETIC_ERRNO(ENODEV), "No rfkill device found, ignoring.");
327 return 0;
328 }
329
330 if (sd_device_enumerator_get_device_next(e))
331 return log_wiphy_debug_errno(w, SYNTHETIC_ERRNO(EEXIST), "Multiple rfkill devices found.");
332
333 w->rfkill = sd_device_ref(rfkill);
334
335 if (DEBUG_LOGGING) {
336 const char *s = NULL;
337
338 (void) sd_device_get_syspath(rfkill, &s);
339 log_wiphy_debug(w, "Found rfkill device: %s", strna(s));
340 }
341
342 return 0;
343 }
344
345 static int wiphy_update(Wiphy *w) {
346 int r;
347
348 assert(w);
349
350 r = wiphy_update_device(w);
351 if (r < 0)
352 return log_wiphy_debug_errno(w, r, "Failed to update wiphy device: %m");
353
354 r = wiphy_update_rfkill(w);
355 if (r < 0)
356 return log_wiphy_debug_errno(w, r, "Failed to update rfkill device: %m");
357
358 return 0;
359 }
360
361 int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
362 const char *family;
363 uint32_t index;
364 uint8_t cmd;
365 Wiphy *w = NULL;
366 int r;
367
368 assert(genl);
369 assert(message);
370 assert(manager);
371
372 if (sd_netlink_message_is_error(message)) {
373 r = sd_netlink_message_get_errno(message);
374 if (r < 0)
375 log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
376
377 return 0;
378 }
379
380 r = sd_genl_message_get_family_name(genl, message, &family);
381 if (r < 0) {
382 log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
383 return 0;
384 }
385 if (!streq(family, NL80211_GENL_NAME)) {
386 log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family);
387 return 0;
388 }
389
390 r = sd_genl_message_get_command(genl, message, &cmd);
391 if (r < 0) {
392 log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
393 return 0;
394 }
395
396 r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
397 if (r < 0) {
398 log_debug_errno(r, "nl80211: received %s(%u) message without valid index, ignoring: %m",
399 strna(nl80211_cmd_to_string(cmd)), cmd);
400 return 0;
401 }
402
403 (void) wiphy_get_by_index(manager, index, &w);
404
405 switch (cmd) {
406 case NL80211_CMD_NEW_WIPHY: {
407
408 if (!w) {
409 r = wiphy_new(manager, message, &w);
410 if (r < 0) {
411 log_warning_errno(r, "Failed to save new wiphy, ignoring: %m");
412 return 0;
413 }
414 } else {
415 r = wiphy_update_name(w, message);
416 if (r < 0) {
417 log_wiphy_warning_errno(w, r, "Failed to update wiphy name, ignoring: %m");
418 return 0;
419 }
420 if (r == 0)
421 return 0;
422 }
423
424 r = wiphy_update(w);
425 if (r < 0)
426 log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m");
427
428 break;
429 }
430 case NL80211_CMD_DEL_WIPHY:
431
432 if (!w) {
433 log_debug("The kernel removes wiphy we do not know, ignoring: %m");
434 return 0;
435 }
436
437 log_wiphy_debug(w, "Removed.");
438 wiphy_free(w);
439 break;
440
441 default:
442 log_wiphy_debug(w, "nl80211: received %s(%u) message.",
443 strna(nl80211_cmd_to_string(cmd)), cmd);
444 }
445
446 return 0;
447 }
448
449 int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action) {
450 const char *name;
451 Wiphy *w;
452 int r;
453
454 assert(m);
455 assert(device);
456
457 r = sd_device_get_sysname(device, &name);
458 if (r < 0)
459 return log_device_debug_errno(device, r, "Failed to get sysname: %m");
460
461 r = wiphy_get_by_name(m, name, &w);
462 if (r < 0) {
463 /* This error is not critical, as the corresponding genl message may be received later. */
464 log_device_debug_errno(device, r, "Failed to get Wiphy object, ignoring: %m");
465 return 0;
466 }
467
468 return device_unref_and_replace(w->dev, action == SD_DEVICE_REMOVE ? NULL : device);
469 }
470
471 int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action) {
472 _cleanup_free_ char *parent_path = NULL, *parent_name = NULL;
473 const char *s;
474 Wiphy *w;
475 int r;
476
477 assert(m);
478 assert(device);
479
480 r = sd_device_get_syspath(device, &s);
481 if (r < 0)
482 return log_device_debug_errno(device, r, "Failed to get syspath: %m");
483
484 /* Do not use sd_device_get_parent() here, as this might be a 'remove' uevent. */
485 r = path_extract_directory(s, &parent_path);
486 if (r < 0)
487 return log_device_debug_errno(device, r, "Failed to get parent syspath: %m");
488
489 r = path_extract_filename(parent_path, &parent_name);
490 if (r < 0)
491 return log_device_debug_errno(device, r, "Failed to get parent name: %m");
492
493 r = wiphy_get_by_name(m, parent_name, &w);
494 if (r < 0) {
495 /* This error is not critical, as the corresponding genl message may be received later. */
496 log_device_debug_errno(device, r, "Failed to get Wiphy object: %m");
497 return 0;
498 }
499
500 return device_unref_and_replace(w->rfkill, action == SD_DEVICE_REMOVE ? NULL : device);
501 }