1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "supp_events.h"
8
9 #include "includes.h"
10 #include "common.h"
11 #include "common/ieee802_11_defs.h"
12 #include "wpa_supplicant_i.h"
13
14 #ifdef CONFIG_AP
15 #include "ap/sta_info.h"
16 #include "ap/ieee802_11.h"
17 #include "ap/hostapd.h"
18 #endif /* CONFIG_AP */
19
20 #include <zephyr/net/wifi_mgmt.h>
21
22 /* Re-defines MAC2STR with address of the element */
23 #define MACADDR2STR(a) &(a)[0], &(a)[1], &(a)[2], &(a)[3], &(a)[4], &(a)[5]
24
25 static const struct wpa_supp_event_info {
26 const char *event_str;
27 enum supplicant_event_num event;
28 } wpa_supp_event_info[] = {
29 { "CTRL-EVENT-CONNECTED", SUPPLICANT_EVENT_CONNECTED },
30 { "CTRL-EVENT-DISCONNECTED", SUPPLICANT_EVENT_DISCONNECTED },
31 { "CTRL-EVENT-ASSOC-REJECT", SUPPLICANT_EVENT_ASSOC_REJECT },
32 { "CTRL-EVENT-AUTH-REJECT", SUPPLICANT_EVENT_AUTH_REJECT },
33 { "CTRL-EVENT-SSID-TEMP-DISABLED", SUPPLICANT_EVENT_SSID_TEMP_DISABLED },
34 { "CTRL-EVENT-SSID-REENABLED", SUPPLICANT_EVENT_SSID_REENABLED },
35 { "CTRL-EVENT-BSS-ADDED", SUPPLICANT_EVENT_BSS_ADDED },
36 { "CTRL-EVENT-BSS-REMOVED", SUPPLICANT_EVENT_BSS_REMOVED },
37 { "CTRL-EVENT-TERMINATING", SUPPLICANT_EVENT_TERMINATING },
38 { "CTRL-EVENT-SCAN-STARTED", SUPPLICANT_EVENT_SCAN_STARTED },
39 { "CTRL-EVENT-SCAN-RESULTS", SUPPLICANT_EVENT_SCAN_RESULTS },
40 { "CTRL-EVENT-SCAN-FAILED", SUPPLICANT_EVENT_SCAN_FAILED },
41 { "CTRL-EVENT-NETWORK-NOT-FOUND", SUPPLICANT_EVENT_NETWORK_NOT_FOUND },
42 { "CTRL-EVENT-NETWORK-ADDED", SUPPLICANT_EVENT_NETWORK_ADDED },
43 { "CTRL-EVENT-NETWORK-REMOVED", SUPPLICANT_EVENT_NETWORK_REMOVED },
44 { "CTRL-EVENT-DSCP-POLICY", SUPPLICANT_EVENT_DSCP_POLICY },
45 { "CTRL-EVENT-REGDOM-CHANGE", SUPPLICANT_EVENT_REGDOM_CHANGE },
46 };
47
copy_mac_addr(const unsigned int * src,uint8_t * dst)48 static void copy_mac_addr(const unsigned int *src, uint8_t *dst)
49 {
50 int i;
51
52 for (i = 0; i < ETH_ALEN; i++) {
53 dst[i] = src[i];
54 }
55 }
56
wpas_to_wifi_mgmt_conn_status(int status)57 static enum wifi_conn_status wpas_to_wifi_mgmt_conn_status(int status)
58 {
59 switch (status) {
60 case WLAN_STATUS_SUCCESS:
61 return WIFI_STATUS_CONN_SUCCESS;
62 case WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT:
63 return WIFI_STATUS_CONN_WRONG_PASSWORD;
64 /* Handle non-supplicant errors */
65 case -ETIMEDOUT:
66 return WIFI_STATUS_CONN_TIMEOUT;
67 default:
68 return WIFI_STATUS_CONN_FAIL;
69 }
70 }
71
wpas_to_wifi_mgmt_disconn_status(int status)72 static enum wifi_disconn_reason wpas_to_wifi_mgmt_disconn_status(int status)
73 {
74 switch (status) {
75 case WIFI_REASON_DISCONN_SUCCESS:
76 return WIFI_REASON_DISCONN_SUCCESS;
77 case WLAN_REASON_DEAUTH_LEAVING:
78 return WIFI_REASON_DISCONN_AP_LEAVING;
79 case WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY:
80 return WIFI_REASON_DISCONN_INACTIVITY;
81 case WLAN_REASON_UNSPECIFIED:
82 /* fall through */
83 default:
84 return WIFI_REASON_DISCONN_UNSPECIFIED;
85 }
86 }
87
supplicant_process_status(struct supplicant_int_event_data * event_data,char * supplicant_status)88 static int supplicant_process_status(struct supplicant_int_event_data *event_data,
89 char *supplicant_status)
90 {
91 int ret = 1; /* For cases where parsing is not being done*/
92 int i;
93 union supplicant_event_data *data;
94 unsigned int tmp_mac_addr[ETH_ALEN];
95 unsigned int event_prefix_len;
96 char *event_no_prefix;
97 struct wpa_supp_event_info event_info;
98
99 data = (union supplicant_event_data *)event_data->data;
100
101 for (i = 0; i < ARRAY_SIZE(wpa_supp_event_info); i++) {
102 if (strncmp(supplicant_status, wpa_supp_event_info[i].event_str,
103 strlen(wpa_supp_event_info[i].event_str)) == 0) {
104 event_info = wpa_supp_event_info[i];
105 break;
106 }
107 }
108
109 if (i >= ARRAY_SIZE(wpa_supp_event_info)) {
110 /* This is not a bug but rather implementation gap (intentional or not) */
111 wpa_printf(MSG_DEBUG, "Event not supported: %s", supplicant_status);
112 return -ENOTSUP;
113 }
114
115 /* Skip the event prefix and a space */
116 event_prefix_len = strlen(event_info.event_str) + 1;
117 event_no_prefix = supplicant_status + event_prefix_len;
118 event_data->event = event_info.event;
119
120 switch (event_data->event) {
121 case SUPPLICANT_EVENT_CONNECTED:
122 ret = sscanf(event_no_prefix, "- Connection to"
123 MACSTR, MACADDR2STR(tmp_mac_addr));
124 event_data->data_len = sizeof(data->connected);
125 copy_mac_addr(tmp_mac_addr, data->connected.bssid);
126 break;
127 case SUPPLICANT_EVENT_DISCONNECTED:
128 ret = sscanf(event_no_prefix,
129 MACSTR" reason=%d", MACADDR2STR(tmp_mac_addr),
130 &data->disconnected.reason_code);
131 event_data->data_len = sizeof(data->disconnected);
132 copy_mac_addr(tmp_mac_addr, data->disconnected.bssid);
133 break;
134 case SUPPLICANT_EVENT_ASSOC_REJECT:
135 /* TODO */
136 break;
137 case SUPPLICANT_EVENT_AUTH_REJECT:
138 ret = sscanf(event_no_prefix, MACSTR
139 " auth_type=%u auth_transaction=%u status_code=%u",
140 MACADDR2STR(tmp_mac_addr),
141 &data->auth_reject.auth_type,
142 &data->auth_reject.auth_transaction,
143 &data->auth_reject.status_code);
144 event_data->data_len = sizeof(data->auth_reject);
145 copy_mac_addr(tmp_mac_addr, data->auth_reject.bssid);
146 break;
147 case SUPPLICANT_EVENT_SSID_TEMP_DISABLED:
148 ret = sscanf(event_no_prefix,
149 "id=%d ssid=%s auth_failures=%u duration=%d reason=%s",
150 &data->temp_disabled.id, data->temp_disabled.ssid,
151 &data->temp_disabled.auth_failures,
152 &data->temp_disabled.duration,
153 data->temp_disabled.reason_code);
154 event_data->data_len = sizeof(data->temp_disabled);
155 break;
156 case SUPPLICANT_EVENT_SSID_REENABLED:
157 ret = sscanf(event_no_prefix,
158 "id=%d ssid=%s", &data->reenabled.id,
159 data->reenabled.ssid);
160 event_data->data_len = sizeof(data->reenabled);
161 break;
162 case SUPPLICANT_EVENT_BSS_ADDED:
163 ret = sscanf(event_no_prefix, "%u "MACSTR,
164 &data->bss_added.id,
165 MACADDR2STR(tmp_mac_addr));
166 copy_mac_addr(tmp_mac_addr, data->bss_added.bssid);
167 event_data->data_len = sizeof(data->bss_added);
168 break;
169 case SUPPLICANT_EVENT_BSS_REMOVED:
170 ret = sscanf(event_no_prefix,
171 "%u "MACSTR,
172 &data->bss_removed.id,
173 MACADDR2STR(tmp_mac_addr));
174 event_data->data_len = sizeof(data->bss_removed);
175 copy_mac_addr(tmp_mac_addr, data->bss_removed.bssid);
176 break;
177 case SUPPLICANT_EVENT_TERMINATING:
178 case SUPPLICANT_EVENT_SCAN_STARTED:
179 case SUPPLICANT_EVENT_SCAN_RESULTS:
180 case SUPPLICANT_EVENT_SCAN_FAILED:
181 case SUPPLICANT_EVENT_NETWORK_NOT_FOUND:
182 case SUPPLICANT_EVENT_NETWORK_ADDED:
183 case SUPPLICANT_EVENT_NETWORK_REMOVED:
184 strncpy(data->supplicant_event_str, event_info.event_str,
185 sizeof(data->supplicant_event_str) - 1);
186 event_data->data_len = strlen(data->supplicant_event_str) + 1;
187 case SUPPLICANT_EVENT_DSCP_POLICY:
188 /* TODO */
189 break;
190 default:
191 break;
192 }
193
194 if (ret <= 0) {
195 wpa_printf(MSG_ERROR, "%s Parse failed: %s",
196 event_info.event_str, strerror(errno));
197 }
198
199 return ret;
200 }
201
supplicant_send_wifi_mgmt_conn_event(void * ctx,int status_code)202 int supplicant_send_wifi_mgmt_conn_event(void *ctx, int status_code)
203 {
204 struct wpa_supplicant *wpa_s = ctx;
205 int status = wpas_to_wifi_mgmt_conn_status(status_code);
206 enum net_event_wifi_cmd event;
207
208 if (!wpa_s || !wpa_s->current_ssid) {
209 return -EINVAL;
210 }
211
212 if (wpa_s->current_ssid->mode == WPAS_MODE_AP) {
213 event = NET_EVENT_WIFI_CMD_AP_ENABLE_RESULT;
214 } else {
215 event = NET_EVENT_WIFI_CMD_CONNECT_RESULT;
216 }
217
218 return supplicant_send_wifi_mgmt_event(wpa_s->ifname,
219 event,
220 (void *)&status,
221 sizeof(int));
222 }
223
supplicant_send_wifi_mgmt_disc_event(void * ctx,int reason_code)224 int supplicant_send_wifi_mgmt_disc_event(void *ctx, int reason_code)
225 {
226 struct wpa_supplicant *wpa_s = ctx;
227 enum net_event_wifi_cmd event;
228 int status;
229
230 if (!wpa_s || !wpa_s->current_ssid) {
231 return -EINVAL;
232 }
233
234 if (wpa_s->wpa_state >= WPA_COMPLETED) {
235 /* Disconnect event code & status */
236 status = wpas_to_wifi_mgmt_disconn_status(reason_code);
237 if (wpa_s->current_ssid->mode == WPAS_MODE_AP) {
238 event = NET_EVENT_WIFI_CMD_AP_DISABLE_RESULT;
239 } else {
240 event = NET_EVENT_WIFI_CMD_DISCONNECT_RESULT;
241 }
242 } else {
243 /* Connect event code & status */
244 status = WIFI_STATUS_CONN_FAIL;
245 if (wpa_s->current_ssid->mode == WPAS_MODE_AP) {
246 event = NET_EVENT_WIFI_CMD_AP_ENABLE_RESULT;
247 } else {
248 event = NET_EVENT_WIFI_CMD_CONNECT_RESULT;
249 }
250 }
251
252 return supplicant_send_wifi_mgmt_event(wpa_s->ifname,
253 event,
254 (void *)&status,
255 sizeof(int));
256 }
257
258 #ifdef CONFIG_AP
get_sta_link_mode(struct wpa_supplicant * wpa_s,struct sta_info * sta)259 static enum wifi_link_mode get_sta_link_mode(struct wpa_supplicant *wpa_s, struct sta_info *sta)
260 {
261 if (sta->flags & WLAN_STA_HE) {
262 return WIFI_6;
263 } else if (sta->flags & WLAN_STA_VHT) {
264 return WIFI_5;
265 } else if (sta->flags & WLAN_STA_HT) {
266 return WIFI_4;
267 } else if (sta->flags & WLAN_STA_NONERP) {
268 return WIFI_1;
269 } else if (wpa_s->assoc_freq > 4000) {
270 return WIFI_2;
271 } else if (wpa_s->assoc_freq > 2000) {
272 return WIFI_3;
273 } else {
274 return WIFI_LINK_MODE_UNKNOWN;
275 }
276 }
277
is_twt_capable(struct wpa_supplicant * wpa_s,struct sta_info * sta)278 static bool is_twt_capable(struct wpa_supplicant *wpa_s, struct sta_info *sta)
279 {
280 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_11AX
281 return hostapd_get_he_twt_responder(wpa_s->ap_iface->bss[0], IEEE80211_MODE_AP);
282 #else
283 return false;
284 #endif
285 }
286
supplicant_send_wifi_mgmt_ap_status(void * ctx,enum net_event_wifi_cmd event,enum wifi_ap_status ap_status)287 int supplicant_send_wifi_mgmt_ap_status(void *ctx,
288 enum net_event_wifi_cmd event,
289 enum wifi_ap_status ap_status)
290 {
291 struct wpa_supplicant *wpa_s = ctx;
292 char *ifname = wpa_s->ifname;
293 int status = ap_status;
294
295 return supplicant_send_wifi_mgmt_event(ifname,
296 event,
297 (void *)&status,
298 sizeof(int));
299 }
300
supplicant_send_wifi_mgmt_ap_sta_event(void * ctx,enum net_event_wifi_cmd event,void * data)301 int supplicant_send_wifi_mgmt_ap_sta_event(void *ctx,
302 enum net_event_wifi_cmd event,
303 void *data)
304 {
305 struct sta_info *sta = data;
306 struct wpa_supplicant *ap_ctx = ctx;
307 char *ifname;
308 struct wifi_ap_sta_info sta_info = { 0 };
309
310 if (!ap_ctx || !sta) {
311 return -EINVAL;
312 }
313
314 ifname = ap_ctx->ifname;
315
316 memcpy(sta_info.mac, sta->addr, sizeof(sta_info.mac));
317
318 if (event == NET_EVENT_WIFI_CMD_AP_STA_CONNECTED) {
319 sta_info.link_mode = get_sta_link_mode(ap_ctx, sta);
320 sta_info.twt_capable = is_twt_capable(ap_ctx, sta);
321 }
322
323 return supplicant_send_wifi_mgmt_event(ifname,
324 event,
325 (void *)&sta_info,
326 sizeof(sta_info));
327 }
328 #endif /* CONFIG_AP */
329
supplicant_send_wifi_mgmt_event(const char * ifname,enum net_event_wifi_cmd event,void * supplicant_status,size_t len)330 int supplicant_send_wifi_mgmt_event(const char *ifname, enum net_event_wifi_cmd event,
331 void *supplicant_status, size_t len)
332 {
333 struct net_if *iface = net_if_get_by_index(net_if_get_by_name(ifname));
334 union supplicant_event_data data;
335 struct supplicant_int_event_data event_data;
336
337 if (!iface) {
338 wpa_printf(MSG_ERROR, "Could not find iface for %s", ifname);
339 return -ENODEV;
340 }
341
342 switch (event) {
343 case NET_EVENT_WIFI_CMD_CONNECT_RESULT:
344 wifi_mgmt_raise_connect_result_event(
345 iface,
346 *(int *)supplicant_status);
347 break;
348 case NET_EVENT_WIFI_CMD_DISCONNECT_RESULT:
349 wifi_mgmt_raise_disconnect_result_event(
350 iface,
351 *(int *)supplicant_status);
352 break;
353 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_ROAMING
354 case NET_EVENT_WIFI_CMD_SIGNAL_CHANGE:
355 net_mgmt_event_notify_with_info(NET_EVENT_WIFI_SIGNAL_CHANGE,
356 iface, NULL, 0);
357 break;
358 case NET_EVENT_WIFI_CMD_NEIGHBOR_REP_RECEIVED:
359 wifi_mgmt_raise_neighbor_rep_recv_event(
360 iface,
361 (char *)supplicant_status, len);
362 break;
363 case NET_EVENT_WIFI_CMD_NEIGHBOR_REP_COMPLETE:
364 net_mgmt_event_notify_with_info(NET_EVENT_WIFI_NEIGHBOR_REP_COMP,
365 iface, NULL, 0);
366 break;
367 #endif
368 #ifdef CONFIG_AP
369 case NET_EVENT_WIFI_CMD_AP_ENABLE_RESULT:
370 wifi_mgmt_raise_ap_enable_result_event(iface,
371 *(int *)supplicant_status);
372 break;
373 case NET_EVENT_WIFI_CMD_AP_DISABLE_RESULT:
374 wifi_mgmt_raise_ap_disable_result_event(iface,
375 *(int *)supplicant_status);
376 break;
377 case NET_EVENT_WIFI_CMD_AP_STA_CONNECTED:
378 wifi_mgmt_raise_ap_sta_connected_event(iface,
379 (struct wifi_ap_sta_info *)supplicant_status);
380 break;
381 case NET_EVENT_WIFI_CMD_AP_STA_DISCONNECTED:
382 wifi_mgmt_raise_ap_sta_disconnected_event(iface,
383 (struct wifi_ap_sta_info *)supplicant_status);
384 break;
385 #endif /* CONFIG_AP */
386 case NET_EVENT_WIFI_CMD_SUPPLICANT:
387 event_data.data = &data;
388 if (supplicant_process_status(&event_data, (char *)supplicant_status) > 0) {
389 net_mgmt_event_notify_with_info(NET_EVENT_SUPPLICANT_INT_EVENT,
390 iface, &event_data, sizeof(event_data));
391 }
392 break;
393 default:
394 wpa_printf(MSG_ERROR, "Unsupported event %d", event);
395 return -EINVAL;
396 }
397
398 return 0;
399 }
400
supplicant_generate_state_event(const char * ifname,enum net_event_supplicant_cmd event,int status)401 int supplicant_generate_state_event(const char *ifname,
402 enum net_event_supplicant_cmd event,
403 int status)
404 {
405 struct net_if *iface;
406
407 iface = net_if_get_by_index(net_if_get_by_name(ifname));
408 if (!iface) {
409 wpa_printf(MSG_ERROR, "Could not find iface for %s", ifname);
410 return -ENODEV;
411 }
412
413 switch (event) {
414 case NET_EVENT_SUPPLICANT_CMD_READY:
415 net_mgmt_event_notify(NET_EVENT_SUPPLICANT_READY, iface);
416 break;
417 case NET_EVENT_SUPPLICANT_CMD_NOT_READY:
418 net_mgmt_event_notify(NET_EVENT_SUPPLICANT_NOT_READY, iface);
419 break;
420 case NET_EVENT_SUPPLICANT_CMD_IFACE_ADDED:
421 net_mgmt_event_notify(NET_EVENT_SUPPLICANT_IFACE_ADDED, iface);
422 break;
423 case NET_EVENT_SUPPLICANT_CMD_IFACE_REMOVING:
424 net_mgmt_event_notify(NET_EVENT_SUPPLICANT_IFACE_REMOVING, iface);
425 break;
426 case NET_EVENT_SUPPLICANT_CMD_IFACE_REMOVED:
427 net_mgmt_event_notify_with_info(NET_EVENT_SUPPLICANT_IFACE_REMOVED,
428 iface, &status, sizeof(status));
429 break;
430 default:
431 wpa_printf(MSG_ERROR, "Unsupported event %d", event);
432 return -EINVAL;
433 }
434
435 return 0;
436 }
437