1 /*
2  * Copyright (c) 2024 Espressif Systems (Shanghai) Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/types.h>
10 
11 #include <zephyr/net/net_if.h>
12 #include <zephyr/net/wifi_mgmt.h>
13 #include <zephyr/net/wifi_nm.h>
14 #include <zephyr/net/icmp.h>
15 
16 #include "icmpv4.h"
17 
18 LOG_MODULE_REGISTER(wifi_test, LOG_LEVEL_INF);
19 
20 #include "net_private.h"
21 
22 K_SEM_DEFINE(wifi_event, 0, 1);
23 
24 #define WIFI_MGMT_EVENTS                                                                           \
25 	(NET_EVENT_WIFI_SCAN_DONE | NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_CONNECT_RESULT |   \
26 	 NET_EVENT_WIFI_DISCONNECT_RESULT)
27 
28 #define TEST_DATA "ICMP dummy data"
29 
30 static struct wifi_context {
31 	struct net_if *iface;
32 	uint32_t scan_result;
33 	bool connecting;
34 	int result;
35 	struct net_mgmt_event_callback wifi_mgmt_cb;
36 } wifi_ctx;
37 
38 extern char *net_sprint_ll_addr_buf(const uint8_t *ll, uint8_t ll_len, char *buf, int buflen);
39 
wifi_scan_result(struct net_mgmt_event_callback * cb)40 static void wifi_scan_result(struct net_mgmt_event_callback *cb)
41 {
42 	const struct wifi_scan_result *entry = (const struct wifi_scan_result *)cb->info;
43 	uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")];
44 	uint8_t ssid_print[WIFI_SSID_MAX_LEN + 1];
45 
46 	wifi_ctx.scan_result++;
47 
48 	if (wifi_ctx.scan_result == 1U) {
49 		printk("\n%-4s | %-32s %-5s | %-13s | %-4s | %-15s | %-17s | %-8s\n", "Num", "SSID",
50 		       "(len)", "Chan (Band)", "RSSI", "Security", "BSSID", "MFP");
51 	}
52 
53 	strncpy(ssid_print, entry->ssid, sizeof(ssid_print) - 1);
54 	ssid_print[sizeof(ssid_print) - 1] = '\0';
55 
56 	printk("%-4d | %-32s %-5u | %-4u (%-6s) | %-4d | %-15s | %-17s | %-8s\n",
57 	       wifi_ctx.scan_result, ssid_print, entry->ssid_length, entry->channel,
58 	       wifi_band_txt(entry->band), entry->rssi, wifi_security_txt(entry->security),
59 	       ((entry->mac_length) ? net_sprint_ll_addr_buf(entry->mac, WIFI_MAC_ADDR_LEN,
60 							     mac_string_buf, sizeof(mac_string_buf))
61 				    : ""),
62 	       wifi_mfp_txt(entry->mfp));
63 }
64 
wifi_connect_result(struct net_mgmt_event_callback * cb)65 static void wifi_connect_result(struct net_mgmt_event_callback *cb)
66 {
67 	const struct wifi_status *status = (const struct wifi_status *)cb->info;
68 
69 	wifi_ctx.result = status->status;
70 
71 	if (wifi_ctx.result) {
72 		LOG_INF("Connection request failed (%d)", wifi_ctx.result);
73 	} else {
74 		LOG_INF("Connected");
75 	}
76 }
77 
wifi_disconnect_result(struct net_mgmt_event_callback * cb)78 static void wifi_disconnect_result(struct net_mgmt_event_callback *cb)
79 {
80 	const struct wifi_status *status = (const struct wifi_status *)cb->info;
81 
82 	wifi_ctx.result = status->status;
83 
84 	if (!wifi_ctx.connecting) {
85 		if (wifi_ctx.result) {
86 			LOG_INF("Disconnect failed (%d)", wifi_ctx.result);
87 		} else {
88 			LOG_INF("Disconnected");
89 		}
90 	} else {
91 		/* Disconnect event while connecting is a failed attempt */
92 		wifi_ctx.result = WIFI_STATUS_CONN_FAIL;
93 	}
94 }
95 
wifi_mgmt_event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)96 static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
97 				    struct net_if *iface)
98 {
99 	switch (mgmt_event) {
100 	case NET_EVENT_WIFI_SCAN_RESULT:
101 		wifi_scan_result(cb);
102 		break;
103 	case NET_EVENT_WIFI_SCAN_DONE:
104 		k_sem_give(&wifi_event);
105 		break;
106 	case NET_EVENT_WIFI_CONNECT_RESULT:
107 		wifi_connect_result(cb);
108 		k_sem_give(&wifi_event);
109 		break;
110 	case NET_EVENT_WIFI_DISCONNECT_RESULT:
111 		wifi_disconnect_result(cb);
112 		k_sem_give(&wifi_event);
113 		break;
114 	default:
115 		break;
116 	}
117 }
118 
icmp_event(struct net_icmp_ctx * ctx,struct net_pkt * pkt,struct net_icmp_ip_hdr * hdr,struct net_icmp_hdr * icmp_hdr,void * user_data)119 static int icmp_event(struct net_icmp_ctx *ctx, struct net_pkt *pkt, struct net_icmp_ip_hdr *hdr,
120 		      struct net_icmp_hdr *icmp_hdr, void *user_data)
121 {
122 	struct net_ipv4_hdr *ip_hdr = hdr->ipv4;
123 	size_t hdr_offset = net_pkt_ip_hdr_len(pkt) + net_pkt_ip_opts_len(pkt) +
124 			    sizeof(struct net_icmp_hdr) + sizeof(struct net_icmpv4_echo_req);
125 	size_t data_len = net_pkt_get_len(pkt) - hdr_offset;
126 	char buf[50];
127 
128 	if (net_calc_chksum_icmpv4(pkt)) {
129 		/* checksum error */
130 		wifi_ctx.result = -EIO;
131 		goto sem_give;
132 	}
133 
134 	net_pkt_cursor_init(pkt);
135 	net_pkt_skip(pkt, hdr_offset);
136 	net_pkt_read(pkt, buf, MIN(data_len, sizeof(buf)));
137 
138 	LOG_INF("Received ICMP reply from %s", net_sprint_ipv4_addr(&ip_hdr->src));
139 	LOG_INF("Payload: '%s'", buf);
140 
141 	/* payload check */
142 	wifi_ctx.result = strcmp(buf, TEST_DATA);
143 
144 sem_give:
145 	k_sem_give(&wifi_event);
146 
147 	return 0;
148 }
149 
wifi_scan(void)150 static int wifi_scan(void)
151 {
152 	int ret = net_mgmt(NET_REQUEST_WIFI_SCAN, wifi_ctx.iface, NULL, 0);
153 
154 	if (ret) {
155 		LOG_INF("Scan request failed with error: %d", ret);
156 		return ret;
157 	}
158 
159 	LOG_INF("Wifi scan requested...");
160 
161 	return 0;
162 }
163 
wifi_connect(void)164 static int wifi_connect(void)
165 {
166 	struct wifi_connect_req_params params = {0};
167 	int ret;
168 
169 	/* Defaults */
170 	params.band = WIFI_FREQ_BAND_UNKNOWN;
171 	params.channel = WIFI_CHANNEL_ANY;
172 	params.mfp = WIFI_MFP_OPTIONAL;
173 
174 	/* Input parameters */
175 	params.ssid = CONFIG_WIFI_TEST_SSID;
176 	params.ssid_length = strlen(params.ssid);
177 #if defined(CONFIG_WIFI_TEST_AUTH_MODE_WPA2)
178 	params.security = WIFI_SECURITY_TYPE_PSK;
179 	params.psk = CONFIG_WIFI_TEST_PSK;
180 	params.psk_length = strlen(CONFIG_WIFI_TEST_PSK);
181 #elif defined(CONFIG_WIFI_TEST_AUTH_MODE_WPA3)
182 	params.security = WIFI_SECURITY_TYPE_SAE;
183 	params.sae_password = CONFIG_WIFI_TEST_PSK;
184 	params.sae_password_length = strlen(CONFIG_WIFI_TEST_PSK);
185 #else
186 	params.security = WIFI_SECURITY_TYPE_NONE;
187 #endif
188 
189 	ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, wifi_ctx.iface, &params,
190 		       sizeof(struct wifi_connect_req_params));
191 
192 	if (ret) {
193 		LOG_INF("Connection request failed with error: %d", ret);
194 		return ret;
195 	}
196 
197 	LOG_INF("Connection requested...");
198 
199 	return 0;
200 }
201 
wifi_disconnect(void)202 static int wifi_disconnect(void)
203 {
204 	int ret;
205 
206 	ret = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, wifi_ctx.iface, NULL, 0);
207 
208 	if (ret) {
209 		LOG_INF("Disconnect request failed with error: %d", ret);
210 		return ret;
211 	}
212 
213 	return 0;
214 }
215 
wifi_state(void)216 static int wifi_state(void)
217 {
218 	struct wifi_iface_status status = {0};
219 
220 	net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, wifi_ctx.iface, &status,
221 		 sizeof(struct wifi_iface_status));
222 
223 	return status.state;
224 }
225 
ZTEST(wifi,test_0_scan)226 ZTEST(wifi, test_0_scan)
227 {
228 	int ret;
229 
230 	ret = wifi_scan();
231 	zassert_equal(ret, 0, "Scan request failed");
232 
233 	zassert_equal(k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_SCAN_TIMEOUT)), 0,
234 		      "Wifi scan failed or timed out");
235 
236 	LOG_INF("Scan done");
237 }
238 
ZTEST(wifi,test_1_connect)239 ZTEST(wifi, test_1_connect)
240 {
241 	int ret;
242 	int retry = CONFIG_WIFI_CONNECT_ATTEMPTS;
243 
244 	/* Manage connect retry as disconnect event may happen */
245 	wifi_ctx.connecting = true;
246 
247 	do {
248 		ret = wifi_connect();
249 		zassert_equal(ret, 0, "Connect request failed");
250 
251 		zassert_equal(k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_CONNECT_TIMEOUT)), 0,
252 			      "Wifi connect timed out");
253 
254 		if (wifi_ctx.result) {
255 			zassert(--retry, "Connect failed");
256 			LOG_INF("Failed attempt, retry %d", CONFIG_WIFI_CONNECT_ATTEMPTS - retry);
257 			k_sleep(K_SECONDS(1));
258 		} else {
259 			break;
260 		}
261 	} while (retry);
262 
263 	wifi_ctx.connecting = false;
264 
265 	/* Check interface state */
266 	int state = wifi_state();
267 
268 	LOG_INF("Interface state: %s", wifi_state_txt(state));
269 
270 	zassert_equal(state, WIFI_STATE_COMPLETED, "Interface state check failed");
271 }
272 
ZTEST(wifi,test_2_icmp)273 ZTEST(wifi, test_2_icmp)
274 {
275 	struct net_icmp_ping_params params;
276 	struct net_icmp_ctx icmp_ctx;
277 	struct in_addr gw_addr_4;
278 	struct sockaddr_in dst4 = {0};
279 	int retry = CONFIG_WIFI_PING_ATTEMPTS;
280 	int ret;
281 
282 	gw_addr_4 = net_if_ipv4_get_gw(wifi_ctx.iface);
283 	zassert_not_equal(gw_addr_4.s_addr, 0, "Gateway address is not set");
284 
285 	ret = net_icmp_init_ctx(&icmp_ctx, NET_ICMPV4_ECHO_REPLY, 0, icmp_event);
286 	zassert_equal(ret, 0, "Cannot init ICMP (%d)", ret);
287 
288 	dst4.sin_family = AF_INET;
289 	memcpy(&dst4.sin_addr, &gw_addr_4, sizeof(gw_addr_4));
290 
291 	params.identifier = 1234;
292 	params.sequence = 5678;
293 	params.tc_tos = 1;
294 	params.priority = 2;
295 	params.data = TEST_DATA;
296 	params.data_size = sizeof(TEST_DATA);
297 
298 	LOG_INF("Pinging the gateway...");
299 
300 	do {
301 		ret = net_icmp_send_echo_request(&icmp_ctx, wifi_ctx.iface,
302 						 (struct sockaddr *)&dst4, &params, NULL);
303 		zassert_equal(ret, 0, "Cannot send ICMP echo request (%d)", ret);
304 
305 		int timeout = k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_PING_TIMEOUT));
306 
307 		if (timeout) {
308 			zassert(--retry, "Gateway ping (ICMP) timed out on all attempts");
309 			LOG_INF("No reply, retry %d", CONFIG_WIFI_PING_ATTEMPTS - retry);
310 		} else {
311 			break;
312 		}
313 	} while (retry);
314 
315 	/* check result */
316 	zassert_equal(wifi_ctx.result, 0, "ICMP data error");
317 
318 	net_icmp_cleanup_ctx(&icmp_ctx);
319 }
320 
ZTEST(wifi,test_3_disconnect)321 ZTEST(wifi, test_3_disconnect)
322 {
323 	int ret;
324 
325 	ret = wifi_disconnect();
326 	zassert_equal(ret, 0, "Disconect request failed");
327 
328 	zassert_equal(k_sem_take(&wifi_event, K_SECONDS(CONFIG_WIFI_DISCONNECT_TIMEOUT)), 0,
329 		      "Wifi disconnect timed out");
330 
331 	zassert_equal(wifi_ctx.result, 0, "Disconnect failed");
332 }
333 
wifi_setup(void)334 static void *wifi_setup(void)
335 {
336 	wifi_ctx.iface = net_if_get_wifi_sta();
337 
338 	net_mgmt_init_event_callback(&wifi_ctx.wifi_mgmt_cb, wifi_mgmt_event_handler,
339 				     WIFI_MGMT_EVENTS);
340 	net_mgmt_add_event_callback(&wifi_ctx.wifi_mgmt_cb);
341 
342 	/* reset semaphore that tracks wifi events */
343 	k_sem_reset(&wifi_event);
344 
345 	return NULL;
346 }
347 
348 ZTEST_SUITE(wifi, NULL, wifi_setup, NULL, NULL, NULL);
349