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
link_get_wlan_interface(Link * link)15 static 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
manager_genl_process_nl80211_config(sd_netlink * genl,sd_netlink_message * message,Manager * 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;
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 return 0;
96 }
97
98 r = sd_netlink_message_read_string(message, NL80211_ATTR_IFNAME, &ifname);
99 if (r < 0) {
100 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid interface name, ignoring: %m",
101 strna(nl80211_cmd_to_string(cmd)), cmd);
102 return 0;
103 }
104
105 if (!streq(ifname, link->ifname)) {
106 log_link_debug_errno(link, r, "nl80211: received %s(%u) message with invalid interface name '%s', ignoring: %m",
107 strna(nl80211_cmd_to_string(cmd)), cmd, ifname);
108 return 0;
109 }
110
111 r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFTYPE, &wlan_iftype);
112 if (r < 0) {
113 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid wlan interface type, ignoring: %m",
114 strna(nl80211_cmd_to_string(cmd)), cmd);
115 return 0;
116 }
117
118 r = sd_netlink_message_read_data_suffix0(message, NL80211_ATTR_SSID, &len, (void**) &ssid);
119 if (r < 0 && r != -ENODATA) {
120 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid SSID, ignoring: %m",
121 strna(nl80211_cmd_to_string(cmd)), cmd);
122 return 0;
123 }
124 if (r >= 0) {
125 if (len == 0) {
126 log_link_debug(link, "nl80211: received SSID has zero length, ignoring the received SSID: %m");
127 ssid = mfree(ssid);
128 } else if (strlen_ptr(ssid) != len) {
129 log_link_debug(link, "nl80211: received SSID contains NUL character(s), ignoring the received SSID.");
130 ssid = mfree(ssid);
131 }
132 }
133
134 log_link_debug(link, "nl80211: received %s(%u) message: iftype=%s, ssid=%s",
135 strna(nl80211_cmd_to_string(cmd)), cmd,
136 strna(nl80211_iftype_to_string(wlan_iftype)), strna(ssid));
137
138 switch (cmd) {
139 case NL80211_CMD_SET_INTERFACE:
140 case NL80211_CMD_NEW_INTERFACE:
141 link->wlan_iftype = wlan_iftype;
142 free_and_replace(link->ssid, ssid);
143 break;
144
145 case NL80211_CMD_DEL_INTERFACE:
146 link->wlan_iftype = NL80211_IFTYPE_UNSPECIFIED;
147 link->ssid = mfree(link->ssid);
148 break;
149
150 default:
151 assert_not_reached();
152 }
153
154 return 0;
155 }
156
manager_genl_process_nl80211_mlme(sd_netlink * genl,sd_netlink_message * message,Manager * manager)157 int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
158 const char *family;
159 uint32_t ifindex;
160 uint8_t cmd;
161 Link *link;
162 int r;
163
164 assert(genl);
165 assert(message);
166 assert(manager);
167
168 if (sd_netlink_message_is_error(message)) {
169 r = sd_netlink_message_get_errno(message);
170 if (r < 0)
171 log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
172
173 return 0;
174 }
175
176 r = sd_genl_message_get_family_name(genl, message, &family);
177 if (r < 0) {
178 log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
179 return 0;
180 }
181 if (!streq(family, NL80211_GENL_NAME)) {
182 log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family);
183 return 0;
184 }
185
186 r = sd_genl_message_get_command(genl, message, &cmd);
187 if (r < 0) {
188 log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
189 return 0;
190 }
191
192 r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex);
193 if (r < 0) {
194 log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
195 strna(nl80211_cmd_to_string(cmd)), cmd);
196 return 0;
197 }
198
199 r = link_get_by_index(manager, ifindex, &link);
200 if (r < 0) {
201 log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.",
202 strna(nl80211_cmd_to_string(cmd)), cmd, ifindex);
203 return 0;
204 }
205
206 switch (cmd) {
207 case NL80211_CMD_NEW_STATION:
208 case NL80211_CMD_DEL_STATION: {
209 struct ether_addr bssid;
210
211 r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid);
212 if (r < 0) {
213 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m",
214 strna(nl80211_cmd_to_string(cmd)), cmd);
215 return 0;
216 }
217
218 log_link_debug(link, "nl80211: received %s(%u) message: bssid=%s",
219 strna(nl80211_cmd_to_string(cmd)), cmd, ETHER_ADDR_TO_STR(&bssid));
220
221 if (cmd == NL80211_CMD_DEL_STATION) {
222 link->bssid = ETHER_ADDR_NULL;
223 return 0;
224 }
225
226 link->bssid = bssid;
227
228 if (manager->enumerating &&
229 link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid)
230 log_link_info(link, "Connected WiFi access point: %s (%s)",
231 link->ssid, ETHER_ADDR_TO_STR(&link->bssid));
232 break;
233 }
234 case NL80211_CMD_CONNECT: {
235 struct ether_addr bssid;
236 uint16_t status_code;
237
238 r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid);
239 if (r < 0 && r != -ENODATA) {
240 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m",
241 strna(nl80211_cmd_to_string(cmd)), cmd);
242 return 0;
243 }
244
245 r = sd_netlink_message_read_u16(message, NL80211_ATTR_STATUS_CODE, &status_code);
246 if (r < 0) {
247 log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid status code, ignoring: %m",
248 strna(nl80211_cmd_to_string(cmd)), cmd);
249 return 0;
250 }
251
252 log_link_debug(link, "nl80211: received %s(%u) message: status=%u, bssid=%s",
253 strna(nl80211_cmd_to_string(cmd)), cmd, status_code, ETHER_ADDR_TO_STR(&bssid));
254
255 if (status_code != 0)
256 return 0;
257
258 link->bssid = bssid;
259
260 if (!manager->enumerating) {
261 r = link_get_wlan_interface(link);
262 if (r < 0) {
263 log_link_warning_errno(link, r, "Failed to update wireless LAN interface: %m");
264 link_enter_failed(link);
265 return 0;
266 }
267 }
268
269 if (link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid)
270 log_link_info(link, "Connected WiFi access point: %s (%s)",
271 link->ssid, ETHER_ADDR_TO_STR(&link->bssid));
272 break;
273 }
274 case NL80211_CMD_DISCONNECT:
275 log_link_debug(link, "nl80211: received %s(%u) message.",
276 strna(nl80211_cmd_to_string(cmd)), cmd);
277
278 link->bssid = ETHER_ADDR_NULL;
279 free_and_replace(link->previous_ssid, link->ssid);
280 break;
281
282 default:
283 log_link_debug(link, "nl80211: received %s(%u) message.",
284 strna(nl80211_cmd_to_string(cmd)), cmd);
285 }
286
287 return 0;
288 }
289