1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #undef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 200809L /* For strnlen() */
9
10 #include <zephyr/kernel.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <zephyr/shell/shell.h>
14 #include <zephyr/init.h>
15
16 #include <zephyr/net/net_if.h>
17 #include <zephyr/net/wifi_mgmt.h>
18 #include <zephyr/net/net_event.h>
19 #include <zephyr/net/net_l2.h>
20 #include <zephyr/net/ethernet.h>
21
22 #include <zephyr/net/wifi_credentials.h>
23
24 #define MACSTR "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
25
print_network_info(void * cb_arg,const char * ssid,size_t ssid_len)26 static void print_network_info(void *cb_arg, const char *ssid, size_t ssid_len)
27 {
28 int ret = 0;
29 struct wifi_credentials_personal creds = {0};
30 const struct shell *sh = (const struct shell *)cb_arg;
31
32 ret = wifi_credentials_get_by_ssid_personal_struct(ssid, ssid_len, &creds);
33 if (ret) {
34 shell_error(sh,
35 "An error occurred when trying to load credentials for network \"%.*s\""
36 ". err: %d",
37 (int)ssid_len, ssid, ret);
38 return;
39 }
40
41 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT,
42 " network ssid: \"%.*s\", ssid_len: %d, type: %s", (int)ssid_len, ssid,
43 ssid_len, wifi_security_txt(creds.header.type));
44
45 if (creds.header.type == WIFI_SECURITY_TYPE_PSK ||
46 creds.header.type == WIFI_SECURITY_TYPE_PSK_SHA256 ||
47 creds.header.type == WIFI_SECURITY_TYPE_SAE ||
48 creds.header.type == WIFI_SECURITY_TYPE_WPA_PSK) {
49 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT,
50 ", password: \"%.*s\", password_len: %d", (int)creds.password_len,
51 creds.password, creds.password_len);
52 }
53
54 if (creds.header.flags & WIFI_CREDENTIALS_FLAG_BSSID) {
55 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", bssid: " MACSTR,
56 creds.header.bssid[0], creds.header.bssid[1], creds.header.bssid[2],
57 creds.header.bssid[3], creds.header.bssid[4], creds.header.bssid[5]);
58 }
59
60 if (creds.header.flags & WIFI_CREDENTIALS_FLAG_2_4GHz) {
61 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", band: 2.4GHz");
62 }
63
64 if (creds.header.flags & WIFI_CREDENTIALS_FLAG_5GHz) {
65 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", band: 5GHz");
66 }
67
68 if (creds.header.channel) {
69 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", channel: %d", creds.header.channel);
70 }
71
72 if (creds.header.flags & WIFI_CREDENTIALS_FLAG_FAVORITE) {
73 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", favorite");
74 }
75
76 if (creds.header.flags & WIFI_CREDENTIALS_FLAG_MFP_REQUIRED) {
77 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", MFP_REQUIRED");
78 } else if (creds.header.flags & WIFI_CREDENTIALS_FLAG_MFP_DISABLED) {
79 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", MFP_DISABLED");
80 } else {
81 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", MFP_OPTIONAL");
82 }
83
84 if (creds.header.timeout) {
85 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", timeout: %d", creds.header.timeout);
86 }
87
88 shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, "\n");
89 }
90
parse_sec_type(const char * s)91 static enum wifi_security_type parse_sec_type(const char *s)
92 {
93 if (strcmp("OPEN", s) == 0) {
94 return WIFI_SECURITY_TYPE_NONE;
95 }
96
97 if (strcmp("WPA2-PSK", s) == 0) {
98 return WIFI_SECURITY_TYPE_PSK;
99 }
100
101 if (strcmp("WPA2-PSK-SHA256", s) == 0) {
102 return WIFI_SECURITY_TYPE_PSK_SHA256;
103 }
104
105 if (strcmp("WPA3-SAE", s) == 0) {
106 return WIFI_SECURITY_TYPE_SAE;
107 }
108
109 if (strcmp("WPA-PSK", s) == 0) {
110 return WIFI_SECURITY_TYPE_WPA_PSK;
111 }
112
113 return WIFI_SECURITY_TYPE_UNKNOWN;
114 }
115
parse_band(const char * s)116 static enum wifi_frequency_bands parse_band(const char *s)
117 {
118 if (strcmp("2.4GHz", s) == 0) {
119 return WIFI_FREQ_BAND_2_4_GHZ;
120 }
121
122 if (strcmp("5GHz", s) == 0) {
123 return WIFI_FREQ_BAND_5_GHZ;
124 }
125
126 if (strcmp("6GHz", s) == 0) {
127 return WIFI_FREQ_BAND_6_GHZ;
128 }
129
130 return WIFI_FREQ_BAND_UNKNOWN;
131 }
132
cmd_add_network(const struct shell * sh,size_t argc,char * argv[])133 static int cmd_add_network(const struct shell *sh, size_t argc, char *argv[])
134 {
135 int ret;
136
137 if (argc < 3) {
138 goto help;
139 }
140
141 if (strnlen(argv[1], WIFI_SSID_MAX_LEN + 1) > WIFI_SSID_MAX_LEN) {
142 shell_error(sh, "SSID too long");
143 goto help;
144 }
145
146 struct wifi_credentials_personal creds = {
147 .header.ssid_len = strlen(argv[1]),
148 .header.type = parse_sec_type(argv[2]),
149 };
150
151 memcpy(creds.header.ssid, argv[1], creds.header.ssid_len);
152
153 if (creds.header.type == WIFI_SECURITY_TYPE_UNKNOWN) {
154 shell_error(sh, "Cannot parse security type");
155 goto help;
156 }
157
158 size_t arg_idx = 3;
159
160 if (creds.header.type == WIFI_SECURITY_TYPE_PSK ||
161 creds.header.type == WIFI_SECURITY_TYPE_PSK_SHA256 ||
162 creds.header.type == WIFI_SECURITY_TYPE_SAE ||
163 creds.header.type == WIFI_SECURITY_TYPE_WPA_PSK) {
164 /* parse passphrase */
165 if (argc < 4) {
166 shell_error(sh, "Missing password");
167 goto help;
168 }
169 creds.password_len = strlen(argv[3]);
170 if (creds.password_len < WIFI_PSK_MIN_LEN) {
171 shell_error(sh, "Passphrase should be minimum %d characters",
172 WIFI_PSK_MIN_LEN);
173 goto help;
174 }
175 if ((creds.password_len > CONFIG_WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH &&
176 creds.header.type == WIFI_SECURITY_TYPE_SAE) ||
177 (creds.password_len > WIFI_PSK_MAX_LEN &&
178 creds.header.type != WIFI_SECURITY_TYPE_SAE)) {
179 shell_error(sh, "Password is too long for this security type");
180 goto help;
181 }
182 memcpy(creds.password, argv[3], creds.password_len);
183 ++arg_idx;
184 }
185
186 if (arg_idx < argc) {
187 /* look for bssid */
188 ret = sscanf(argv[arg_idx], MACSTR, &creds.header.bssid[0], &creds.header.bssid[1],
189 &creds.header.bssid[2], &creds.header.bssid[3], &creds.header.bssid[4],
190 &creds.header.bssid[5]);
191 if (ret == 6) {
192 creds.header.flags |= WIFI_CREDENTIALS_FLAG_BSSID;
193 ++arg_idx;
194 }
195 }
196
197 if (arg_idx < argc) {
198 /* look for band */
199 enum wifi_frequency_bands band = parse_band(argv[arg_idx]);
200
201 if (band == WIFI_FREQ_BAND_2_4_GHZ) {
202 creds.header.flags |= WIFI_CREDENTIALS_FLAG_2_4GHz;
203 ++arg_idx;
204 }
205 if (band == WIFI_FREQ_BAND_5_GHZ) {
206 creds.header.flags |= WIFI_CREDENTIALS_FLAG_5GHz;
207 ++arg_idx;
208 }
209 }
210
211 if (arg_idx < argc) {
212 /* look for channel */
213 char *end;
214
215 creds.header.channel = strtol(argv[arg_idx], &end, 10);
216 if (*end == '\0') {
217 ++arg_idx;
218 }
219 }
220
221 if (arg_idx < argc) {
222 /* look for favorite flag */
223 if (strncmp("favorite", argv[arg_idx], strlen("favorite")) == 0) {
224 creds.header.flags |= WIFI_CREDENTIALS_FLAG_FAVORITE;
225 ++arg_idx;
226 }
227 }
228
229 if (arg_idx < argc) {
230 /* look for mfp_disabled flag */
231 if (strncmp("mfp_disabled", argv[arg_idx], strlen("mfp_disabled")) == 0) {
232 creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_DISABLED;
233 ++arg_idx;
234 } else if (strncmp("mfp_required", argv[arg_idx], strlen("mfp_required")) == 0) {
235 creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_REQUIRED;
236 ++arg_idx;
237 }
238 }
239
240 if (arg_idx < argc) {
241 /* look for timeout */
242 char *end;
243
244 creds.header.timeout = strtol(argv[arg_idx], &end, 10);
245 if (*end == '\0') {
246 ++arg_idx;
247 }
248 }
249
250 if (arg_idx != argc) {
251 for (size_t i = arg_idx; i < argc; ++i) {
252 shell_warn(sh, "Unparsed arg: [%s]", argv[i]);
253 }
254 }
255
256 return wifi_credentials_set_personal_struct(&creds);
257 help:
258 shell_print(sh, "Usage: wifi_cred add \"network name\""
259 " {OPEN, WPA2-PSK, WPA2-PSK-SHA256, WPA3-SAE, WPA-PSK}"
260 " [psk/password]"
261 " [bssid]"
262 " [{2.4GHz, 5GHz}]"
263 " [channel]"
264 " [favorite]"
265 " [mfp_disabled|mfp_required]"
266 " [timeout]");
267 return -EINVAL;
268 }
269
cmd_delete_network(const struct shell * sh,size_t argc,char * argv[])270 static int cmd_delete_network(const struct shell *sh, size_t argc, char *argv[])
271 {
272 if (argc != 2) {
273 shell_print(sh, "Usage: wifi_cred delete \"network name\"");
274 return -EINVAL;
275 }
276
277 if (strnlen(argv[1], WIFI_SSID_MAX_LEN + 1) > WIFI_SSID_MAX_LEN) {
278 shell_error(sh, "SSID too long");
279 return -EINVAL;
280 }
281
282 shell_print(sh, "\tDeleting network ssid: \"%s\", ssid_len: %d", argv[1], strlen(argv[1]));
283 return wifi_credentials_delete_by_ssid(argv[1], strlen(argv[1]));
284 }
285
cmd_list_networks(const struct shell * sh,size_t argc,char * argv[])286 static int cmd_list_networks(const struct shell *sh, size_t argc, char *argv[])
287 {
288 wifi_credentials_for_each_ssid(print_network_info, (void *)sh);
289 return 0;
290 }
291
292 #if CONFIG_WIFI_CREDENTIALS_CONNECT_STORED
293
cmd_auto_connect(const struct shell * sh,size_t argc,char * argv[])294 static int cmd_auto_connect(const struct shell *sh, size_t argc, char *argv[])
295 {
296 struct net_if *iface = net_if_get_first_by_type(&NET_L2_GET_NAME(ETHERNET));
297 int rc = net_mgmt(NET_REQUEST_WIFI_CONNECT_STORED, iface, NULL, 0);
298
299 if (rc) {
300 shell_error(sh,
301 "An error occurred when trying to auto-connect to a network. err: %d",
302 rc);
303 }
304
305 return 0;
306 }
307
308 #endif /* CONFIG_WIFI_CREDENTIALS_CONNECT_STORED */
309
310 SHELL_STATIC_SUBCMD_SET_CREATE(sub_wifi_cred,
311 SHELL_CMD_ARG(add, NULL,
312 "Add network to storage.\n",
313 cmd_add_network, 0, 0),
314 SHELL_CMD_ARG(delete, NULL,
315 "Delete network from storage.\n",
316 cmd_delete_network,
317 0, 0),
318 SHELL_CMD_ARG(list, NULL,
319 "List stored networks.\n",
320 cmd_list_networks,
321 0, 0),
322
323 #if CONFIG_WIFI_CREDENTIALS_CONNECT_STORED
324 SHELL_CMD_ARG(auto_connect, NULL,
325 "Connect to any stored network.\n",
326 cmd_auto_connect,
327 0, 0),
328 #endif /* CONFIG_WIFI_CREDENTIALS_CONNECT_STORED */
329
330 SHELL_SUBCMD_SET_END
331 );
332
333 SHELL_SUBCMD_ADD((wifi), cred, &sub_wifi_cred,
334 "Wifi credentials management.\n",
335 NULL,
336 0, 0);
337