1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(wifi_supplicant, CONFIG_WIFI_NM_WPA_SUPPLICANT_LOG_LEVEL);
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/init.h>
12 #include <poll.h>
13
14 #if !defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE) && !defined(CONFIG_MBEDTLS_ENABLE_HEAP)
15 #include <mbedtls/platform.h>
16 #endif /* !CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE && !CONFIG_MBEDTLS_ENABLE_HEAP */
17 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA
18 #include "supp_psa_api.h"
19 #endif
20
21 #include <zephyr/net/wifi_mgmt.h>
22 #include <zephyr/net/wifi_nm.h>
23 #include <zephyr/net/socket.h>
24
25 static K_THREAD_STACK_DEFINE(supplicant_thread_stack,
26 CONFIG_WIFI_NM_WPA_SUPPLICANT_THREAD_STACK_SIZE);
27 static struct k_thread tid;
28
29 static K_THREAD_STACK_DEFINE(iface_wq_stack, CONFIG_WIFI_NM_WPA_SUPPLICANT_WQ_STACK_SIZE);
30
31 #define IFACE_NOTIFY_TIMEOUT_MS 1000
32 #define IFACE_NOTIFY_RETRY_MS 10
33
34 #include "supp_main.h"
35 #include "supp_api.h"
36 #include "supp_events.h"
37
38 #include "includes.h"
39 #include "common.h"
40 #include "eloop.h"
41 #include "wpa_supplicant/config.h"
42 #include "wpa_supplicant_i.h"
43 #include "fst/fst.h"
44 #include "includes.h"
45 #include "wpa_cli_zephyr.h"
46
47 static const struct wifi_mgmt_ops mgmt_ops = {
48 .scan = supplicant_scan,
49 .connect = supplicant_connect,
50 .disconnect = supplicant_disconnect,
51 .iface_status = supplicant_status,
52 #ifdef CONFIG_NET_STATISTICS_WIFI
53 .get_stats = supplicant_get_stats,
54 #endif
55 .set_power_save = supplicant_set_power_save,
56 .set_twt = supplicant_set_twt,
57 .get_power_save_config = supplicant_get_power_save_config,
58 .reg_domain = supplicant_reg_domain,
59 .mode = supplicant_mode,
60 .filter = supplicant_filter,
61 .channel = supplicant_channel,
62 #ifdef CONFIG_AP
63 .ap_enable = supplicant_ap_enable,
64 .ap_disable = supplicant_ap_disable,
65 .ap_sta_disconnect = supplicant_ap_sta_disconnect,
66 #endif /* CONFIG_AP */
67 };
68
69 DEFINE_WIFI_NM_INSTANCE(wifi_supplicant, &mgmt_ops);
70
71 #define WRITE_TIMEOUT 100 /* ms */
72 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_INF_MON
73 #define INTERFACE_EVENT_MASK (NET_EVENT_IF_ADMIN_UP | NET_EVENT_IF_ADMIN_DOWN)
74 #endif
75 struct supplicant_context {
76 struct wpa_global *supplicant;
77 struct net_mgmt_event_callback cb;
78 struct net_if *iface;
79 char if_name[CONFIG_NET_INTERFACE_NAME_LEN + 1];
80 int event_socketpair[2];
81 struct k_work iface_work;
82 struct k_work_q iface_wq;
83 int (*iface_handler)(struct supplicant_context *ctx, struct net_if *iface);
84 };
85
get_default_context(void)86 static struct supplicant_context *get_default_context(void)
87 {
88 static struct supplicant_context ctx;
89
90 return &ctx;
91 }
92
zephyr_get_default_supplicant_context(void)93 struct wpa_global *zephyr_get_default_supplicant_context(void)
94 {
95 return get_default_context()->supplicant;
96 }
97
get_workq(void)98 struct k_work_q *get_workq(void)
99 {
100 return &get_default_context()->iface_wq;
101 }
102
zephyr_wifi_send_event(const struct wpa_supplicant_event_msg * msg)103 int zephyr_wifi_send_event(const struct wpa_supplicant_event_msg *msg)
104 {
105 struct supplicant_context *ctx;
106 struct pollfd fds[1];
107 int ret;
108
109 /* TODO: Fix this to get the correct container */
110 ctx = get_default_context();
111
112 if (ctx->event_socketpair[1] < 0) {
113 ret = -ENOENT;
114 goto out;
115 }
116
117 fds[0].fd = ctx->event_socketpair[0];
118 fds[0].events = POLLOUT;
119 fds[0].revents = 0;
120
121 ret = zsock_poll(fds, 1, WRITE_TIMEOUT);
122 if (ret < 0) {
123 ret = -errno;
124 LOG_ERR("Cannot write event (%d)", ret);
125 goto out;
126 }
127
128 ret = zsock_send(ctx->event_socketpair[1], msg, sizeof(*msg), 0);
129 if (ret < 0) {
130 ret = -errno;
131 LOG_WRN("Event send failed (%d)", ret);
132 goto out;
133 }
134
135 if (ret != sizeof(*msg)) {
136 ret = -EMSGSIZE;
137 LOG_WRN("Event partial send (%d)", ret);
138 goto out;
139 }
140
141 ret = 0;
142
143 out:
144 return ret;
145 }
146
147 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_INF_MON
send_event(const struct wpa_supplicant_event_msg * msg)148 static int send_event(const struct wpa_supplicant_event_msg *msg)
149 {
150 return zephyr_wifi_send_event(msg);
151 }
152
is_wanted_interface(struct net_if * iface)153 static bool is_wanted_interface(struct net_if *iface)
154 {
155 if (!net_if_is_wifi(iface)) {
156 return false;
157 }
158
159 /* TODO: check against a list of valid interfaces */
160
161 return true;
162 }
163 #endif
zephyr_get_handle_by_ifname(const char * ifname)164 struct wpa_supplicant *zephyr_get_handle_by_ifname(const char *ifname)
165 {
166 struct wpa_supplicant *wpa_s = NULL;
167 struct supplicant_context *ctx = get_default_context();
168
169 wpa_s = wpa_supplicant_get_iface(ctx->supplicant, ifname);
170 if (!wpa_s) {
171 wpa_printf(MSG_ERROR, "%s: Unable to get wpa_s handle for %s\n", __func__, ifname);
172 return NULL;
173 }
174
175 return wpa_s;
176 }
177
get_iface_count(struct supplicant_context * ctx)178 static int get_iface_count(struct supplicant_context *ctx)
179 {
180 /* FIXME, should not access ifaces as it is supplicant internal data */
181 struct wpa_supplicant *wpa_s;
182 unsigned int count = 0;
183
184 for (wpa_s = ctx->supplicant->ifaces; wpa_s; wpa_s = wpa_s->next) {
185 count += 1;
186 }
187
188 return count;
189 }
190
add_interface(struct supplicant_context * ctx,struct net_if * iface)191 static int add_interface(struct supplicant_context *ctx, struct net_if *iface)
192 {
193 struct wpa_supplicant *wpa_s;
194 char ifname[IFNAMSIZ + 1] = { 0 };
195 int ret, retry = 0, count = IFACE_NOTIFY_TIMEOUT_MS / IFACE_NOTIFY_RETRY_MS;
196
197 ret = net_if_get_name(iface, ifname, sizeof(ifname) - 1);
198 if (ret < 0) {
199 LOG_ERR("Cannot get interface %d (%p) name", net_if_get_by_iface(iface), iface);
200 goto out;
201 }
202
203 LOG_DBG("Adding interface %s [%d] (%p)", ifname, net_if_get_by_iface(iface), iface);
204
205 ret = zephyr_wpa_cli_global_cmd_v("interface_add %s %s %s %s",
206 ifname, "zephyr", "zephyr", "zephyr");
207 if (ret) {
208 LOG_ERR("Failed to add interface %s", ifname);
209 goto out;
210 }
211
212 while (retry++ < count && !wpa_supplicant_get_iface(ctx->supplicant, ifname)) {
213 k_sleep(K_MSEC(IFACE_NOTIFY_RETRY_MS));
214 }
215
216 wpa_s = wpa_supplicant_get_iface(ctx->supplicant, ifname);
217 if (wpa_s == NULL) {
218 LOG_ERR("Failed to add iface %s", ifname);
219 goto out;
220 }
221
222 wpa_s->conf->filter_ssids = 1;
223 wpa_s->conf->ap_scan = 1;
224
225 /* Default interface, kick start supplicant */
226 if (get_iface_count(ctx) > 0) {
227 ctx->iface = iface;
228 net_if_get_name(iface, ctx->if_name, CONFIG_NET_INTERFACE_NAME_LEN);
229 }
230
231 ret = zephyr_wpa_ctrl_init(wpa_s);
232 if (ret) {
233 LOG_ERR("Failed to initialize supplicant control interface");
234 goto out;
235 }
236
237 ret = wifi_nm_register_mgd_iface(wifi_nm_get_instance("wifi_supplicant"), iface);
238 if (ret) {
239 LOG_ERR("Failed to register mgd iface with native stack %s (%d)",
240 ifname, ret);
241 goto out;
242 }
243
244 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_IFACE_ADDED, 0);
245
246 if (get_iface_count(ctx) == 1) {
247 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_READY, 0);
248 }
249
250 ret = 0;
251
252 out:
253 return ret;
254 }
255 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_INF_MON
del_interface(struct supplicant_context * ctx,struct net_if * iface)256 static int del_interface(struct supplicant_context *ctx, struct net_if *iface)
257 {
258 struct wpa_supplicant_event_msg msg;
259 struct wpa_supplicant *wpa_s;
260 union wpa_event_data *event = NULL;
261 int ret, retry = 0, count = IFACE_NOTIFY_TIMEOUT_MS / IFACE_NOTIFY_RETRY_MS;
262 char ifname[IFNAMSIZ + 1] = { 0 };
263
264 ret = net_if_get_name(iface, ifname, sizeof(ifname) - 1);
265 if (ret < 0) {
266 LOG_ERR("Cannot get interface %d (%p) name", net_if_get_by_iface(iface), iface);
267 goto out;
268 }
269
270 LOG_DBG("Removing interface %s %d (%p)", ifname, net_if_get_by_iface(iface), iface);
271
272 event = os_zalloc(sizeof(*event));
273 if (!event) {
274 ret = -ENOMEM;
275 LOG_ERR("Failed to allocate event data");
276 goto out;
277 }
278
279 wpa_s = wpa_supplicant_get_iface(ctx->supplicant, ifname);
280 if (!wpa_s) {
281 ret = -ENOENT;
282 LOG_ERR("Failed to get wpa_s handle for %s", ifname);
283 goto out;
284 }
285
286 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_IFACE_REMOVING, 0);
287
288 if (sizeof(event->interface_status.ifname) < strlen(ifname)) {
289 wpa_printf(MSG_ERROR, "Interface name too long: %s (max: %d)",
290 ifname, sizeof(event->interface_status.ifname));
291 goto out;
292 }
293
294 os_memcpy(event->interface_status.ifname, ifname, strlen(ifname));
295 event->interface_status.ievent = EVENT_INTERFACE_REMOVED;
296
297 msg.global = true;
298 msg.ctx = ctx->supplicant;
299 msg.event = EVENT_INTERFACE_STATUS;
300 msg.data = event;
301
302 ret = send_event(&msg);
303 if (ret) {
304 /* We failed notify WPA supplicant about interface removal.
305 * There is not much we can do, interface is still registered
306 * with WPA supplicant so we cannot unregister NM etc.
307 */
308 wpa_printf(MSG_ERROR, "Failed to send event: %d", ret);
309 goto out;
310 }
311
312 while (retry++ < count && wpa_s->wpa_state != WPA_INTERFACE_DISABLED) {
313 k_sleep(K_MSEC(IFACE_NOTIFY_RETRY_MS));
314 }
315
316 if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) {
317 LOG_ERR("Failed to notify remove interface %s", ifname);
318 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_IFACE_REMOVED, -1);
319 goto out;
320 }
321
322 zephyr_wpa_ctrl_deinit(wpa_s);
323
324 ret = zephyr_wpa_cli_global_cmd_v("interface_remove %s", ifname);
325 if (ret) {
326 LOG_ERR("Failed to remove interface %s", ifname);
327 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_IFACE_REMOVED,
328 -EINVAL);
329 goto out;
330 }
331
332 ret = wifi_nm_unregister_mgd_iface(wifi_nm_get_instance("wpa_supplicant"), iface);
333 if (ret) {
334 LOG_ERR("Failed to unregister mgd iface %s with native stack (%d)",
335 ifname, ret);
336 goto out;
337 }
338
339 if (get_iface_count(ctx) == 0) {
340 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_NOT_READY, 0);
341 }
342
343 supplicant_generate_state_event(ifname, NET_EVENT_SUPPLICANT_CMD_IFACE_REMOVED, 0);
344
345 out:
346 if (event) {
347 os_free(event);
348 }
349
350 return ret;
351 }
352 #endif
iface_work_handler(struct k_work * work)353 static void iface_work_handler(struct k_work *work)
354 {
355 struct supplicant_context *ctx = CONTAINER_OF(work, struct supplicant_context,
356 iface_work);
357 int ret;
358
359 ret = (*ctx->iface_handler)(ctx, ctx->iface);
360 if (ret < 0) {
361 LOG_ERR("Interface %d (%p) handler failed (%d)",
362 net_if_get_by_iface(ctx->iface), ctx->iface, ret);
363 }
364 }
365
366 /* As the mgmt thread stack is limited, use a separate work queue for any network
367 * interface add/delete.
368 */
submit_iface_work(struct supplicant_context * ctx,struct net_if * iface,int (* handler)(struct supplicant_context * ctx,struct net_if * iface))369 static void submit_iface_work(struct supplicant_context *ctx,
370 struct net_if *iface,
371 int (*handler)(struct supplicant_context *ctx,
372 struct net_if *iface))
373 {
374 ctx->iface_handler = handler;
375
376 k_work_submit_to_queue(&ctx->iface_wq, &ctx->iface_work);
377 }
378 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_INF_MON
interface_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)379 static void interface_handler(struct net_mgmt_event_callback *cb,
380 uint32_t mgmt_event, struct net_if *iface)
381 {
382 struct supplicant_context *ctx = CONTAINER_OF(cb, struct supplicant_context,
383 cb);
384
385 if ((mgmt_event & INTERFACE_EVENT_MASK) != mgmt_event) {
386 return;
387 }
388
389 if (!is_wanted_interface(iface)) {
390 LOG_DBG("Ignoring event (0x%02x) from interface %d (%p)",
391 mgmt_event, net_if_get_by_iface(iface), iface);
392 return;
393 }
394
395 if (mgmt_event == NET_EVENT_IF_ADMIN_UP) {
396 LOG_INF("Network interface %d (%p) up", net_if_get_by_iface(iface), iface);
397 submit_iface_work(ctx, iface, add_interface);
398 return;
399 }
400
401 if (mgmt_event == NET_EVENT_IF_ADMIN_DOWN) {
402 LOG_INF("Network interface %d (%p) down", net_if_get_by_iface(iface), iface);
403 submit_iface_work(ctx, iface, del_interface);
404 return;
405 }
406 }
407 #endif
408
iface_cb(struct net_if * iface,void * user_data)409 static void iface_cb(struct net_if *iface, void *user_data)
410 {
411 struct supplicant_context *ctx = user_data;
412 int ret;
413
414 if (!net_if_is_wifi(iface)) {
415 return;
416 }
417
418 if (!net_if_is_admin_up(iface)) {
419 return;
420 }
421
422 ret = add_interface(ctx, iface);
423 if (ret < 0) {
424 return;
425 }
426 }
427
setup_interface_monitoring(struct supplicant_context * ctx,struct net_if * iface)428 static int setup_interface_monitoring(struct supplicant_context *ctx, struct net_if *iface)
429 {
430 ARG_UNUSED(iface);
431 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_INF_MON
432 net_mgmt_init_event_callback(&ctx->cb, interface_handler,
433 INTERFACE_EVENT_MASK);
434 net_mgmt_add_event_callback(&ctx->cb);
435 #endif
436 net_if_foreach(iface_cb, ctx);
437
438 return 0;
439 }
440
event_socket_handler(int sock,void * eloop_ctx,void * user_data)441 static void event_socket_handler(int sock, void *eloop_ctx, void *user_data)
442 {
443 struct supplicant_context *ctx = user_data;
444 struct wpa_supplicant_event_msg msg;
445 int ret;
446
447 ARG_UNUSED(eloop_ctx);
448 ARG_UNUSED(ctx);
449
450 ret = zsock_recv(sock, &msg, sizeof(msg), 0);
451 if (ret < 0) {
452 LOG_ERR("Failed to recv the message (%d)", -errno);
453 return;
454 }
455
456 if (ret != sizeof(msg)) {
457 LOG_ERR("Received incomplete message: got: %d, expected:%d",
458 ret, sizeof(msg));
459 return;
460 }
461
462 LOG_DBG("Passing message %d to wpa_supplicant", msg.event);
463
464 if (msg.global) {
465 wpa_supplicant_event_global(msg.ctx, msg.event, msg.data);
466 } else {
467 wpa_supplicant_event(msg.ctx, msg.event, msg.data);
468 }
469
470 if (msg.data) {
471 union wpa_event_data *data = msg.data;
472
473 /* Free up deep copied data */
474 if (msg.event == EVENT_AUTH) {
475 os_free((char *)data->auth.ies);
476 } else if (msg.event == EVENT_RX_MGMT) {
477 os_free((char *)data->rx_mgmt.frame);
478 } else if (msg.event == EVENT_TX_STATUS) {
479 os_free((char *)data->tx_status.data);
480 } else if (msg.event == EVENT_ASSOC) {
481 os_free((char *)data->assoc_info.addr);
482 os_free((char *)data->assoc_info.req_ies);
483 os_free((char *)data->assoc_info.resp_ies);
484 os_free((char *)data->assoc_info.resp_frame);
485 } else if (msg.event == EVENT_ASSOC_REJECT) {
486 os_free((char *)data->assoc_reject.bssid);
487 os_free((char *)data->assoc_reject.resp_ies);
488 } else if (msg.event == EVENT_DEAUTH) {
489 os_free((char *)data->deauth_info.addr);
490 os_free((char *)data->deauth_info.ie);
491 } else if (msg.event == EVENT_DISASSOC) {
492 os_free((char *)data->disassoc_info.addr);
493 os_free((char *)data->disassoc_info.ie);
494 } else if (msg.event == EVENT_UNPROT_DEAUTH) {
495 os_free((char *)data->unprot_deauth.sa);
496 os_free((char *)data->unprot_deauth.da);
497 } else if (msg.event == EVENT_UNPROT_DISASSOC) {
498 os_free((char *)data->unprot_disassoc.sa);
499 os_free((char *)data->unprot_disassoc.da);
500 }
501
502 os_free(msg.data);
503 }
504 }
505
register_supplicant_event_socket(struct supplicant_context * ctx)506 static int register_supplicant_event_socket(struct supplicant_context *ctx)
507 {
508 int ret;
509
510 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->event_socketpair);
511 if (ret < 0) {
512 ret = -errno;
513 LOG_ERR("Failed to initialize socket (%d)", ret);
514 return ret;
515 }
516
517 eloop_register_read_sock(ctx->event_socketpair[0], event_socket_handler, NULL, ctx);
518
519 return 0;
520 }
521
handler(void)522 static void handler(void)
523 {
524 struct supplicant_context *ctx;
525 struct wpa_params params;
526
527 #if !defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE) && !defined(CONFIG_MBEDTLS_ENABLE_HEAP)
528 /* Needed for crypto operation as default is no-op and fails */
529 mbedtls_platform_set_calloc_free(calloc, free);
530 #endif /* !CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE && !CONFIG_MBEDTLS_ENABLE_HEAP */
531
532 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA
533 supp_psa_crypto_init();
534 #endif
535
536 ctx = get_default_context();
537
538 k_work_queue_init(&ctx->iface_wq);
539 k_work_queue_start(&ctx->iface_wq, iface_wq_stack,
540 K_THREAD_STACK_SIZEOF(iface_wq_stack),
541 CONFIG_WIFI_NM_WPA_SUPPLICANT_WQ_PRIO,
542 NULL);
543
544 k_work_init(&ctx->iface_work, iface_work_handler);
545
546 memset(¶ms, 0, sizeof(params));
547 params.wpa_debug_level = CONFIG_WIFI_NM_WPA_SUPPLICANT_DEBUG_LEVEL;
548
549 ctx->supplicant = wpa_supplicant_init(¶ms);
550 if (ctx->supplicant == NULL) {
551 LOG_ERR("Failed to initialize %s", "wpa_supplicant");
552 goto err;
553 }
554
555 LOG_INF("%s initialized", "wpa_supplicant");
556
557 if (fst_global_init()) {
558 LOG_ERR("Failed to initialize %s", "FST");
559 goto out;
560 }
561
562 #if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
563 if (!fst_global_add_ctrl(fst_ctrl_cli)) {
564 LOG_WRN("Failed to add CLI FST ctrl");
565 }
566 #endif
567 zephyr_global_wpa_ctrl_init();
568
569 register_supplicant_event_socket(ctx);
570
571 submit_iface_work(ctx, NULL, setup_interface_monitoring);
572
573 (void)wpa_supplicant_run(ctx->supplicant);
574
575 supplicant_generate_state_event(ctx->if_name, NET_EVENT_SUPPLICANT_CMD_NOT_READY, 0);
576
577 eloop_unregister_read_sock(ctx->event_socketpair[0]);
578
579 zephyr_global_wpa_ctrl_deinit();
580
581 fst_global_deinit();
582
583 out:
584 wpa_supplicant_deinit(ctx->supplicant);
585
586 zsock_close(ctx->event_socketpair[0]);
587 zsock_close(ctx->event_socketpair[1]);
588
589 err:
590 os_free(params.pid_file);
591 }
592
init(void)593 static int init(void)
594 {
595 /* We create a thread that handles all supplicant connections */
596 k_thread_create(&tid, supplicant_thread_stack,
597 K_THREAD_STACK_SIZEOF(supplicant_thread_stack),
598 (k_thread_entry_t)handler, NULL, NULL, NULL,
599 0, 0, K_NO_WAIT);
600
601 return 0;
602 }
603
604 SYS_INIT(init, APPLICATION, 0);
605