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