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