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, ¶ms,
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, ¶ms, 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