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