1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /** @file
8  * @brief Utility functions to be used by the Wi-Fi subsystem.
9  */
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(net_wifi_utils, CONFIG_NET_L2_WIFI_MGMT_LOG_LEVEL);
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 
18 #include <zephyr/kernel.h>
19 #include <zephyr/net/net_core.h>
20 #include <zephyr/net/wifi.h>
21 #include <zephyr/net/wifi_mgmt.h>
22 #include <zephyr/net/wifi_utils.h>
23 
24 /* Ensure 'strtok_r' is available even with -std=c99. */
25 char *strtok_r(char *str, const char *delim, char **saveptr);
26 
27 static const uint8_t valid_5g_chans_20mhz[] = {32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 96, 100,
28 	104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 159, 161,
29 	163, 165, 167, 169, 171, 173, 175, 177};
30 
wifi_utils_map_band_str_to_idx(char * band_str)31 static enum wifi_frequency_bands wifi_utils_map_band_str_to_idx(char *band_str)
32 {
33 	enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
34 
35 	if (!strcmp(band_str, "2")) {
36 		band = WIFI_FREQ_BAND_2_4_GHZ;
37 	} else if (!strcmp(band_str, "5")) {
38 		band = WIFI_FREQ_BAND_5_GHZ;
39 	} else if (!strcmp(band_str, "6")) {
40 		band = WIFI_FREQ_BAND_6_GHZ;
41 	}
42 
43 	return band;
44 }
45 
46 
wifi_utils_validate_chan_2g(uint16_t chan)47 bool wifi_utils_validate_chan_2g(uint16_t chan)
48 {
49 	if ((chan >= 1) && (chan <= 14)) {
50 		return true;
51 	}
52 
53 	return false;
54 }
55 
56 
wifi_utils_validate_chan_5g(uint16_t chan)57 bool wifi_utils_validate_chan_5g(uint16_t chan)
58 {
59 	uint16_t i;
60 
61 	for (i = 0; i < ARRAY_SIZE(valid_5g_chans_20mhz); i++) {
62 		if (chan == valid_5g_chans_20mhz[i]) {
63 			return true;
64 		}
65 	}
66 
67 	return false;
68 }
69 
70 
wifi_utils_validate_chan_6g(uint16_t chan)71 bool wifi_utils_validate_chan_6g(uint16_t chan)
72 {
73 	if (((chan >= 1) && (chan <= 233) && (!((chan - 1) % 4))) ||
74 	    (chan == 2)) {
75 		return true;
76 	}
77 
78 	return false;
79 }
80 
81 
wifi_utils_validate_chan(uint8_t band,uint16_t chan)82 bool wifi_utils_validate_chan(uint8_t band,
83 			      uint16_t chan)
84 {
85 	bool result = false;
86 
87 	switch (band) {
88 	case WIFI_FREQ_BAND_2_4_GHZ:
89 		result = wifi_utils_validate_chan_2g(chan);
90 		break;
91 	case WIFI_FREQ_BAND_5_GHZ:
92 		result = wifi_utils_validate_chan_5g(chan);
93 		break;
94 	case WIFI_FREQ_BAND_6_GHZ:
95 		result = wifi_utils_validate_chan_6g(chan);
96 		break;
97 	default:
98 		NET_ERR("Unknown band: %d", band);
99 		break;
100 	}
101 
102 	return result;
103 }
104 
105 /**
106  * @brief Get the next Wi-Fi 6GHz channel based on the given (valid) channel.
107  * The function handles the initial edge cases (1 -> 2, 2 -> 5) and then increments by 4.
108  *
109  * @param chan Current channel number.
110  * @return Next valid channel number.
111  */
wifi_utils_get_next_chan_6g(uint16_t chan)112 uint16_t wifi_utils_get_next_chan_6g(uint16_t chan)
113 {
114 	if (chan == 1) {
115 		return 2;
116 	} else if (chan == 2) {
117 		return 5;
118 	} else {
119 		return chan + 4;
120 	}
121 }
122 
wifi_utils_get_all_chans_in_range(uint8_t chan_start,uint8_t chan_end,struct wifi_band_channel * band_chan,uint8_t band_idx,uint8_t * chan_idx)123 static int wifi_utils_get_all_chans_in_range(uint8_t chan_start,
124 		uint8_t chan_end,
125 		struct wifi_band_channel *band_chan,
126 		uint8_t band_idx,
127 		uint8_t *chan_idx)
128 {
129 	uint8_t i;
130 	bool start = false;
131 	bool end = false;
132 	uint8_t idx;
133 
134 	if (!wifi_utils_validate_chan(band_idx, chan_start)) {
135 		NET_ERR("Invalid channel value %d in band %d", chan_start, band_idx);
136 		return -EINVAL;
137 	}
138 
139 	if (!wifi_utils_validate_chan(band_idx, chan_end)) {
140 		NET_ERR("Invalid channel value %d in band %d", chan_end, band_idx);
141 		return -EINVAL;
142 	}
143 
144 	if (chan_end < chan_start) {
145 		NET_ERR("Channel range end (%d) cannot be less than start (%d)",
146 			chan_end,
147 			chan_start);
148 		return -EINVAL;
149 	}
150 
151 	switch (band_idx) {
152 	case WIFI_FREQ_BAND_2_4_GHZ:
153 		idx = *chan_idx;
154 
155 		for (i = chan_start+1; i <= chan_end; i++) {
156 			band_chan[idx].band = band_idx;
157 			band_chan[idx].channel = i;
158 			idx++;
159 		}
160 
161 		*chan_idx = idx;
162 
163 		break;
164 	case WIFI_FREQ_BAND_5_GHZ:
165 		idx = *chan_idx;
166 
167 		for (i = 0; i < ARRAY_SIZE(valid_5g_chans_20mhz); i++) {
168 			if (valid_5g_chans_20mhz[i] == chan_start) {
169 				start = true;
170 				continue;
171 			}
172 
173 			if (valid_5g_chans_20mhz[i] == chan_end) {
174 				end = true;
175 			}
176 
177 			if (start) {
178 				band_chan[idx].band = band_idx;
179 				band_chan[idx].channel = valid_5g_chans_20mhz[i];
180 				idx++;
181 			}
182 
183 			if (end) {
184 				*chan_idx = idx;
185 				break;
186 			}
187 		}
188 
189 		break;
190 	case WIFI_FREQ_BAND_6_GHZ:
191 		idx = *chan_idx;
192 
193 		i = wifi_utils_get_next_chan_6g(chan_start);
194 
195 		while (i <= chan_end) {
196 			band_chan[idx].band = band_idx;
197 			band_chan[idx].channel = i;
198 			idx++;
199 
200 			i = wifi_utils_get_next_chan_6g(i);
201 		}
202 
203 		*chan_idx = idx;
204 		break;
205 	default:
206 		NET_ERR("Unknown band value: %d", band_idx);
207 		return -EINVAL;
208 	}
209 
210 	return 0;
211 }
212 
213 
wifi_utils_validate_chan_str(char * chan_str)214 static int wifi_utils_validate_chan_str(char *chan_str)
215 {
216 	uint8_t i;
217 
218 	if ((!chan_str) || (!strlen(chan_str))) {
219 		NET_ERR("Null or empty channel string\n");
220 		return -EINVAL;
221 	}
222 
223 	for (i = 0; i < strlen(chan_str); i++) {
224 		if (!isdigit((int)chan_str[i])) {
225 			NET_ERR("Invalid character in channel string %c\n", chan_str[i]);
226 			return -EINVAL;
227 		}
228 	}
229 
230 	return 0;
231 }
232 
233 
wifi_utils_parse_scan_bands(char * scan_bands_str,uint8_t * band_map)234 int wifi_utils_parse_scan_bands(char *scan_bands_str, uint8_t *band_map)
235 {
236 	char parse_str[WIFI_MGMT_BAND_STR_SIZE_MAX + 1];
237 	char *band_str = NULL;
238 	char *ctx = NULL;
239 	enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
240 	int len;
241 
242 	if (!scan_bands_str) {
243 		return -EINVAL;
244 	}
245 
246 	len = strlen(scan_bands_str);
247 
248 	if (len > WIFI_MGMT_BAND_STR_SIZE_MAX) {
249 		NET_ERR("Band string (%s) size (%d) exceeds maximum allowed value (%d)",
250 			scan_bands_str,
251 			len,
252 			WIFI_MGMT_BAND_STR_SIZE_MAX);
253 		return -EINVAL;
254 	}
255 
256 	strncpy(parse_str, scan_bands_str, sizeof(parse_str) - 1);
257 	parse_str[sizeof(parse_str) - 1] = '\0';
258 
259 	band_str = strtok_r(parse_str, ",", &ctx);
260 
261 	while (band_str) {
262 		band = wifi_utils_map_band_str_to_idx(band_str);
263 
264 		if (band == WIFI_FREQ_BAND_UNKNOWN) {
265 			NET_ERR("Invalid band value: %s", band_str);
266 			return -EINVAL;
267 		}
268 
269 		*band_map |= (1 << band);
270 
271 		band_str = strtok_r(NULL, ",", &ctx);
272 	}
273 
274 	return 0;
275 }
276 
wifi_utils_parse_scan_ssids(char * scan_ssids_str,const char * ssids[],uint8_t num_ssids)277 int wifi_utils_parse_scan_ssids(char *scan_ssids_str,
278 				const char *ssids[],
279 				uint8_t num_ssids)
280 {
281 	int len;
282 
283 	if (!scan_ssids_str) {
284 		return -EINVAL;
285 	}
286 
287 	len = strlen(scan_ssids_str);
288 	if (len > WIFI_SSID_MAX_LEN) {
289 		NET_ERR("SSID string (%s) size (%d) exceeds maximum allowed value (%d)",
290 			scan_ssids_str,
291 			len,
292 			WIFI_SSID_MAX_LEN);
293 		return -EINVAL;
294 	}
295 
296 	for (int i = 0; i < num_ssids; i++) {
297 		if (ssids[i] != NULL) {
298 			continue;
299 		}
300 		ssids[i] = scan_ssids_str;
301 		return 0;
302 	}
303 
304 	NET_WARN("Exceeded maximum allowed SSIDs (%d)", num_ssids);
305 	return 0;
306 }
307 
308 
wifi_utils_parse_scan_chan(char * scan_chan_str,struct wifi_band_channel * band_chan,uint8_t max_channels)309 int wifi_utils_parse_scan_chan(char *scan_chan_str,
310 			       struct wifi_band_channel *band_chan,
311 			       uint8_t max_channels)
312 {
313 	char band_str[WIFI_UTILS_MAX_BAND_STR_LEN] = {0};
314 	char chan_str[WIFI_UTILS_MAX_CHAN_STR_LEN] = {0};
315 	enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
316 	uint16_t band_str_start_idx = 0;
317 	uint16_t chan_str_start_idx = 0;
318 	uint8_t chan_idx = 0;
319 	uint8_t chan_start = 0;
320 	uint8_t chan_val = 0;
321 	uint16_t i = 0;
322 	bool valid_band = false;
323 	bool valid_chan = false;
324 
325 	while (scan_chan_str[i] != '\0') {
326 		if (scan_chan_str[i] != ':') {
327 			i++;
328 			continue;
329 		}
330 
331 		if (((i - band_str_start_idx) <= 0) ||
332 		    ((i - band_str_start_idx) > WIFI_UTILS_MAX_BAND_STR_LEN)) {
333 			NET_ERR("Invalid band value %s",
334 				&scan_chan_str[band_str_start_idx]);
335 			return -EINVAL;
336 		}
337 
338 		strncpy(band_str,
339 			&scan_chan_str[band_str_start_idx],
340 			(i - band_str_start_idx));
341 
342 		band = wifi_utils_map_band_str_to_idx(band_str);
343 
344 		if (band == WIFI_FREQ_BAND_UNKNOWN) {
345 			NET_ERR("Unsupported band value: %s", band_str);
346 			return -EINVAL;
347 		}
348 
349 		i++;
350 		chan_str_start_idx = i;
351 		valid_band = true;
352 
353 		while (1) {
354 			if ((scan_chan_str[i] != ',') &&
355 			    (scan_chan_str[i] != '_') &&
356 			    (scan_chan_str[i] != '-') &&
357 			    (scan_chan_str[i] != '\0')) {
358 				i++;
359 				continue;
360 			}
361 
362 			if ((i - chan_str_start_idx) >
363 			    WIFI_UTILS_MAX_CHAN_STR_LEN) {
364 				NET_ERR("Invalid chan value %s",
365 					&scan_chan_str[chan_str_start_idx]);
366 				return -EINVAL;
367 			}
368 
369 			strncpy(chan_str,
370 				&scan_chan_str[chan_str_start_idx],
371 				(i - chan_str_start_idx));
372 
373 			if (wifi_utils_validate_chan_str(chan_str)) {
374 				NET_ERR("Channel string validation failed");
375 				return -EINVAL;
376 			}
377 
378 			chan_val = atoi(chan_str);
379 
380 			memset(chan_str, 0, sizeof(chan_str));
381 
382 			if (chan_start) {
383 				if (wifi_utils_get_all_chans_in_range(chan_start,
384 								      chan_val,
385 								      band_chan,
386 								      band,
387 								      &chan_idx)) {
388 					NET_ERR("Channel range invalid");
389 					return -EINVAL;
390 				}
391 
392 				if (chan_idx > max_channels) {
393 					NET_ERR("Too many channels specified (%d)", max_channels);
394 					return -EINVAL;
395 				}
396 				chan_start = 0;
397 			} else {
398 				if (!wifi_utils_validate_chan(band,
399 							      chan_val)) {
400 					NET_ERR("Invalid channel %d", chan_val);
401 					return -EINVAL;
402 				}
403 				if (chan_idx == max_channels) {
404 					NET_ERR("Too many channels specified (%d)", max_channels);
405 					return -EINVAL;
406 				}
407 
408 				band_chan[chan_idx].band = band;
409 				band_chan[chan_idx].channel = chan_val;
410 				chan_idx++;
411 			}
412 
413 			valid_chan = true;
414 
415 			if (scan_chan_str[i] == '_') {
416 				band_str_start_idx = ++i;
417 				break;
418 			} else if (scan_chan_str[i] == ',') {
419 				chan_str_start_idx = ++i;
420 			} else if (scan_chan_str[i] == '-') {
421 				chan_start = chan_val;
422 				chan_str_start_idx = ++i;
423 			} else if (scan_chan_str[i] == '\0') {
424 				break;
425 			}
426 		}
427 	}
428 
429 	if (!valid_band) {
430 		NET_ERR("No valid band found");
431 		return -EINVAL;
432 	}
433 
434 	if (!valid_chan) {
435 		NET_ERR("No valid channel found");
436 		return -EINVAL;
437 	}
438 
439 	return 0;
440 }
441