1 /*
2 * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: Implement parsing logic for action_oui strings, extract
22 * extensions and store them using linked list. Functions defined in
23 * this file can be accessed internally in action_oui component only.
24 */
25
26 #include "wlan_action_oui_main.h"
27 #include "wlan_action_oui_public_struct.h"
28 #include "wlan_action_oui_tgt_api.h"
29 #include "target_if_action_oui.h"
30 #include <qdf_str.h>
31 #include <wlan_utility.h>
32
33 /**
34 * action_oui_string_to_hex() - convert string to uint8_t hex array
35 * @token: string to be converted
36 * @hex: output string to hold converted string
37 * @no_of_lengths: count of possible lengths for input string
38 * @possible_lengths: array holding possible lengths
39 *
40 * This function converts the continuous input string of even length and
41 * containing hexa decimal characters into hexa decimal array of uint8_t type.
42 * Input string needs to be NULL terminated and the length should match with
43 * one of entries in @possible_lengths
44 *
45 * Return: If conversion is successful return true else false
46 */
action_oui_string_to_hex(uint8_t * token,uint8_t * hex,uint32_t no_of_lengths,uint32_t * possible_lengths)47 static bool action_oui_string_to_hex(uint8_t *token, uint8_t *hex,
48 uint32_t no_of_lengths,
49 uint32_t *possible_lengths)
50 {
51 uint32_t token_len = qdf_str_len(token);
52 uint32_t hex_str_len;
53 uint32_t i;
54 int ret;
55
56 if (!token_len || (token_len & 0x01)) {
57 action_oui_err("Token len is not multiple of 2");
58 return false;
59 }
60
61 for (i = 0; i < no_of_lengths; i++)
62 if (token_len == possible_lengths[i])
63 break;
64
65 if (i == no_of_lengths) {
66 action_oui_err("Token len doesn't match with expected len");
67 return false;
68 }
69
70 hex_str_len = token_len / 2;
71
72 ret = qdf_hex_str_to_binary(hex, token, hex_str_len);
73 if (ret) {
74 action_oui_err("Token doesn't contain hex digits");
75 return false;
76 }
77
78 return true;
79 }
80
81 /**
82 * action_oui_token_string() - converts enum value to string
83 * @token_id: enum value to be converted to string
84 *
85 * This function converts the enum value of type action_oui_token_type
86 * to string
87 *
88 * Return: converted string
89 */
90 static
action_oui_token_string(enum action_oui_token_type token_id)91 uint8_t *action_oui_token_string(enum action_oui_token_type token_id)
92 {
93 switch (token_id) {
94 CASE_RETURN_STRING(ACTION_OUI_TOKEN);
95 CASE_RETURN_STRING(ACTION_OUI_DATA_LENGTH_TOKEN);
96 CASE_RETURN_STRING(ACTION_OUI_DATA_TOKEN);
97 CASE_RETURN_STRING(ACTION_OUI_DATA_MASK_TOKEN);
98 CASE_RETURN_STRING(ACTION_OUI_INFO_MASK_TOKEN);
99 CASE_RETURN_STRING(ACTION_OUI_MAC_ADDR_TOKEN);
100 CASE_RETURN_STRING(ACTION_OUI_MAC_MASK_TOKEN);
101 CASE_RETURN_STRING(ACTION_OUI_CAPABILITY_TOKEN);
102 CASE_RETURN_STRING(ACTION_OUI_END_TOKEN);
103 }
104
105 return (uint8_t *) "UNKNOWN";
106 }
107
108 /**
109 * validate_and_convert_oui() - validate and convert OUI str to hex array
110 * @token: OUI string
111 * @ext: pointer to container which holds converted hex array
112 * @action_token: next action to be parsed
113 *
114 * This is an internal function invoked from action_oui_parse to validate
115 * the OUI string for action OUI inis, convert them to hex array and store it
116 * in action_oui extension. After successful parsing update the
117 * @action_token to hold the next expected string.
118 *
119 * Return: If conversion is successful return true else false
120 */
121 static
validate_and_convert_oui(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)122 bool validate_and_convert_oui(uint8_t *token,
123 struct action_oui_extension *ext,
124 enum action_oui_token_type *action_token)
125 {
126 bool valid;
127 uint32_t expected_token_len[2] = {6, 10};
128
129 valid = action_oui_string_to_hex(token, ext->oui, 2,
130 expected_token_len);
131 if (!valid)
132 return false;
133
134 ext->oui_length = qdf_str_len(token) / 2;
135
136 *action_token = ACTION_OUI_DATA_LENGTH_TOKEN;
137
138 return valid;
139 }
140
141 /**
142 * validate_and_convert_data_length() - validate data len str
143 * @token: data length string
144 * @ext: pointer to container which holds hex value formed from input str
145 * @action_token: next action to be parsed
146 *
147 * This is an internal function invoked from action_oui_parse to validate
148 * the data length string for action OUI inis, convert it to hex value and
149 * store it in action_oui extension. After successful parsing update the
150 * @action_token to hold the next expected string.
151 *
152 * Return: If conversion is successful return true else false
153 */
154 static bool
validate_and_convert_data_length(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)155 validate_and_convert_data_length(uint8_t *token,
156 struct action_oui_extension *ext,
157 enum action_oui_token_type *action_token)
158 {
159 uint32_t token_len = qdf_str_len(token);
160 int ret;
161 uint8_t len = 0;
162
163 if (token_len != 1 && token_len != 2) {
164 action_oui_err("Invalid str token len for action OUI data len");
165 return false;
166 }
167
168 ret = kstrtou8(token, 16, &len);
169 if (ret) {
170 action_oui_err("Invalid char in action OUI data len str token");
171 return false;
172 }
173
174 if ((uint32_t)len > ACTION_OUI_MAX_DATA_LENGTH) {
175 action_oui_err("action OUI data len %d is more than %u",
176 len, ACTION_OUI_MAX_DATA_LENGTH);
177 return false;
178 }
179
180 ext->data_length = len;
181
182 if (!ext->data_length)
183 *action_token = ACTION_OUI_INFO_MASK_TOKEN;
184 else
185 *action_token = ACTION_OUI_DATA_TOKEN;
186
187 return true;
188 }
189
190 /**
191 * validate_and_convert_data() - validate and convert data str to hex array
192 * @token: data string
193 * @ext: pointer to container which holds converted hex array
194 * @action_token: next action to be parsed
195 *
196 * This is an internal function invoked from action_oui_parse to validate
197 * the data string for action OUI inis, convert it to hex array and store in
198 * action_oui extension. After successful parsing update the @action_token
199 * to hold the next expected string.
200 *
201 * Return: If conversion is successful return true else false
202 */
203 static bool
validate_and_convert_data(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)204 validate_and_convert_data(uint8_t *token,
205 struct action_oui_extension *ext,
206 enum action_oui_token_type *action_token)
207 {
208 bool valid;
209 uint32_t expected_token_len[1] = {2 * ext->data_length};
210
211 valid = action_oui_string_to_hex(token, ext->data, 1,
212 expected_token_len);
213 if (!valid)
214 return false;
215
216 *action_token = ACTION_OUI_DATA_MASK_TOKEN;
217
218 return true;
219 }
220
221 /**
222 * validate_and_convert_data_mask() - validate and convert data mask str
223 * @token: data mask string
224 * @ext: pointer to container which holds converted hex array
225 * @action_token: next action to be parsed
226 *
227 * This is an internal function invoked from action_oui_parse to validate
228 * the data mask string for action OUI inis, convert it to hex array and store
229 * in action_oui extension. After successful parsing update the
230 * @action_token to hold the next expected string.
231 *
232 * Return: If conversion is successful return true else false
233 */
234 static bool
validate_and_convert_data_mask(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)235 validate_and_convert_data_mask(uint8_t *token,
236 struct action_oui_extension *ext,
237 enum action_oui_token_type *action_token)
238 {
239 bool valid;
240 uint32_t expected_token_len[1];
241 uint32_t data_mask_length;
242 uint32_t data_length = ext->data_length;
243
244 if (data_length % 8 == 0)
245 data_mask_length = data_length / 8;
246 else
247 data_mask_length = ((data_length / 8) + 1);
248
249 if (data_mask_length > ACTION_OUI_MAX_DATA_MASK_LENGTH)
250 return false;
251
252 expected_token_len[0] = 2 * data_mask_length;
253
254 valid = action_oui_string_to_hex(token, ext->data_mask, 1,
255 expected_token_len);
256 if (!valid)
257 return false;
258
259 ext->data_mask_length = data_mask_length;
260
261 *action_token = ACTION_OUI_INFO_MASK_TOKEN;
262
263 return valid;
264 }
265
266 /**
267 * validate_and_convert_info_mask() - validate and convert info mask str
268 * @token: info mask string
269 * @ext: pointer to container which holds converted hex array
270 * @action_token: next action to be parsed
271 *
272 * This is an internal function invoked from action_oui_parse to validate
273 * the info mask string for action OUI inis, convert it to hex array and store
274 * in action_oui extension. After successful parsing update the
275 * @action_token to hold the next expected string.
276 *
277 * Return: If conversion is successful return true else false
278 */
279 static bool
validate_and_convert_info_mask(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)280 validate_and_convert_info_mask(uint8_t *token,
281 struct action_oui_extension *ext,
282 enum action_oui_token_type *action_token)
283 {
284 uint32_t token_len = qdf_str_len(token);
285 uint8_t hex_value = 0;
286 uint32_t info_mask;
287 int ret;
288
289 if (token_len != 2) {
290 action_oui_err("action OUI info mask str token len is not of 2 chars");
291 return false;
292 }
293
294 ret = kstrtou8(token, 16, &hex_value);
295 if (ret) {
296 action_oui_err("Invalid char in action OUI info mask str token");
297 return false;
298 }
299
300 info_mask = hex_value;
301
302 ext->info_mask = info_mask;
303
304 if (!info_mask || !(info_mask & ~ACTION_OUI_INFO_OUI)) {
305 *action_token = ACTION_OUI_END_TOKEN;
306 return true;
307 }
308
309 if (info_mask & ~ACTION_OUI_INFO_MASK) {
310 action_oui_err("Invalid bits are set in action OUI info mask");
311 return false;
312 }
313
314 /*
315 * If OUI bit is not set in the info presence, we need to ignore the
316 * OUI and OUI Data. Set OUI and OUI data length to 0 here.
317 */
318 if (!(info_mask & ACTION_OUI_INFO_OUI)) {
319 ext->oui_length = 0;
320 ext->data_length = 0;
321 ext->data_mask_length = 0;
322 }
323
324 if (info_mask & ACTION_OUI_INFO_MAC_ADDRESS) {
325 *action_token = ACTION_OUI_MAC_ADDR_TOKEN;
326 return true;
327 }
328
329 *action_token = ACTION_OUI_CAPABILITY_TOKEN;
330 return true;
331 }
332
333 /**
334 * validate_and_convert_mac_addr() - validate and convert mac addr str
335 * @token: mac address string
336 * @ext: pointer to container which holds converted hex array
337 * @action_token: next action to be parsed
338 *
339 * This is an internal function invoked from action_oui_parse to validate
340 * the mac address string for action OUI inis, convert it to hex array and store
341 * in action_oui extension. After successful parsing update the
342 * @action_token to hold the next expected string.
343 *
344 * Return: If conversion is successful return true else false
345 */
346 static bool
validate_and_convert_mac_addr(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)347 validate_and_convert_mac_addr(uint8_t *token,
348 struct action_oui_extension *ext,
349 enum action_oui_token_type *action_token)
350 {
351 uint32_t expected_token_len[1] = {2 * QDF_MAC_ADDR_SIZE};
352 bool valid;
353
354 valid = action_oui_string_to_hex(token, ext->mac_addr, 1,
355 expected_token_len);
356 if (!valid)
357 return false;
358
359 ext->mac_addr_length = QDF_MAC_ADDR_SIZE;
360
361 *action_token = ACTION_OUI_MAC_MASK_TOKEN;
362
363 return true;
364 }
365
366 /**
367 * validate_and_convert_mac_mask() - validate and convert mac mask
368 * @token: mac mask string
369 * @ext: pointer to container which holds converted hex value
370 * @action_token: next action to be parsed
371 *
372 * This is an internal function invoked from action_oui_parse to validate
373 * the mac mask string for action OUI inis, convert it to hex value and store
374 * in action_oui extension. After successful parsing update the
375 * @action_token to hold the next expected string.
376 *
377 * Return: If conversion is successful return true else false
378 */
379 static bool
validate_and_convert_mac_mask(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)380 validate_and_convert_mac_mask(uint8_t *token,
381 struct action_oui_extension *ext,
382 enum action_oui_token_type *action_token)
383 {
384 uint32_t expected_token_len[1] = {2};
385 uint32_t info_mask = ext->info_mask;
386 bool valid;
387 uint32_t mac_mask_length;
388
389 valid = action_oui_string_to_hex(token, ext->mac_mask, 1,
390 expected_token_len);
391 if (!valid)
392 return false;
393
394 mac_mask_length = qdf_str_len(token) / 2;
395 if (mac_mask_length > ACTION_OUI_MAC_MASK_LENGTH) {
396 action_oui_err("action OUI mac mask str token len is more than %u chars",
397 expected_token_len[0]);
398 return false;
399 }
400
401 ext->mac_mask_length = mac_mask_length;
402
403 if ((info_mask & ACTION_OUI_INFO_AP_CAPABILITY_NSS) ||
404 (info_mask & ACTION_OUI_INFO_AP_CAPABILITY_HT) ||
405 (info_mask & ACTION_OUI_INFO_AP_CAPABILITY_VHT) ||
406 (info_mask & ACTION_OUI_INFO_AP_CAPABILITY_BAND)) {
407 *action_token = ACTION_OUI_CAPABILITY_TOKEN;
408 return true;
409 }
410
411 *action_token = ACTION_OUI_END_TOKEN;
412 return true;
413 }
414
415 /**
416 * validate_and_convert_capability() - validate and convert capability str
417 * @token: capability string
418 * @ext: pointer to container which holds converted hex value
419 * @action_token: next action to be parsed
420 *
421 * This is an internal function invoked from action_oui_parse to validate
422 * the capability string for action OUI inis, convert it to hex value and store
423 * in action_oui extension. After successful parsing update the
424 * @action_token to hold the next expected string.
425 *
426 * Return: If conversion is successful return true else false
427 */
428 static bool
validate_and_convert_capability(uint8_t * token,struct action_oui_extension * ext,enum action_oui_token_type * action_token)429 validate_and_convert_capability(uint8_t *token,
430 struct action_oui_extension *ext,
431 enum action_oui_token_type *action_token)
432 {
433 uint32_t expected_token_len[1] = {2};
434 uint32_t info_mask = ext->info_mask;
435 uint32_t capability_length;
436 uint8_t caps_0;
437 bool valid;
438
439 valid = action_oui_string_to_hex(token, ext->capability, 1,
440 expected_token_len);
441 if (!valid)
442 return false;
443
444 capability_length = qdf_str_len(token) / 2;
445 if (capability_length > ACTION_OUI_MAX_CAPABILITY_LENGTH) {
446 action_oui_err("action OUI capability str token len is more than %u chars",
447 expected_token_len[0]);
448 return false;
449 }
450
451 caps_0 = ext->capability[0];
452
453 if ((info_mask & ACTION_OUI_INFO_AP_CAPABILITY_NSS) &&
454 (!(caps_0 & ACTION_OUI_CAPABILITY_NSS_MASK))) {
455 action_oui_err("Info presence for NSS is set but respective bits in capability are not set");
456 return false;
457 }
458
459 if ((info_mask & ACTION_OUI_INFO_AP_CAPABILITY_BAND) &&
460 (!(caps_0 & ACTION_OUI_CAPABILITY_BAND_MASK))) {
461 action_oui_err("Info presence for BAND is set but respective bits in capability are not set");
462 return false;
463 }
464
465 ext->capability_length = capability_length;
466
467 *action_token = ACTION_OUI_END_TOKEN;
468
469 return true;
470 }
471
472 /**
473 * action_oui_extension_store() - store action oui extension
474 * @psoc_priv: pointer to action_oui priv obj
475 * @oui_priv: type of the action
476 * @ext: oui extension to store in sme
477 *
478 * This function stores the parsed oui extension
479 *
480 * Return: QDF_STATUS
481 *
482 */
483 static QDF_STATUS
action_oui_extension_store(struct action_oui_psoc_priv * psoc_priv,struct action_oui_priv * oui_priv,struct action_oui_extension ext)484 action_oui_extension_store(struct action_oui_psoc_priv *psoc_priv,
485 struct action_oui_priv *oui_priv,
486 struct action_oui_extension ext)
487 {
488 struct action_oui_extension_priv *ext_priv;
489
490 qdf_mutex_acquire(&oui_priv->extension_lock);
491 if (qdf_list_size(&oui_priv->extension_list) ==
492 ACTION_OUI_MAX_EXTENSIONS) {
493 qdf_mutex_release(&oui_priv->extension_lock);
494 action_oui_err("Reached maximum OUI extensions");
495 return QDF_STATUS_E_FAILURE;
496 }
497
498 ext_priv = qdf_mem_malloc(sizeof(*ext_priv));
499 if (!ext_priv) {
500 qdf_mutex_release(&oui_priv->extension_lock);
501 return QDF_STATUS_E_NOMEM;
502 }
503
504 ext_priv->extension = ext;
505 qdf_list_insert_back(&oui_priv->extension_list, &ext_priv->item);
506 psoc_priv->total_extensions++;
507 qdf_mutex_release(&oui_priv->extension_lock);
508
509 return QDF_STATUS_SUCCESS;
510 }
511
512 QDF_STATUS
action_oui_parse(struct action_oui_psoc_priv * psoc_priv,uint8_t * oui_string,enum action_oui_id action_id)513 action_oui_parse(struct action_oui_psoc_priv *psoc_priv,
514 uint8_t *oui_string, enum action_oui_id action_id)
515 {
516 struct action_oui_extension ext = {0};
517 enum action_oui_token_type action_token = ACTION_OUI_TOKEN;
518 char *str1;
519 char *str2;
520 char *token;
521 bool valid = true;
522 bool oui_count_exceed = false;
523 uint32_t oui_index = 0;
524 QDF_STATUS status = QDF_STATUS_SUCCESS;
525 struct action_oui_priv *oui_priv;
526
527 if (!oui_string) {
528 action_oui_err("Invalid string for action oui: %u", action_id);
529 return QDF_STATUS_E_INVAL;
530 }
531
532 oui_priv = psoc_priv->oui_priv[action_id];
533 if (!oui_priv) {
534 action_oui_err("action oui priv not allocated");
535 return QDF_STATUS_E_INVAL;
536 }
537
538 if (!psoc_priv->action_oui_enable) {
539 action_oui_debug("action_oui is not enable");
540 return QDF_STATUS_SUCCESS;
541 }
542
543 str1 = qdf_str_trim((char *)oui_string);
544
545 while (str1) {
546 str2 = (char *)qdf_str_left_trim(str1);
547 if (str2[0] == '\0') {
548 action_oui_err("Invalid spaces in action oui: %u at extension: %u for token: %s",
549 action_id,
550 oui_index + 1,
551 action_oui_token_string(action_token));
552 valid = false;
553 break;
554 }
555
556 token = strsep(&str2, " ");
557 if (!token) {
558 action_oui_err("Invalid string for token: %s at extension: %u in action oui: %u",
559 action_oui_token_string(action_token),
560 oui_index + 1, action_id);
561 valid = false;
562 break;
563 }
564
565 str1 = str2;
566
567 switch (action_token) {
568
569 case ACTION_OUI_TOKEN:
570 valid = validate_and_convert_oui(token, &ext,
571 &action_token);
572 break;
573
574 case ACTION_OUI_DATA_LENGTH_TOKEN:
575 valid = validate_and_convert_data_length(token, &ext,
576 &action_token);
577 break;
578
579 case ACTION_OUI_DATA_TOKEN:
580 valid = validate_and_convert_data(token, &ext,
581 &action_token);
582 break;
583
584 case ACTION_OUI_DATA_MASK_TOKEN:
585 valid = validate_and_convert_data_mask(token, &ext,
586 &action_token);
587 break;
588
589 case ACTION_OUI_INFO_MASK_TOKEN:
590 valid = validate_and_convert_info_mask(token, &ext,
591 &action_token);
592 break;
593
594 case ACTION_OUI_MAC_ADDR_TOKEN:
595 valid = validate_and_convert_mac_addr(token, &ext,
596 &action_token);
597 break;
598
599 case ACTION_OUI_MAC_MASK_TOKEN:
600 valid = validate_and_convert_mac_mask(token, &ext,
601 &action_token);
602 break;
603
604 case ACTION_OUI_CAPABILITY_TOKEN:
605 valid = validate_and_convert_capability(token, &ext,
606 &action_token);
607 break;
608
609 default:
610 valid = false;
611 break;
612 }
613
614 if (!valid) {
615 action_oui_err("Invalid string for token: %s at extension: %u in action oui: %u",
616 action_oui_token_string(action_token),
617 oui_index + 1,
618 action_id);
619 break;
620 }
621
622 if (action_token != ACTION_OUI_END_TOKEN)
623 continue;
624
625 status = action_oui_extension_store(psoc_priv, oui_priv, ext);
626 if (!QDF_IS_STATUS_SUCCESS(status)) {
627 valid = false;
628 action_oui_err("sme set of extension: %u for action oui: %u failed",
629 oui_index + 1, action_id);
630 break;
631 }
632
633 if (action_id >= ACTION_OUI_HOST_ONLY) {
634 qdf_mutex_acquire(&oui_priv->extension_lock);
635 psoc_priv->host_only_extensions++;
636 qdf_mutex_release(&oui_priv->extension_lock);
637 }
638
639 oui_index++;
640 if (oui_index == ACTION_OUI_MAX_EXTENSIONS) {
641 if (str1)
642 oui_count_exceed = true;
643 break;
644 }
645
646 /* reset the params for next action OUI parse */
647 action_token = ACTION_OUI_TOKEN;
648 qdf_mem_zero(&ext, sizeof(ext));
649 }
650
651 if (oui_count_exceed) {
652 action_oui_err("Reached Maximum extensions: %u in action_oui: %u, ignoring the rest",
653 ACTION_OUI_MAX_EXTENSIONS, action_id);
654 return QDF_STATUS_SUCCESS;
655 }
656
657 if (action_token != ACTION_OUI_TOKEN &&
658 action_token != ACTION_OUI_END_TOKEN &&
659 valid && !str1) {
660 action_oui_err("No string for token: %s at extension: %u in action oui: %u",
661 action_oui_token_string(action_token),
662 oui_index + 1,
663 action_id);
664 valid = false;
665 }
666
667 if (!oui_index) {
668 action_oui_err("Not able to parse any extension in action oui: %u",
669 action_id);
670 return QDF_STATUS_E_INVAL;
671 }
672
673 if (valid)
674 action_oui_debug("All extensions: %u parsed successfully in action oui: %u",
675 oui_index, action_id);
676 else
677 action_oui_err("First %u extensions parsed successfully in action oui: %u",
678 oui_index, action_id);
679
680 return QDF_STATUS_SUCCESS;
681 }
682
683 QDF_STATUS
action_oui_parse_string(struct wlan_objmgr_psoc * psoc,const uint8_t * in_str,enum action_oui_id action_id)684 action_oui_parse_string(struct wlan_objmgr_psoc *psoc,
685 const uint8_t *in_str,
686 enum action_oui_id action_id)
687 {
688 struct action_oui_psoc_priv *psoc_priv;
689 QDF_STATUS status = QDF_STATUS_E_INVAL;
690 uint8_t *oui_str;
691 int len;
692
693 ACTION_OUI_ENTER();
694
695 if (!psoc) {
696 action_oui_err("psoc is NULL");
697 goto exit;
698 }
699
700 if (action_id >= ACTION_OUI_MAXIMUM_ID) {
701 action_oui_err("Invalid action_oui id: %u", action_id);
702 goto exit;
703 }
704
705 psoc_priv = action_oui_psoc_get_priv(psoc);
706 if (!psoc_priv) {
707 action_oui_err("psoc priv is NULL");
708 goto exit;
709 }
710
711 len = qdf_str_len(in_str);
712 if (len <= 0 || len > ACTION_OUI_MAX_STR_LEN - 1) {
713 action_oui_err("Invalid string length: %u", action_id);
714 goto exit;
715 }
716
717 oui_str = qdf_mem_malloc(len + 1);
718 if (!oui_str) {
719 status = QDF_STATUS_E_NOMEM;
720 goto exit;
721 }
722
723 qdf_mem_copy(oui_str, in_str, len);
724 oui_str[len] = '\0';
725
726 status = action_oui_parse(psoc_priv, oui_str, action_id);
727 if (!QDF_IS_STATUS_SUCCESS(status))
728 action_oui_err("Failed to parse: %u", action_id);
729
730 qdf_mem_free(oui_str);
731
732 exit:
733 ACTION_OUI_EXIT();
734 return status;
735 }
736
action_oui_send(struct action_oui_psoc_priv * psoc_priv,enum action_oui_id action_id)737 QDF_STATUS action_oui_send(struct action_oui_psoc_priv *psoc_priv,
738 enum action_oui_id action_id)
739 {
740 QDF_STATUS status;
741 struct action_oui_request *req;
742 struct action_oui_priv *oui_priv;
743 struct action_oui_extension *extension;
744 struct action_oui_extension_priv *ext_priv;
745 qdf_list_node_t *node = NULL;
746 qdf_list_node_t *next_node = NULL;
747 qdf_list_t *extension_list;
748 uint32_t len;
749 uint32_t no_oui_extensions;
750
751 oui_priv = psoc_priv->oui_priv[action_id];
752 if (!oui_priv)
753 return QDF_STATUS_SUCCESS;
754
755 if (!psoc_priv->action_oui_enable) {
756 action_oui_debug("action_oui is not enable");
757 return QDF_STATUS_SUCCESS;
758 }
759
760 extension_list = &oui_priv->extension_list;
761 qdf_mutex_acquire(&oui_priv->extension_lock);
762
763 if (psoc_priv->max_extensions -
764 (psoc_priv->total_extensions - psoc_priv->host_only_extensions) < 0) {
765 action_oui_err("total_extensions: %d exceeds max_extensions: %d, do not update",
766 psoc_priv->max_extensions,
767 (psoc_priv->total_extensions -
768 psoc_priv->host_only_extensions));
769 qdf_mutex_release(&oui_priv->extension_lock);
770 return QDF_STATUS_E_FAILURE;
771 }
772
773 no_oui_extensions = qdf_list_size(extension_list);
774 len = sizeof(*req) + no_oui_extensions * sizeof(*extension);
775 req = qdf_mem_malloc(len);
776 if (!req) {
777 qdf_mutex_release(&oui_priv->extension_lock);
778 return QDF_STATUS_E_NOMEM;
779 }
780
781 req->action_id = oui_priv->id;
782 req->no_oui_extensions = no_oui_extensions;
783 req->total_no_oui_extensions = psoc_priv->max_extensions;
784
785 extension = req->extension;
786 qdf_list_peek_front(extension_list, &node);
787 while (node) {
788 ext_priv = qdf_container_of(node,
789 struct action_oui_extension_priv,
790 item);
791 *extension = ext_priv->extension;
792 status = qdf_list_peek_next(extension_list, node,
793 &next_node);
794 if (!QDF_IS_STATUS_SUCCESS(status))
795 break;
796 node = next_node;
797 next_node = NULL;
798 extension++;
799 }
800
801 qdf_mutex_release(&oui_priv->extension_lock);
802
803 status = tgt_action_oui_send(psoc_priv->psoc, req);
804 qdf_mem_free(req);
805
806 return status;
807 }
808
809 /**
810 * check_for_vendor_oui_data() - compares for vendor OUI data from IE
811 * and returns true if OUI data matches with the ini
812 * @extension: pointer to action oui extension data
813 * @oui_ptr: pointer to Vendor IE in the beacon
814 *
815 * Return: true or false
816 */
817 static bool
check_for_vendor_oui_data(struct action_oui_extension * extension,const uint8_t * oui_ptr)818 check_for_vendor_oui_data(struct action_oui_extension *extension,
819 const uint8_t *oui_ptr)
820 {
821 const uint8_t *data;
822 uint8_t i, j, elem_len, data_len;
823 uint8_t data_mask = 0x80;
824
825 if (!oui_ptr)
826 return false;
827
828 elem_len = oui_ptr[1];
829 if (elem_len < extension->oui_length)
830 return false;
831
832 data_len = elem_len - extension->oui_length;
833 if (data_len < extension->data_length)
834 return false;
835
836 data = &oui_ptr[2 + extension->oui_length];
837 for (i = 0, j = 0;
838 (i < data_len && j < extension->data_mask_length);
839 i++) {
840 if ((extension->data_mask[j] & data_mask) &&
841 !(extension->data[i] == data[i]))
842 return false;
843 data_mask = data_mask >> 1;
844 if (!data_mask) {
845 data_mask = 0x80;
846 j++;
847 }
848 }
849
850 return true;
851 }
852
853 /**
854 * check_for_vendor_ap_mac() - compares for vendor AP MAC in the ini with
855 * bssid from the session and returns true if matches
856 * @extension: pointer to action oui extension data
857 * @attr: pointer to structure containing mac_addr (bssid) of AP
858 *
859 * Return: true or false
860 */
861 static bool
check_for_vendor_ap_mac(struct action_oui_extension * extension,struct action_oui_search_attr * attr)862 check_for_vendor_ap_mac(struct action_oui_extension *extension,
863 struct action_oui_search_attr *attr)
864 {
865 uint8_t i;
866 uint8_t mac_mask = 0x80;
867 uint8_t *mac_addr = attr->mac_addr;
868
869 for (i = 0; i < QDF_MAC_ADDR_SIZE; i++) {
870 if ((*extension->mac_mask & mac_mask) &&
871 !(extension->mac_addr[i] == mac_addr[i]))
872 return false;
873 mac_mask = mac_mask >> 1;
874 }
875
876 return true;
877 }
878
879 /**
880 * check_for_vendor_ap_capabilities() - Compares various Vendor AP
881 * capabilities like NSS, HT, VHT, Band from the ini with the AP's capability
882 * from the beacon and returns true if all the capability matches
883 * @extension: pointer to oui extension data
884 * @attr: pointer to structure containing type of action, ap capabilities
885 *
886 * Return: true or false
887 */
888 static bool
check_for_vendor_ap_capabilities(struct action_oui_extension * extension,struct action_oui_search_attr * attr)889 check_for_vendor_ap_capabilities(struct action_oui_extension *extension,
890 struct action_oui_search_attr *attr)
891 {
892 uint8_t nss_mask;
893
894 if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_NSS) {
895 nss_mask = 1 << (attr->nss - 1);
896 if (!((*extension->capability &
897 ACTION_OUI_CAPABILITY_NSS_MASK) &
898 nss_mask))
899 return false;
900 }
901
902 if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_HT) {
903 if (*extension->capability &
904 ACTION_OUI_CAPABILITY_HT_ENABLE_MASK) {
905 if (!attr->ht_cap)
906 return false;
907 } else {
908 if (attr->ht_cap)
909 return false;
910 }
911 }
912
913 if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_VHT) {
914 if (*extension->capability &
915 ACTION_OUI_CAPABILITY_VHT_ENABLE_MASK) {
916 if (!attr->vht_cap)
917 return false;
918 } else {
919 if (attr->vht_cap)
920 return false;
921 }
922 }
923
924 if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_BAND &&
925 ((attr->enable_5g &&
926 !(*extension->capability & ACTION_CAPABILITY_5G_BAND_MASK)) ||
927 (attr->enable_2g &&
928 !(*extension->capability & ACTION_OUI_CAPABILITY_2G_BAND_MASK))))
929 return false;
930
931 return true;
932 }
933
934 static const uint8_t *
action_oui_get_oui_ptr(struct action_oui_extension * extension,struct action_oui_search_attr * attr)935 action_oui_get_oui_ptr(struct action_oui_extension *extension,
936 struct action_oui_search_attr *attr)
937 {
938 if (!attr->ie_data || !attr->ie_length)
939 return NULL;
940
941 return wlan_get_vendor_ie_ptr_from_oui(extension->oui,
942 extension->oui_length,
943 attr->ie_data,
944 attr->ie_length);
945 }
946
947 bool
action_oui_is_empty(struct action_oui_psoc_priv * psoc_priv,enum action_oui_id action_id)948 action_oui_is_empty(struct action_oui_psoc_priv *psoc_priv,
949 enum action_oui_id action_id)
950 {
951 struct action_oui_priv *oui_priv;
952 qdf_list_t *extension_list;
953
954 oui_priv = psoc_priv->oui_priv[action_id];
955 if (!oui_priv)
956 return true;
957
958 extension_list = &oui_priv->extension_list;
959 qdf_mutex_acquire(&oui_priv->extension_lock);
960 if (qdf_list_empty(extension_list)) {
961 qdf_mutex_release(&oui_priv->extension_lock);
962 return true;
963 }
964 qdf_mutex_release(&oui_priv->extension_lock);
965
966 return false;
967 }
968
validate_vendor_oui_data(struct action_oui_extension * extension,struct action_oui_search_attr * attr)969 static bool validate_vendor_oui_data(struct action_oui_extension *extension,
970 struct action_oui_search_attr *attr)
971 {
972 uint8_t elem_id, elem_len;
973 int32_t left;
974 uint8_t eid = WLAN_MAC_EID_VENDOR;
975 const uint8_t *ptr = NULL;
976 const uint8_t *oui = extension->oui;
977
978 if (!attr->ie_data || !attr->ie_length || !oui)
979 return false;
980
981 ptr = attr->ie_data;
982 left = attr->ie_length;
983
984 while (left >= 2) {
985 elem_id = ptr[0];
986 elem_len = ptr[1];
987 left -= 2;
988
989 if (elem_len > left)
990 return false;
991
992 if (eid == elem_id) {
993 /*
994 * if oui is provided and oui_size is more than left
995 * bytes, then we cannot have match
996 */
997 if (extension->oui_length > left)
998 return false;
999
1000 if (qdf_mem_cmp(&ptr[2], extension->oui,
1001 extension->oui_length) == 0 &&
1002 check_for_vendor_oui_data(extension, ptr))
1003 return true;
1004 }
1005
1006 left -= elem_len;
1007 ptr += (elem_len + 2);
1008 }
1009
1010 return false;
1011 }
1012
1013 bool
action_oui_search(struct action_oui_psoc_priv * psoc_priv,struct action_oui_search_attr * attr,enum action_oui_id action_id)1014 action_oui_search(struct action_oui_psoc_priv *psoc_priv,
1015 struct action_oui_search_attr *attr,
1016 enum action_oui_id action_id)
1017 {
1018 struct action_oui_priv *oui_priv;
1019 struct action_oui_extension_priv *priv_ext;
1020 struct action_oui_extension *extension;
1021 qdf_list_node_t *node = NULL;
1022 qdf_list_node_t *next_node = NULL;
1023 qdf_list_t *extension_list;
1024 QDF_STATUS qdf_status;
1025 const uint8_t *oui_ptr;
1026 bool wildcard_oui = false;
1027
1028 oui_priv = psoc_priv->oui_priv[action_id];
1029 if (!oui_priv) {
1030 action_oui_debug("action oui for id %d is empty",
1031 action_id);
1032 return false;
1033 }
1034
1035 extension_list = &oui_priv->extension_list;
1036 qdf_mutex_acquire(&oui_priv->extension_lock);
1037 if (qdf_list_empty(extension_list)) {
1038 qdf_mutex_release(&oui_priv->extension_lock);
1039 return false;
1040 }
1041
1042 qdf_list_peek_front(extension_list, &node);
1043 while (node) {
1044 priv_ext = qdf_container_of(node,
1045 struct action_oui_extension_priv,
1046 item);
1047 extension = &priv_ext->extension;
1048
1049 /*
1050 * If a wildcard OUI bit is not set in the info_mask, proceed
1051 * to other checks skipping the OUI and vendor data checks
1052 */
1053
1054 if (!(extension->info_mask & ACTION_OUI_INFO_OUI))
1055 wildcard_oui = true;
1056
1057 oui_ptr = action_oui_get_oui_ptr(extension, attr);
1058
1059 if (!oui_ptr && !wildcard_oui)
1060 goto next;
1061
1062 if (extension->data_length && !wildcard_oui &&
1063 !validate_vendor_oui_data(extension, attr))
1064 goto next;
1065
1066 if ((extension->info_mask & ACTION_OUI_INFO_MAC_ADDRESS) &&
1067 !check_for_vendor_ap_mac(extension, attr))
1068 goto next;
1069
1070 if (!check_for_vendor_ap_capabilities(extension, attr))
1071 goto next;
1072
1073 action_oui_debug("Vendor AP/STA found for OUI");
1074 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
1075 extension->oui, extension->oui_length);
1076 qdf_mutex_release(&oui_priv->extension_lock);
1077 return true;
1078 next:
1079 qdf_status = qdf_list_peek_next(extension_list,
1080 node, &next_node);
1081 if (!QDF_IS_STATUS_SUCCESS(qdf_status))
1082 break;
1083
1084 node = next_node;
1085 next_node = NULL;
1086 wildcard_oui = false;
1087 }
1088
1089 qdf_mutex_release(&oui_priv->extension_lock);
1090 return false;
1091 }
1092