1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/nl80211.h>
4
5 #include "device-util.h"
6 #include "networkd-manager.h"
7 #include "networkd-wiphy.h"
8 #include "parse-util.h"
9 #include "wifi-util.h"
10
wiphy_free(Wiphy * w)11 Wiphy *wiphy_free(Wiphy *w) {
12 if (!w)
13 return NULL;
14
15 if (w->manager) {
16 hashmap_remove_value(w->manager->wiphy_by_index, UINT32_TO_PTR(w->index), w);
17 if (w->name)
18 hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
19 }
20
21 free(w->name);
22 return mfree(w);
23 }
24
wiphy_new(Manager * manager,uint32_t index,Wiphy ** ret)25 static int wiphy_new(Manager *manager, uint32_t index, Wiphy **ret) {
26 _cleanup_(wiphy_freep) Wiphy *w = NULL;
27 int r;
28
29 assert(manager);
30
31 w = new(Wiphy, 1);
32 if (!w)
33 return -ENOMEM;
34
35 *w = (Wiphy) {
36 .index = index,
37 };
38
39 r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w);
40 if (r < 0)
41 return r;
42
43 w->manager = manager;
44
45 if (ret)
46 *ret = w;
47
48 TAKE_PTR(w);
49 return 0;
50 }
51
wiphy_get_by_index(Manager * manager,uint32_t index,Wiphy ** ret)52 int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret) {
53 Wiphy *w;
54
55 assert(manager);
56
57 w = hashmap_get(manager->wiphy_by_index, UINT32_TO_PTR(index));
58 if (!w)
59 return -ENODEV;
60
61 if (ret)
62 *ret = w;
63
64 return 0;
65 }
66
wiphy_get_by_name(Manager * manager,const char * name,Wiphy ** ret)67 int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) {
68 Wiphy *w;
69
70 assert(manager);
71 assert(name);
72
73 w = hashmap_get(manager->wiphy_by_name, name);
74 if (!w)
75 return -ENODEV;
76
77 if (ret)
78 *ret = w;
79
80 return 0;
81 }
82
wiphy_update_name(Wiphy * w,sd_netlink_message * message)83 static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
84 const char *name;
85 int r;
86
87 assert(w);
88 assert(w->manager);
89 assert(message);
90
91 r = sd_netlink_message_read_string(message, NL80211_ATTR_WIPHY_NAME, &name);
92 if (r == -ENODATA)
93 return 0;
94 if (r < 0)
95 return r;
96
97 if (streq_ptr(w->name, name))
98 return 0;
99
100 if (w->name)
101 hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
102
103 r = free_and_strdup(&w->name, name);
104 if (r < 0)
105 return r;
106
107 return hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
108 }
109
wiphy_update(Wiphy * w,sd_netlink_message * message)110 static int wiphy_update(Wiphy *w, sd_netlink_message *message) {
111 int r;
112
113 assert(w);
114 assert(message);
115
116 r = wiphy_update_name(w, message);
117 if (r < 0)
118 return log_wiphy_debug_errno(w, r, "Failed to update wiphy name: %m");
119
120 return 0;
121 }
122
manager_genl_process_nl80211_wiphy(sd_netlink * genl,sd_netlink_message * message,Manager * manager)123 int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
124 const char *family;
125 uint32_t index;
126 uint8_t cmd;
127 Wiphy *w = NULL;
128 int r;
129
130 assert(genl);
131 assert(message);
132 assert(manager);
133
134 if (sd_netlink_message_is_error(message)) {
135 r = sd_netlink_message_get_errno(message);
136 if (r < 0)
137 log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
138
139 return 0;
140 }
141
142 r = sd_genl_message_get_family_name(genl, message, &family);
143 if (r < 0) {
144 log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
145 return 0;
146 }
147 if (!streq(family, NL80211_GENL_NAME)) {
148 log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family);
149 return 0;
150 }
151
152 r = sd_genl_message_get_command(genl, message, &cmd);
153 if (r < 0) {
154 log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
155 return 0;
156 }
157
158 r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
159 if (r < 0) {
160 log_debug_errno(r, "nl80211: received %s(%u) message without valid index, ignoring: %m",
161 strna(nl80211_cmd_to_string(cmd)), cmd);
162 return 0;
163 }
164
165 (void) wiphy_get_by_index(manager, index, &w);
166
167 switch (cmd) {
168 case NL80211_CMD_NEW_WIPHY: {
169 bool is_new = !w;
170
171 if (!w) {
172 r = wiphy_new(manager, index, &w);
173 if (r < 0) {
174 log_warning_errno(r, "Failed to allocate wiphy, ignoring: %m");
175 return 0;
176 }
177 }
178
179 r = wiphy_update(w, message);
180 if (r < 0) {
181 log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m");
182 return 0;
183 }
184
185 log_wiphy_debug(w, "Received %s phy.", is_new ? "new" : "updated");
186 break;
187 }
188 case NL80211_CMD_DEL_WIPHY:
189
190 if (!w) {
191 log_debug("The kernel removes wiphy we do not know, ignoring: %m");
192 return 0;
193 }
194
195 log_wiphy_debug(w, "Removed.");
196 wiphy_free(w);
197 break;
198
199 default:
200 log_wiphy_debug(w, "nl80211: received %s(%u) message.",
201 strna(nl80211_cmd_to_string(cmd)), cmd);
202 }
203
204 return 0;
205 }
206