|
@@ -132,13 +132,13 @@ uint8_t *util_parse_multi_link_ctrl(uint8_t *element,
|
|
|
}
|
|
|
|
|
|
static
|
|
|
-uint8_t *util_parse_perstaprofile(uint8_t *subelement,
|
|
|
- qdf_size_t len,
|
|
|
- bool is_staprof_reqd,
|
|
|
- qdf_size_t *staprof_len,
|
|
|
- uint8_t *linkid,
|
|
|
- bool *is_macaddr_valid,
|
|
|
- struct qdf_mac_addr *macaddr)
|
|
|
+uint8_t *util_parse_bvmlie_perstaprofile(uint8_t *subelement,
|
|
|
+ qdf_size_t len,
|
|
|
+ bool is_staprof_reqd,
|
|
|
+ qdf_size_t *staprof_len,
|
|
|
+ uint8_t *linkid,
|
|
|
+ bool *is_macaddr_valid,
|
|
|
+ struct qdf_mac_addr *macaddr)
|
|
|
{
|
|
|
qdf_size_t subelement_len = 0;
|
|
|
struct wlan_ml_bv_linfo_perstaprof *perstaprof_fixed;
|
|
@@ -371,7 +371,8 @@ QDF_STATUS util_parse_partner_info_from_linkinfo(uint8_t *linkinfo,
|
|
|
/* Per-STA profile fragmentation support may be added
|
|
|
* once support for this is introduced in the standard.
|
|
|
*/
|
|
|
- endofstainfo = util_parse_perstaprofile(currpos,
|
|
|
+ endofstainfo =
|
|
|
+ util_parse_bvmlie_perstaprofile(currpos,
|
|
|
currlen,
|
|
|
false,
|
|
|
NULL,
|
|
@@ -415,197 +416,1343 @@ QDF_STATUS util_parse_partner_info_from_linkinfo(uint8_t *linkinfo,
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
}
|
|
|
|
|
|
-QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
|
- struct qdf_mac_addr link_addr,
|
|
|
- uint8_t *assoc_link_frame)
|
|
|
+static
|
|
|
+QDF_STATUS util_get_noninheritlists(uint8_t *buff, qdf_size_t buff_len,
|
|
|
+ uint8_t **ninherit_elemlist,
|
|
|
+ qdf_size_t *ninherit_elemlist_len,
|
|
|
+ uint8_t **ninherit_elemextlist,
|
|
|
+ qdf_size_t *ninherit_elemextlist_len)
|
|
|
+{
|
|
|
+ uint8_t *ninherit_ie;
|
|
|
+ qdf_size_t unparsed_len;
|
|
|
+
|
|
|
+ /* Note: This funtionality provided by this helper may be combined with
|
|
|
+ * other, older non-inheritance parsing helper functionality and exposed
|
|
|
+ * as a common API as part of future efforts once the older
|
|
|
+ * functionality can be made generic.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!buff) {
|
|
|
+ mlo_err("Pointer to buffer for IEs is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!buff_len) {
|
|
|
+ mlo_err("IE buffer length is zero");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ninherit_elemlist) {
|
|
|
+ mlo_err("Pointer to Non-Inheritance element ID list array is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ninherit_elemlist_len) {
|
|
|
+ mlo_err("Pointer to Non-Inheritance element ID list array length is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ninherit_elemextlist) {
|
|
|
+ mlo_err("Pointer to Non-Inheritance element ID extension list array is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ninherit_elemextlist_len) {
|
|
|
+ mlo_err("Pointer to Non-Inheritance element ID extension list array length is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ ninherit_ie = NULL;
|
|
|
+ *ninherit_elemlist_len = 0;
|
|
|
+ *ninherit_elemlist = NULL;
|
|
|
+ *ninherit_elemextlist_len = 0;
|
|
|
+ *ninherit_elemextlist = NULL;
|
|
|
+
|
|
|
+ ninherit_ie =
|
|
|
+ (uint8_t *)util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
|
|
|
+ WLAN_EXTN_ELEMID_NONINHERITANCE,
|
|
|
+ buff,
|
|
|
+ buff_len);
|
|
|
+
|
|
|
+ if (ninherit_ie) {
|
|
|
+ if ((ninherit_ie + TAG_LEN_POS) > (buff + buff_len - 1)) {
|
|
|
+ mlo_err_rl("Position of length field of Non-Inheritance element would exceed IE buffer boundary");
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((ninherit_ie + ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) >
|
|
|
+ (buff + buff_len)) {
|
|
|
+ mlo_err_rl("Non-Inheritance element with total length %u would exceed IE buffer boundary",
|
|
|
+ ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) <
|
|
|
+ MIN_NONINHERITANCEELEM_LEN) {
|
|
|
+ mlo_err_rl("Non-Inheritance element size %u is smaller than the minimum required %u",
|
|
|
+ ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN,
|
|
|
+ MIN_NONINHERITANCEELEM_LEN);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Track the number of unparsed octets, excluding the IE header.
|
|
|
+ */
|
|
|
+ unparsed_len = ninherit_ie[TAG_LEN_POS];
|
|
|
+
|
|
|
+ /* Mark the element ID extension as parsed */
|
|
|
+ unparsed_len--;
|
|
|
+
|
|
|
+ *ninherit_elemlist_len = ninherit_ie[ELEM_ID_LIST_LEN_POS];
|
|
|
+ unparsed_len--;
|
|
|
+
|
|
|
+ /* While checking if the Non-Inheritance element ID list length
|
|
|
+ * exceeds the remaining unparsed IE space, we factor in one
|
|
|
+ * octet for the element extension ID list length and subtract
|
|
|
+ * this from the unparsed IE space.
|
|
|
+ */
|
|
|
+ if (*ninherit_elemlist_len > (unparsed_len - 1)) {
|
|
|
+ mlo_err_rl("Non-Inheritance element ID list length %zu exceeds remaining unparsed IE space, minus an octet for element extension ID list length %zu",
|
|
|
+ *ninherit_elemlist_len, unparsed_len - 1);
|
|
|
+
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*ninherit_elemlist_len != 0) {
|
|
|
+ *ninherit_elemlist = ninherit_ie + ELEM_ID_LIST_POS;
|
|
|
+ unparsed_len -= *ninherit_elemlist_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ *ninherit_elemextlist_len =
|
|
|
+ ninherit_ie[ELEM_ID_LIST_LEN_POS + *ninherit_elemlist_len + 1];
|
|
|
+ unparsed_len--;
|
|
|
+
|
|
|
+ if (*ninherit_elemextlist_len > unparsed_len) {
|
|
|
+ mlo_err_rl("Non-Inheritance element ID extension list length %zu exceeds remaining unparsed IE space %zu",
|
|
|
+ *ninherit_elemextlist_len, unparsed_len);
|
|
|
+
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*ninherit_elemextlist_len != 0) {
|
|
|
+ *ninherit_elemextlist = ninherit_ie +
|
|
|
+ ELEM_ID_LIST_LEN_POS + (*ninherit_elemlist_len)
|
|
|
+ + 2;
|
|
|
+ unparsed_len -= *ninherit_elemlist_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (unparsed_len > 0) {
|
|
|
+ mlo_err_rl("Unparsed length is %zu, expected 0",
|
|
|
+ unparsed_len);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If Non-Inheritance element is not found, we still return success,
|
|
|
+ * with the list lengths kept at zero.
|
|
|
+ */
|
|
|
+ mlo_debug("Non-Inheritance element ID list array length=%zu",
|
|
|
+ *ninherit_elemlist_len);
|
|
|
+ mlo_debug("Non-Inheritance element ID extension list array length=%zu",
|
|
|
+ *ninherit_elemextlist_len);
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+QDF_STATUS util_eval_ie_in_noninheritlist(uint8_t *ie, qdf_size_t total_ie_len,
|
|
|
+ uint8_t *ninherit_elemlist,
|
|
|
+ qdf_size_t ninherit_elemlist_len,
|
|
|
+ uint8_t *ninherit_elemextlist,
|
|
|
+ qdf_size_t ninherit_elemextlist_len,
|
|
|
+ bool *is_in_noninheritlist)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Evaluate whether the given IE is in the given Non-Inheritance element
|
|
|
+ * ID list or Non-Inheritance element ID extension list, and update the
|
|
|
+ * result into is_in_noninheritlist. If any list is empty, then the IE
|
|
|
+ * is considered to not be present in that list. Both lists can be
|
|
|
+ * empty.
|
|
|
+ *
|
|
|
+ * If QDF_STATUS_SUCCESS is returned, it means that the evaluation is
|
|
|
+ * successful, and that is_in_noninheritlist contains a valid value
|
|
|
+ * (which could be true or false). If a QDF_STATUS error value is
|
|
|
+ * returned, the value in is_in_noninheritlist is invalid and the caller
|
|
|
+ * should ignore it.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Note: The funtionality provided by this helper may be combined with
|
|
|
+ * other, older non-inheritance parsing helper functionality and exposed
|
|
|
+ * as a common API as part of future efforts once the older
|
|
|
+ * functionality can be made generic.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Except for is_in_noninheritlist and ie, other pointer arguments are
|
|
|
+ * permitted to be NULL if they are inapplicable. If they are
|
|
|
+ * applicable, they will be checked to ensure they are not NULL.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!is_in_noninheritlist) {
|
|
|
+ mlo_err("NULL pointer to flag that indicates if element is in a Non-Inheritance list");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If ninherit_elemlist_len and ninherit_elemextlist_len are both zero
|
|
|
+ * as checked soon in this function, we won't be accessing the IE.
|
|
|
+ * However, we still check right-away if the pointer to the IE is
|
|
|
+ * non-NULL and whether the total IE length is sane enough to access the
|
|
|
+ * element ID and if applicable, the element ID extension, since it
|
|
|
+ * doesn't make sense to set the flag in is_in_noninheritlist for a NULL
|
|
|
+ * IE pointer or an IE whose total length is not sane enough to
|
|
|
+ * distinguish the identity of the IE.
|
|
|
+ */
|
|
|
+ if (!ie) {
|
|
|
+ mlo_err("NULL pointer to IE");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (total_ie_len < (ID_POS + 1)) {
|
|
|
+ mlo_err("Total IE length %zu is smaller than minimum required to access element ID %u",
|
|
|
+ total_ie_len, ID_POS + 1);
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
|
|
|
+ (total_ie_len < (IDEXT_POS + 1))) {
|
|
|
+ mlo_err("Total IE length %zu is smaller than minimum required to access element ID extension %u",
|
|
|
+ total_ie_len, IDEXT_POS + 1);
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ *is_in_noninheritlist = false;
|
|
|
+
|
|
|
+ /* If both the Non-Inheritance element list and Non-Inheritance element
|
|
|
+ * ID extension list are empty, then return success since we can
|
|
|
+ * conclude immediately that the given element does not occur in any
|
|
|
+ * Non-Inheritance list. The is_in_noninheritlist remains set to false
|
|
|
+ * as required.
|
|
|
+ */
|
|
|
+ if (!ninherit_elemlist_len && !ninherit_elemextlist_len)
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ if (ie[ID_POS] != WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ if (!ninherit_elemlist_len)
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ if (!ninherit_elemlist) {
|
|
|
+ mlo_err("NULL pointer to Non-Inheritance element ID list though length of element ID list is %zu",
|
|
|
+ ninherit_elemlist_len);
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ninherit_elemlist_len; i++) {
|
|
|
+ if (ie[ID_POS] == ninherit_elemlist[i]) {
|
|
|
+ *is_in_noninheritlist = true;
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!ninherit_elemextlist_len)
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ if (!ninherit_elemextlist) {
|
|
|
+ mlo_err("NULL pointer to Non-Inheritance element ID extension list though length of element ID extension list is %zu",
|
|
|
+ ninherit_elemextlist_len);
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ninherit_elemextlist_len; i++) {
|
|
|
+ if (ie[IDEXT_POS] == ninherit_elemextlist[i]) {
|
|
|
+ *is_in_noninheritlist = true;
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static inline
|
|
|
+QDF_STATUS util_validate_reportingsta_ie(const uint8_t *reportingsta_ie,
|
|
|
+ const uint8_t *frame_iesection,
|
|
|
+ const qdf_size_t frame_iesection_len)
|
|
|
{
|
|
|
- uint8_t *tmp = NULL;
|
|
|
- const uint8_t *tmp_old, *rsn_ie;
|
|
|
- qdf_size_t sub_len, tmp_rem_len;
|
|
|
- qdf_size_t link_info_len, sta_prof_len = 0;
|
|
|
- uint8_t *subelement;
|
|
|
- uint8_t *pos;
|
|
|
- uint8_t *sub_copy, *orig_copy;
|
|
|
- bool is_bssid_valid;
|
|
|
- struct qdf_mac_addr bssid;
|
|
|
- struct wlan_frame_hdr *hdr;
|
|
|
-
|
|
|
- if (!frame || !len)
|
|
|
+ qdf_size_t reportingsta_ie_size;
|
|
|
+
|
|
|
+ if (!reportingsta_ie) {
|
|
|
+ mlo_err("Pointer to reporting STA IE is NULL");
|
|
|
return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
|
|
|
- pos = assoc_link_frame;
|
|
|
- hdr = (struct wlan_frame_hdr *)pos;
|
|
|
- pos = pos + WLAN_MAC_HDR_LEN_3A;
|
|
|
-
|
|
|
- /* Assoc resp Capability(2) + AID(2) + Status Code(2) */
|
|
|
- qdf_mem_copy(pos, frame, WLAN_ASSOC_RSP_IES_OFFSET);
|
|
|
- pos = pos + WLAN_ASSOC_RSP_IES_OFFSET;
|
|
|
-
|
|
|
- rsn_ie = wlan_get_ie_ptr_from_eid(WLAN_ELEMID_RSN, frame, len);
|
|
|
- if (rsn_ie) {
|
|
|
- qdf_mem_copy(pos, rsn_ie, rsn_ie[1]);
|
|
|
- pos = pos + rsn_ie[1];
|
|
|
- }
|
|
|
- /* find MLO IE */
|
|
|
- subelement = util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
|
|
|
- WLAN_EXTN_ELEMID_MULTI_LINK,
|
|
|
- frame,
|
|
|
- len);
|
|
|
- if (!subelement)
|
|
|
+ if (!frame_iesection) {
|
|
|
+ mlo_err("Pointer to start of IE section in reporting frame is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!frame_iesection_len) {
|
|
|
+ mlo_err("Length of IE section in reporting frame is zero");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((reportingsta_ie + ID_POS) > (frame_iesection +
|
|
|
+ frame_iesection_len - 1)) {
|
|
|
+ mlo_err_rl("Position of element ID field of element for reporting STA would exceed frame IE section boundary");
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((reportingsta_ie + TAG_LEN_POS) > (frame_iesection +
|
|
|
+ frame_iesection_len - 1)) {
|
|
|
+ mlo_err_rl("Position of length field of element with element ID %u for reporting STA would exceed frame IE section boundary",
|
|
|
+ reportingsta_ie[ID_POS]);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
|
|
|
+ ((reportingsta_ie + IDEXT_POS) > (frame_iesection +
|
|
|
+ frame_iesection_len - 1))) {
|
|
|
+ mlo_err_rl("Position of element ID extension field of element would exceed frame IE section boundary");
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
+
|
|
|
+ if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
|
|
|
+ (reportingsta_ie_size < (IDEXT_POS + 1))) {
|
|
|
+ mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access element ID extension %u",
|
|
|
+ reportingsta_ie_size, IDEXT_POS + 1);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((reportingsta_ie + reportingsta_ie_size) >
|
|
|
+ (frame_iesection + frame_iesection_len)) {
|
|
|
+ if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_err_rl("Total size %zu octets of element with element ID %u element ID extension %u for reporting STA would exceed frame IE section boundary",
|
|
|
+ reportingsta_ie_size,
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie[IDEXT_POS]);
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Total size %zu octets of element with element ID %u for reporting STA would exceed frame IE section boundary",
|
|
|
+ reportingsta_ie_size,
|
|
|
+ reportingsta_ie[ID_POS]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static inline
|
|
|
+QDF_STATUS util_validate_sta_prof_ie(const uint8_t *sta_prof_ie,
|
|
|
+ const uint8_t *sta_prof_iesection,
|
|
|
+ const qdf_size_t sta_prof_iesection_len)
|
|
|
+{
|
|
|
+ qdf_size_t sta_prof_ie_size;
|
|
|
+
|
|
|
+ if (!sta_prof_ie) {
|
|
|
+ mlo_err("Pointer to STA profile IE is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sta_prof_iesection) {
|
|
|
+ mlo_err("Pointer to start of IE section in STA profile is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sta_prof_iesection_len) {
|
|
|
+ mlo_err("Length of IE section in STA profile is zero");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((sta_prof_ie + ID_POS) > (sta_prof_iesection +
|
|
|
+ sta_prof_iesection_len - 1)) {
|
|
|
+ mlo_err_rl("Position of element ID field of STA profile element would exceed STA profile IE section boundary");
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((sta_prof_ie + TAG_LEN_POS) > (sta_prof_iesection +
|
|
|
+ sta_prof_iesection_len - 1)) {
|
|
|
+ mlo_err_rl("Position of length field of element with element ID %u in STA profile would exceed STA profile IE section boundary",
|
|
|
+ sta_prof_ie[ID_POS]);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
|
|
|
+ ((sta_prof_ie + IDEXT_POS) > (sta_prof_iesection +
|
|
|
+ sta_prof_iesection_len - 1))) {
|
|
|
+ mlo_err_rl("Position of element ID extension field of element would exceed STA profile IE section boundary");
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
+
|
|
|
+ if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
|
|
|
+ (sta_prof_ie_size < (IDEXT_POS + 1))) {
|
|
|
+ mlo_err_rl("Total length %zu of STA profile element is smaller than minimum required to access element ID extension %u",
|
|
|
+ sta_prof_ie_size, IDEXT_POS + 1);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((sta_prof_ie + sta_prof_ie_size) >
|
|
|
+ (sta_prof_iesection + sta_prof_iesection_len)) {
|
|
|
+ if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_err_rl("Total size %zu octets of element with element ID %u element ID extension %u in STA profile would exceed STA profile IE section boundary",
|
|
|
+ sta_prof_ie_size,
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie[IDEXT_POS]);
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Total size %zu octets of element with element ID %u in STA profile would exceed STA profile IE section boundary",
|
|
|
+ sta_prof_ie_size,
|
|
|
+ sta_prof_ie[ID_POS]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+#define MLO_LINKSPECIFIC_ASSOC_REQ_FC0 0x00
|
|
|
+#define MLO_LINKSPECIFIC_ASSOC_REQ_FC1 0x00
|
|
|
+#define MLO_LINKSPECIFIC_ASSOC_RESP_FC0 0x10
|
|
|
+#define MLO_LINKSPECIFIC_ASSOC_RESP_FC1 0x00
|
|
|
+
|
|
|
+static
|
|
|
+QDF_STATUS util_gen_link_assoc_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len,
|
|
|
+ uint8_t subtype,
|
|
|
+ struct qdf_mac_addr link_addr,
|
|
|
+ uint8_t *link_frame,
|
|
|
+ qdf_size_t link_frame_maxsize,
|
|
|
+ qdf_size_t *link_frame_len)
|
|
|
+{
|
|
|
+ /* Please see documentation for util_gen_link_assoc_req() and
|
|
|
+ * util_gen_link_assoc_resp() for information on the inputs to and
|
|
|
+ * output from this helper, since those APIs are essentially wrappers
|
|
|
+ * over this helper.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Pointer to Multi-Link element */
|
|
|
+ uint8_t *mlie;
|
|
|
+ /* Total length of Multi-Link element sequence (including fragements if
|
|
|
+ * any)
|
|
|
+ */
|
|
|
+ qdf_size_t mlieseqlen;
|
|
|
+ /* Variant (i.e. type) of the Multi-Link element */
|
|
|
+ enum wlan_ml_variant variant;
|
|
|
+ /* Pointer to original copy of Multi-Link element */
|
|
|
+ uint8_t *orig_mlie_copy;
|
|
|
+
|
|
|
+ /* Length of the Link Info */
|
|
|
+ qdf_size_t link_info_len;
|
|
|
+
|
|
|
+ /* Pointer to the IE section that occurs after the fixed fields in the
|
|
|
+ * original frame for the reporting STA.
|
|
|
+ */
|
|
|
+ uint8_t *frame_iesection;
|
|
|
+ /* Offset to the start of the IE section in the original frame for the
|
|
|
+ * reporting STA.
|
|
|
+ */
|
|
|
+ qdf_size_t frame_iesection_offset;
|
|
|
+ /* Total length of the IE section in the original frame for the
|
|
|
+ * reporting STA.
|
|
|
+ */
|
|
|
+ qdf_size_t frame_iesection_len;
|
|
|
+
|
|
|
+ /* Pointer to the IEEE802.11 frame header in the link specific frame
|
|
|
+ * being generated for the reported STA.
|
|
|
+ */
|
|
|
+ struct wlan_frame_hdr *link_frame_hdr;
|
|
|
+ /* Current position in the link specific frame being generated for the
|
|
|
+ * reported STA.
|
|
|
+ */
|
|
|
+ uint8_t *link_frame_currpos;
|
|
|
+ /* Current length of the link specific frame being generated for the
|
|
|
+ * reported STA.
|
|
|
+ */
|
|
|
+ qdf_size_t link_frame_currlen;
|
|
|
+
|
|
|
+ /* Pointer to IE for reporting STA */
|
|
|
+ const uint8_t *reportingsta_ie;
|
|
|
+ /* Total size of IE for reporting STA, inclusive of the element header
|
|
|
+ */
|
|
|
+ qdf_size_t reportingsta_ie_size;
|
|
|
+
|
|
|
+ /* Pointer to current position in STA profile */
|
|
|
+ uint8_t *sta_prof_currpos;
|
|
|
+ /* Remaining length of STA profile */
|
|
|
+ qdf_size_t sta_prof_remlen;
|
|
|
+ /* Pointer to start of IE section in STA profile that occurs after fixed
|
|
|
+ * fields.
|
|
|
+ */
|
|
|
+ uint8_t *sta_prof_iesection;
|
|
|
+ /* Total length of IE section in STA profile */
|
|
|
+ qdf_size_t sta_prof_iesection_len;
|
|
|
+ /* Pointer to current position being processed in IE section in STA
|
|
|
+ * profile.
|
|
|
+ */
|
|
|
+ uint8_t *sta_prof_iesection_currpos;
|
|
|
+ /* Remaining length of IE section in STA profile */
|
|
|
+ qdf_size_t sta_prof_iesection_remlen;
|
|
|
+
|
|
|
+ /* Pointer to IE in STA profile, that occurs within IE section */
|
|
|
+ uint8_t *sta_prof_ie;
|
|
|
+ /* Total size of IE in STA profile, inclusive of the element header */
|
|
|
+ qdf_size_t sta_prof_ie_size;
|
|
|
+
|
|
|
+ /* Pointer to element ID list in Non-Inheritance IE */
|
|
|
+ uint8_t *ninherit_elemlist;
|
|
|
+ /* Length of element ID list in Non-Inheritance IE */
|
|
|
+ qdf_size_t ninherit_elemlist_len;
|
|
|
+ /* Pointer to element ID extension list in Non-Inheritance IE */
|
|
|
+ uint8_t *ninherit_elemextlist;
|
|
|
+ /* Length of element ID extension list in Non-Inheritance IE */
|
|
|
+ qdf_size_t ninherit_elemextlist_len;
|
|
|
+ /* Whether a given IE is in a non-inheritance list */
|
|
|
+ bool is_in_noninheritlist;
|
|
|
+
|
|
|
+ /* Whether MAC address of reported STA is valid */
|
|
|
+ bool is_reportedmacaddr_valid;
|
|
|
+ /* MAC address of reported STA */
|
|
|
+ struct qdf_mac_addr reportedmacaddr;
|
|
|
+
|
|
|
+ /* Other variables for temporary purposes */
|
|
|
+ uint8_t *sub_copy;
|
|
|
+ QDF_STATUS ret;
|
|
|
+
|
|
|
+ if (!frame) {
|
|
|
+ mlo_err("Pointer to original frame is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!frame_len) {
|
|
|
+ mlo_err("Length of original frame is zero");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((subtype != WLAN_FC0_STYPE_ASSOC_REQ) &&
|
|
|
+ (subtype != WLAN_FC0_STYPE_REASSOC_REQ) &&
|
|
|
+ (subtype != WLAN_FC0_STYPE_ASSOC_RESP) &&
|
|
|
+ (subtype != WLAN_FC0_STYPE_REASSOC_RESP)) {
|
|
|
+ mlo_err("802.11 frame subtype %u is invalid", subtype);
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!link_frame) {
|
|
|
+ mlo_err("Pointer to secondary link specific frame is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!link_frame_maxsize) {
|
|
|
+ mlo_err("Maximum size of secondary link specific frame is zero");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!link_frame_len) {
|
|
|
+ mlo_err("Pointer to populated length of secondary link specific frame is NULL");
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ frame_iesection_offset = 0;
|
|
|
+
|
|
|
+ if (subtype == WLAN_FC0_STYPE_ASSOC_REQ) {
|
|
|
+ frame_iesection_offset = WLAN_ASSOC_REQ_IES_OFFSET;
|
|
|
+ } else if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
|
|
|
+ frame_iesection_offset = WLAN_REASSOC_REQ_IES_OFFSET;
|
|
|
+ } else {
|
|
|
+ /* This is a (re)association response */
|
|
|
+ frame_iesection_offset = WLAN_ASSOC_RSP_IES_OFFSET;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (frame_len < frame_iesection_offset) {
|
|
|
+ /* The caller is supposed to have confirmed that this is a valid
|
|
|
+ * frame containing a Multi-Link element. Hence we treat this as
|
|
|
+ * a case of invalid argument being passed to us.
|
|
|
+ */
|
|
|
+ mlo_err("Frame length %zu is smaller than the IE section offset %zu for subtype %u",
|
|
|
+ frame_len, frame_iesection_offset, subtype);
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ frame_iesection_len = frame_len - frame_iesection_offset;
|
|
|
+
|
|
|
+ if (frame_iesection_len == 0) {
|
|
|
+ /* The caller is supposed to have confirmed that this is a valid
|
|
|
+ * frame containing a Multi-Link element. Hence we treat this as
|
|
|
+ * a case of invalid argument being passed to us.
|
|
|
+ */
|
|
|
+ mlo_err("No space left in frame for IE section");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ frame_iesection = frame + frame_iesection_offset;
|
|
|
+
|
|
|
+ mlie = NULL;
|
|
|
+ mlieseqlen = 0;
|
|
|
+
|
|
|
+ ret = util_find_mlie(frame_iesection, frame_iesection_len, &mlie,
|
|
|
+ &mlieseqlen);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret))
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (!mlie) {
|
|
|
+ /* The caller is supposed to have confirmed that a Multi-Link
|
|
|
+ * element is present in the frame. Hence we treat this as a
|
|
|
+ * case of invalid argument being passed to us.
|
|
|
+ */
|
|
|
+ mlo_err("Invalid original frame since no Multi-Link element found");
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Sanity check the Multi-Link element sequence length */
|
|
|
+ if (!mlieseqlen) {
|
|
|
+ mlo_err("Length of Multi-Link element sequence is zero. Investigate.");
|
|
|
return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
|
|
|
- /* EID(1) + len (1) = 2 */
|
|
|
- sub_len = subelement[TAG_LEN_POS] + 2;
|
|
|
- sub_copy = qdf_mem_malloc(sub_len);
|
|
|
- if (!sub_copy)
|
|
|
+ ret = util_get_mlie_variant(mlie, mlieseqlen, (int *)&variant);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret))
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (variant != WLAN_ML_VARIANT_BASIC) {
|
|
|
+ mlo_err_rl("Unexpected variant %u of Multi-Link element.",
|
|
|
+ variant);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Note: Multi-Link element fragmentation support will be added in a
|
|
|
+ * later change. As of now, we temporarily return error if the
|
|
|
+ * Multi-Link element sequence length is greater than the max length for
|
|
|
+ * an IE.
|
|
|
+ */
|
|
|
+ if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
|
|
|
+ mlo_err_rl("Element fragmentation is not yet supported for this API");
|
|
|
+ return QDF_STATUS_E_NOSUPPORT;
|
|
|
+ }
|
|
|
+
|
|
|
+ sub_copy = qdf_mem_malloc(mlieseqlen);
|
|
|
+ if (!sub_copy) {
|
|
|
+ mlo_err_rl("Could not allocate memory for Multi-Link element copy");
|
|
|
return QDF_STATUS_E_NOMEM;
|
|
|
- orig_copy = sub_copy;
|
|
|
- qdf_mem_copy(sub_copy, subelement, sub_len);
|
|
|
+ }
|
|
|
+
|
|
|
+ orig_mlie_copy = sub_copy;
|
|
|
+ qdf_mem_copy(sub_copy, mlie, mlieseqlen);
|
|
|
|
|
|
- /* parse ml ie */
|
|
|
sub_copy = util_parse_multi_link_ctrl(sub_copy,
|
|
|
- sub_len,
|
|
|
+ mlieseqlen,
|
|
|
&link_info_len);
|
|
|
|
|
|
- if (!sub_copy)
|
|
|
- return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ /* As per the standard, the sender must include Link Info for
|
|
|
+ * association request/response. Throw an error if we are unable to
|
|
|
+ * obtain this.
|
|
|
+ */
|
|
|
+ if (!sub_copy) {
|
|
|
+ mlo_err_rl("Unable to successfully parse Multi-Link element control and obtain Link Info");
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
|
|
|
- mlo_debug("dumping hex after parsing multi link ctrl");
|
|
|
+ mlo_debug("Dumping hex after parsing Multi-Link element control");
|
|
|
QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
|
|
|
sub_copy, link_info_len);
|
|
|
|
|
|
- is_bssid_valid = false;
|
|
|
+ sta_prof_remlen = 0;
|
|
|
+ is_reportedmacaddr_valid = false;
|
|
|
+
|
|
|
+ /* Note: We may have a future change to skip subelements which are not
|
|
|
+ * Per-STA Profile, handle more than two links in MLO, handle cases
|
|
|
+ * where we unexpectedly find more Per-STA Profiles than expected, etc.
|
|
|
+ */
|
|
|
|
|
|
/* Parse per-STA profile */
|
|
|
- sub_copy = util_parse_perstaprofile(sub_copy,
|
|
|
- link_info_len,
|
|
|
- true,
|
|
|
- &sta_prof_len,
|
|
|
- NULL,
|
|
|
- &is_bssid_valid,
|
|
|
- &bssid);
|
|
|
-
|
|
|
- if (!is_bssid_valid)
|
|
|
- return QDF_STATUS_E_NULL_VALUE;
|
|
|
+ sta_prof_currpos =
|
|
|
+ util_parse_bvmlie_perstaprofile(sub_copy,
|
|
|
+ link_info_len,
|
|
|
+ true,
|
|
|
+ &sta_prof_remlen,
|
|
|
+ NULL,
|
|
|
+ &is_reportedmacaddr_valid,
|
|
|
+ &reportedmacaddr);
|
|
|
+
|
|
|
+ /* If we do not successfully find a STA Profile, we return an error.
|
|
|
+ * This is because we need to get at least the expected fixed fields,
|
|
|
+ * even if there is an (improbable) total inheritance.
|
|
|
+ */
|
|
|
+ if (!sta_prof_currpos) {
|
|
|
+ mlo_err_rl("Unable to find STA profile");
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
|
|
|
- if (!sub_copy) {
|
|
|
- qdf_mem_copy(pos, frame, len);
|
|
|
- pos += len - WLAN_MAC_HDR_LEN_3A;
|
|
|
- goto update_header;
|
|
|
- }
|
|
|
-
|
|
|
- /* go through IEs in frame and subelement,
|
|
|
- * merge them into new_ie
|
|
|
- */
|
|
|
- tmp_old = util_find_eid(WLAN_ELEMID_SSID, frame, len);
|
|
|
- tmp_old = (tmp_old) ? tmp_old + tmp_old[TAG_LEN_POS] + MIN_IE_LEN : frame;
|
|
|
-
|
|
|
- while (((tmp_old + tmp_old[TAG_LEN_POS] + MIN_IE_LEN) - frame) <= len) {
|
|
|
- tmp = (uint8_t *)util_find_eid(tmp_old[0],
|
|
|
- sub_copy,
|
|
|
- sta_prof_len);
|
|
|
- if (!tmp) {
|
|
|
- /* ie in old ie but not in subelement */
|
|
|
- if (tmp_old[2] != WLAN_EXTN_ELEMID_MULTI_LINK) {
|
|
|
- if ((pos + tmp_old[TAG_LEN_POS] + MIN_IE_LEN) <=
|
|
|
- (assoc_link_frame + len)) {
|
|
|
- qdf_mem_copy(pos, tmp_old,
|
|
|
- (tmp_old[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN));
|
|
|
- pos += tmp_old[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
+ /* As per the standard, the sender sets the MAC address in the per-STA
|
|
|
+ * profile in association request/response. Without this, we cannot
|
|
|
+ * generate the link specific frame.
|
|
|
+ */
|
|
|
+ if (!is_reportedmacaddr_valid) {
|
|
|
+ mlo_err_rl("Unable to get MAC address from per-STA profile");
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ link_frame_currpos = link_frame;
|
|
|
+ *link_frame_len = 0;
|
|
|
+ link_frame_currlen = 0;
|
|
|
+
|
|
|
+ if (link_frame_maxsize < WLAN_MAC_HDR_LEN_3A) {
|
|
|
+ mlo_err("Insufficent space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets",
|
|
|
+ WLAN_MAC_HDR_LEN_3A, link_frame_maxsize);
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ link_frame_currpos += WLAN_MAC_HDR_LEN_3A;
|
|
|
+ link_frame_currlen += WLAN_MAC_HDR_LEN_3A;
|
|
|
+
|
|
|
+ if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
|
|
|
+ (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
|
|
|
+ mlo_debug("Populating fixed fields for (re)assoc req in link specific frame");
|
|
|
+
|
|
|
+ if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
|
|
|
+ mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
|
|
|
+ sta_prof_remlen,
|
|
|
+ WLAN_CAPABILITYINFO_LEN);
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Capability information is specific to the link. Copy this
|
|
|
+ * from the STA profile.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ((link_frame_maxsize - link_frame_currlen) <
|
|
|
+ WLAN_CAPABILITYINFO_LEN) {
|
|
|
+ mlo_err("Insufficent space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
|
|
|
+ WLAN_CAPABILITYINFO_LEN,
|
|
|
+ (link_frame_maxsize - link_frame_currlen));
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
|
|
|
+ WLAN_CAPABILITYINFO_LEN);
|
|
|
+ link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
|
|
|
+ link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
|
|
|
+ mlo_debug("Added Capablity Info field (%u octets) to link specific frame",
|
|
|
+ WLAN_CAPABILITYINFO_LEN);
|
|
|
+
|
|
|
+ sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
|
|
|
+ sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
|
|
|
+
|
|
|
+ /* Listen Interval is common between all links. Copy this from
|
|
|
+ * the reporting section of the frame.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ((link_frame_maxsize - link_frame_currlen) <
|
|
|
+ WLAN_LISTENINTERVAL_LEN) {
|
|
|
+ mlo_err("Insufficent space in link specific frame for Listen Interval field. Required: %u octets, available: %zu octets",
|
|
|
+ WLAN_LISTENINTERVAL_LEN,
|
|
|
+ (link_frame_maxsize - link_frame_currlen));
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ frame + WLAN_CAPABILITYINFO_LEN,
|
|
|
+ WLAN_LISTENINTERVAL_LEN);
|
|
|
+ link_frame_currpos += WLAN_LISTENINTERVAL_LEN;
|
|
|
+ link_frame_currlen += WLAN_LISTENINTERVAL_LEN;
|
|
|
+ mlo_debug("Added Listen Interval field (%u octets) to link specific frame",
|
|
|
+ WLAN_LISTENINTERVAL_LEN);
|
|
|
+
|
|
|
+ if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
|
|
|
+ /* Current AP address is common between all links. Copy
|
|
|
+ * this from the reporting section of the frame.
|
|
|
+ */
|
|
|
+ if ((link_frame_maxsize - link_frame_currlen) <
|
|
|
+ QDF_MAC_ADDR_SIZE) {
|
|
|
+ mlo_err("Insufficent space in link specific frame for current AP address. Required: %u octets, available: %zu octets",
|
|
|
+ QDF_MAC_ADDR_SIZE,
|
|
|
+ (link_frame_maxsize -
|
|
|
+ link_frame_currlen));
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ frame + WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_LISTENINTERVAL_LEN,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+ link_frame_currpos += QDF_MAC_ADDR_SIZE;
|
|
|
+ link_frame_currlen += QDF_MAC_ADDR_SIZE;
|
|
|
+ mlo_debug("Reassoc req: Added Current AP address field (%u octets) to link specific frame",
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* This is a (re)association response */
|
|
|
+ mlo_debug("Populating fixed fields for (re)assoc resp in link specific frame");
|
|
|
+
|
|
|
+ if (sta_prof_remlen <
|
|
|
+ (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
|
|
|
+ mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info + length of Status Code %u",
|
|
|
+ sta_prof_remlen,
|
|
|
+ WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_STATUSCODE_LEN);
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Capability information and Status Code are specific to the
|
|
|
+ * link. Copy these from the STA profile.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ((link_frame_maxsize - link_frame_currlen) <
|
|
|
+ (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
|
|
|
+ mlo_err("Insufficent space in link specific frame for Capability Info and Status Code fields. Required: %u octets, available: %zu octets",
|
|
|
+ WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN,
|
|
|
+ (link_frame_maxsize - link_frame_currlen));
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
|
|
|
+ (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN));
|
|
|
+ link_frame_currpos += (WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_STATUSCODE_LEN);
|
|
|
+ link_frame_currlen += (WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_STATUSCODE_LEN);
|
|
|
+ mlo_debug("Added Capability Info and Status Code fields (%u octets) to link specific frame",
|
|
|
+ WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN);
|
|
|
+
|
|
|
+ sta_prof_currpos += (WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_STATUSCODE_LEN);
|
|
|
+ sta_prof_remlen -= (WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_STATUSCODE_LEN);
|
|
|
+
|
|
|
+ /* AID is common between all links. Copy this from the original
|
|
|
+ * frame.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ((link_frame_maxsize - link_frame_currlen) < WLAN_AID_LEN) {
|
|
|
+ mlo_err("Insufficent space in link specific frame for AID field. Required: %u octets, available: %zu octets",
|
|
|
+ WLAN_AID_LEN,
|
|
|
+ (link_frame_maxsize - link_frame_currlen));
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ frame + WLAN_CAPABILITYINFO_LEN +
|
|
|
+ WLAN_STATUSCODE_LEN,
|
|
|
+ WLAN_AID_LEN);
|
|
|
+ link_frame_currpos += WLAN_AID_LEN;
|
|
|
+ link_frame_currlen += WLAN_AID_LEN;
|
|
|
+ mlo_debug("Added AID field (%u octets) to link specific frame",
|
|
|
+ WLAN_AID_LEN);
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_iesection = sta_prof_currpos;
|
|
|
+ sta_prof_iesection_len = sta_prof_remlen;
|
|
|
+
|
|
|
+ /* Populate non-inheritance lists if applicable */
|
|
|
+ ninherit_elemlist_len = 0;
|
|
|
+ ninherit_elemlist = NULL;
|
|
|
+ ninherit_elemextlist_len = 0;
|
|
|
+ ninherit_elemextlist = NULL;
|
|
|
+
|
|
|
+ ret = util_get_noninheritlists(sta_prof_iesection,
|
|
|
+ sta_prof_iesection_len,
|
|
|
+ &ninherit_elemlist,
|
|
|
+ &ninherit_elemlist_len,
|
|
|
+ &ninherit_elemextlist,
|
|
|
+ &ninherit_elemextlist_len);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Go through IEs of the reporting STA, and those in STA profile, merge
|
|
|
+ * them into link_frame (except for elements in the Non-Inheritance
|
|
|
+ * list).
|
|
|
+ *
|
|
|
+ * Note: Currently, only 2-link MLO is supported here. We may have a
|
|
|
+ * future change to expand to more links.
|
|
|
+ */
|
|
|
+ reportingsta_ie = util_find_eid(WLAN_ELEMID_SSID, frame_iesection,
|
|
|
+ frame_iesection_len);
|
|
|
+
|
|
|
+ if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
|
|
|
+ (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
|
|
|
+ /* Sanity check that the SSID element is present for the
|
|
|
+ * reporting STA. There is no stipulation in the standard for
|
|
|
+ * the STA profile in this regard, so we do not check the STA
|
|
|
+ * profile for the SSID element.
|
|
|
+ */
|
|
|
+ if (!reportingsta_ie) {
|
|
|
+ mlo_err_rl("SSID element not found for reporting STA for (re)association request.");
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* This is a (re)association response. Sanity check that the
|
|
|
+ * SSID element is present neither for the reporting STA nor in
|
|
|
+ * the STA profile.
|
|
|
+ */
|
|
|
+ if (reportingsta_ie) {
|
|
|
+ mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present.");
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID,
|
|
|
+ sta_prof_iesection,
|
|
|
+ sta_prof_iesection_len);
|
|
|
+
|
|
|
+ if (sta_prof_ie) {
|
|
|
+ mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present.");
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ reportingsta_ie = reportingsta_ie ? reportingsta_ie : frame_iesection;
|
|
|
+
|
|
|
+ ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection,
|
|
|
+ frame_iesection_len);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
+
|
|
|
+ while (((reportingsta_ie + reportingsta_ie_size) - frame_iesection)
|
|
|
+ <= frame_iesection_len) {
|
|
|
+ /* Skip Multi-Link element */
|
|
|
+ if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
|
|
|
+ (reportingsta_ie[IDEXT_POS] ==
|
|
|
+ WLAN_EXTN_ELEMID_MULTI_LINK)) {
|
|
|
+ if (((reportingsta_ie + reportingsta_ie_size) -
|
|
|
+ frame_iesection) == frame_iesection_len)
|
|
|
+ break;
|
|
|
+
|
|
|
+ reportingsta_ie += reportingsta_ie_size;
|
|
|
+
|
|
|
+ ret = util_validate_reportingsta_ie(reportingsta_ie,
|
|
|
+ frame_iesection,
|
|
|
+ frame_iesection_len);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
|
|
|
+ MIN_IE_LEN;
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_ie = NULL;
|
|
|
+ sta_prof_ie_size = 0;
|
|
|
+
|
|
|
+ if (sta_prof_iesection_len) {
|
|
|
+ if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ sta_prof_ie = (uint8_t *)util_find_extn_eid(reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie[IDEXT_POS],
|
|
|
+ sta_prof_iesection,
|
|
|
+ sta_prof_iesection_len);
|
|
|
+ } else {
|
|
|
+ sta_prof_ie = (uint8_t *)util_find_eid(reportingsta_ie[ID_POS],
|
|
|
+ sta_prof_iesection,
|
|
|
+ sta_prof_iesection_len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sta_prof_ie) {
|
|
|
+ /* IE is present for reporting STA, but not in STA
|
|
|
+ * profile.
|
|
|
+ */
|
|
|
+
|
|
|
+ is_in_noninheritlist = false;
|
|
|
+
|
|
|
+ ret = util_eval_ie_in_noninheritlist((uint8_t *)reportingsta_ie,
|
|
|
+ reportingsta_ie_size,
|
|
|
+ ninherit_elemlist,
|
|
|
+ ninherit_elemlist_len,
|
|
|
+ ninherit_elemextlist,
|
|
|
+ ninherit_elemextlist_len,
|
|
|
+ &is_in_noninheritlist);
|
|
|
+
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!is_in_noninheritlist) {
|
|
|
+ if ((link_frame_currpos +
|
|
|
+ reportingsta_ie_size) <=
|
|
|
+ (link_frame + link_frame_maxsize)) {
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ reportingsta_ie,
|
|
|
+ reportingsta_ie_size);
|
|
|
+
|
|
|
+ link_frame_currpos +=
|
|
|
+ reportingsta_ie_size;
|
|
|
+ link_frame_currlen +=
|
|
|
+ reportingsta_ie_size;
|
|
|
+
|
|
|
+ if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) present for reporting STA but not in STA profile. Copied IE from reporting frame to link specific frame",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie[IDEXT_POS],
|
|
|
+ reportingsta_ie_size);
|
|
|
+ } else {
|
|
|
+ mlo_debug("IE with element ID : %u (%zu octets) present for reporting STA but not in STA profile. Copied IE from reporting frame to link specific frame",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie_size);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie[IDEXT_POS],
|
|
|
+ reportingsta_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) present for reporting STA but not in STA profile. However it is in Non-Inheritance list, hence ignoring.",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie[IDEXT_POS],
|
|
|
+ reportingsta_ie_size);
|
|
|
+ } else {
|
|
|
+ mlo_debug("IE with element ID : %u (%zu octets) present for reporting STA but not in STA profile. However it is in Non-Inheritance list, hence ignoring.",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie_size);
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- /* ie in transmitting ie also in subelement,
|
|
|
- * copy from subelement and flag the ie in subelement
|
|
|
- * as copied (by setting eid field to 0xff). For
|
|
|
- * vendor ie, compare OUI + type + subType to
|
|
|
- * determine if they are the same ie.
|
|
|
+ /* IE is present for reporting STA and also in STA
|
|
|
+ * profile, copy from STA profile and flag the IE in STA
|
|
|
+ * profile as copied (by setting EID field to 0). The
|
|
|
+ * SSID element (with EID 0) is processed first to
|
|
|
+ * enable this. For vendor IE, compare OUI + type +
|
|
|
+ * subType to determine if they are the same IE.
|
|
|
+ */
|
|
|
+ /* Note: This may be revisited in a future change, to
|
|
|
+ * adhere to provisions in the standard for multiple
|
|
|
+ * occurrences of a given element ID/extension element
|
|
|
+ * ID.
|
|
|
*/
|
|
|
- tmp_rem_len = sta_prof_len - (tmp - sub_copy);
|
|
|
- if (tmp_old[0] == WLAN_ELEMID_VENDOR &&
|
|
|
- tmp_rem_len >= MIN_VENDOR_TAG_LEN) {
|
|
|
- if (!qdf_mem_cmp(tmp_old + PAYLOAD_START_POS,
|
|
|
- tmp + PAYLOAD_START_POS,
|
|
|
+
|
|
|
+ ret = util_validate_sta_prof_ie(sta_prof_ie,
|
|
|
+ sta_prof_iesection,
|
|
|
+ sta_prof_iesection_len);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] +
|
|
|
+ MIN_IE_LEN;
|
|
|
+
|
|
|
+ sta_prof_iesection_remlen =
|
|
|
+ sta_prof_iesection_len -
|
|
|
+ (sta_prof_ie - sta_prof_iesection);
|
|
|
+
|
|
|
+ if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
|
|
|
+ (sta_prof_iesection_remlen >= MIN_VENDOR_TAG_LEN)) {
|
|
|
+ if (!qdf_mem_cmp(reportingsta_ie +
|
|
|
+ PAYLOAD_START_POS,
|
|
|
+ sta_prof_ie +
|
|
|
+ PAYLOAD_START_POS,
|
|
|
OUI_LEN)) {
|
|
|
- /* same vendor ie, copy from
|
|
|
- * subelement
|
|
|
+ /* Same vendor IE, copy from STA profile
|
|
|
*/
|
|
|
- if ((pos + tmp[TAG_LEN_POS] + MIN_IE_LEN) <=
|
|
|
- (assoc_link_frame + len)) {
|
|
|
- qdf_mem_copy(pos, tmp,
|
|
|
- tmp[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN);
|
|
|
- pos += tmp[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
- tmp[0] = 0;
|
|
|
+ if ((link_frame_currpos +
|
|
|
+ sta_prof_ie_size) <=
|
|
|
+ (link_frame +
|
|
|
+ link_frame_maxsize)) {
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ sta_prof_ie,
|
|
|
+ sta_prof_ie_size);
|
|
|
+
|
|
|
+ link_frame_currpos +=
|
|
|
+ sta_prof_ie_size;
|
|
|
+ link_frame_currlen +=
|
|
|
+ sta_prof_ie_size;
|
|
|
+
|
|
|
+ mlo_debug("Vendor IE (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame",
|
|
|
+ sta_prof_ie_size);
|
|
|
+
|
|
|
+ sta_prof_ie[0] = 0;
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
}
|
|
|
} else {
|
|
|
- if ((pos + tmp_old[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN) <=
|
|
|
- (assoc_link_frame + len)) {
|
|
|
- qdf_mem_copy(pos, tmp_old,
|
|
|
- tmp_old[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN);
|
|
|
- pos += tmp_old[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN;
|
|
|
+ if ((link_frame_currpos +
|
|
|
+ reportingsta_ie_size) <=
|
|
|
+ (link_frame +
|
|
|
+ link_frame_maxsize)) {
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ reportingsta_ie,
|
|
|
+ reportingsta_ie_size);
|
|
|
+
|
|
|
+ link_frame_currpos +=
|
|
|
+ reportingsta_ie_size;
|
|
|
+ link_frame_currlen +=
|
|
|
+ reportingsta_ie_size;
|
|
|
+
|
|
|
+ mlo_debug("Vendor IE (%zu octets) present for reporting STA but not present in STA profile. Copied IE from reporting frame to link specific frame",
|
|
|
+ reportingsta_ie_size);
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ reportingsta_ie[ID_POS],
|
|
|
+ reportingsta_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
}
|
|
|
}
|
|
|
- } else if (tmp_old[0] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
- if (tmp_old[PAYLOAD_START_POS] ==
|
|
|
- tmp[PAYLOAD_START_POS]) {
|
|
|
- /* same ie, copy from subelement */
|
|
|
- if ((pos + tmp[TAG_LEN_POS] + MIN_IE_LEN) <=
|
|
|
- (assoc_link_frame + len)) {
|
|
|
- qdf_mem_copy(pos, tmp,
|
|
|
- tmp[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN);
|
|
|
- pos += tmp[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
- tmp[0] = 0;
|
|
|
+ } else {
|
|
|
+ /* Copy IE from STA profile into link specific
|
|
|
+ * frame.
|
|
|
+ */
|
|
|
+ if ((link_frame_currpos + sta_prof_ie_size) <=
|
|
|
+ (link_frame + link_frame_maxsize)) {
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ sta_prof_ie,
|
|
|
+ sta_prof_ie_size);
|
|
|
+
|
|
|
+ link_frame_currpos += sta_prof_ie_size;
|
|
|
+ link_frame_currlen +=
|
|
|
+ sta_prof_ie_size;
|
|
|
+
|
|
|
+ if (reportingsta_ie[ID_POS] ==
|
|
|
+ WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie[IDEXT_POS],
|
|
|
+ sta_prof_ie_size);
|
|
|
+ } else {
|
|
|
+ mlo_debug("IE with element ID : %u (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie_size);
|
|
|
}
|
|
|
+
|
|
|
+ sta_prof_ie[0] = 0;
|
|
|
} else {
|
|
|
- if ((pos + tmp_old[TAG_LEN_POS] + MIN_IE_LEN) <=
|
|
|
- (assoc_link_frame + len)) {
|
|
|
- qdf_mem_copy(pos, tmp_old,
|
|
|
- tmp_old[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN);
|
|
|
- pos += tmp_old[TAG_LEN_POS] +
|
|
|
- MIN_IE_LEN;
|
|
|
+ if (sta_prof_ie[ID_POS] ==
|
|
|
+ WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie[IDEXT_POS],
|
|
|
+ sta_prof_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
}
|
|
|
- }
|
|
|
- } else {
|
|
|
- /* copy ie from subelement into new ie */
|
|
|
- if ((pos + tmp[TAG_LEN_POS] + MIN_IE_LEN) <=
|
|
|
- (assoc_link_frame + len)) {
|
|
|
- qdf_mem_copy(pos, tmp,
|
|
|
- tmp[TAG_LEN_POS] + MIN_IE_LEN);
|
|
|
- pos += tmp[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
- tmp[0] = 0;
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (((tmp_old + tmp_old[TAG_LEN_POS] + MIN_IE_LEN) - frame) >= len)
|
|
|
+ if (((reportingsta_ie + reportingsta_ie_size) -
|
|
|
+ frame_iesection) == frame_iesection_len)
|
|
|
break;
|
|
|
|
|
|
- tmp_old += tmp_old[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
+ reportingsta_ie += reportingsta_ie_size;
|
|
|
+
|
|
|
+ ret = util_validate_reportingsta_ie(reportingsta_ie,
|
|
|
+ frame_iesection,
|
|
|
+ frame_iesection_len);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
|
|
|
+ MIN_IE_LEN;
|
|
|
}
|
|
|
|
|
|
-update_header:
|
|
|
- /* Copy the link mac addr */
|
|
|
- qdf_mem_copy(hdr->i_addr3, bssid.bytes,
|
|
|
- QDF_MAC_ADDR_SIZE);
|
|
|
- qdf_mem_copy(hdr->i_addr2, bssid.bytes,
|
|
|
- QDF_MAC_ADDR_SIZE);
|
|
|
- qdf_mem_copy(hdr->i_addr1, &link_addr,
|
|
|
- QDF_MAC_ADDR_SIZE);
|
|
|
- hdr->i_fc[0] = FC0_IEEE_MGMT_FRM;
|
|
|
- hdr->i_fc[1] = FC1_IEEE_MGMT_FRM;
|
|
|
- /* seq num not used so not populated */
|
|
|
- qdf_mem_free(orig_copy);
|
|
|
+ /* Go through the remaining unprocessed IEs in STA profile and copy them
|
|
|
+ * to the link specific frame. The processed ones are marked with 0 in
|
|
|
+ * the first octet. The first octet corresponds to the element ID. In
|
|
|
+ * the case of (re)association request, the element with actual ID
|
|
|
+ * WLAN_ELEMID_SSID(0) has already been copied to the link specific
|
|
|
+ * frame. In the case of (re)association response, it has been verified
|
|
|
+ * that the element with actual ID WLAN_ELEMID_SSID(0) is present
|
|
|
+ * neither for the reporting STA nor in the STA profile.
|
|
|
+ */
|
|
|
+ sta_prof_iesection_currpos = sta_prof_iesection;
|
|
|
+ sta_prof_iesection_remlen = sta_prof_iesection_len;
|
|
|
+
|
|
|
+ while (sta_prof_iesection_remlen > 0) {
|
|
|
+ sta_prof_ie = sta_prof_iesection_currpos;
|
|
|
+ ret = util_validate_sta_prof_ie(sta_prof_ie,
|
|
|
+ sta_prof_iesection_currpos,
|
|
|
+ sta_prof_iesection_remlen);
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret)) {
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
|
|
|
+
|
|
|
+ if (!sta_prof_ie[0]) {
|
|
|
+ /* Skip this, since it has already been processed */
|
|
|
+ sta_prof_iesection_currpos += sta_prof_ie_size;
|
|
|
+ sta_prof_iesection_remlen -= sta_prof_ie_size;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Copy IE from STA profile into link specific frame. */
|
|
|
+ if ((link_frame_currpos + sta_prof_ie_size) <=
|
|
|
+ (link_frame + link_frame_maxsize)) {
|
|
|
+ qdf_mem_copy(link_frame_currpos,
|
|
|
+ sta_prof_ie,
|
|
|
+ sta_prof_ie_size);
|
|
|
+
|
|
|
+ link_frame_currpos += sta_prof_ie_size;
|
|
|
+ link_frame_currlen +=
|
|
|
+ sta_prof_ie_size;
|
|
|
+
|
|
|
+ if (reportingsta_ie[ID_POS] ==
|
|
|
+ WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie[IDEXT_POS],
|
|
|
+ sta_prof_ie_size);
|
|
|
+ } else {
|
|
|
+ mlo_debug("IE with element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie_size);
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_ie[0] = 0;
|
|
|
+ } else {
|
|
|
+ if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie[IDEXT_POS],
|
|
|
+ sta_prof_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+ } else {
|
|
|
+ mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
|
|
|
+ sta_prof_ie[ID_POS],
|
|
|
+ sta_prof_ie_size,
|
|
|
+ link_frame_maxsize -
|
|
|
+ link_frame_currlen);
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_prof_iesection_currpos += sta_prof_ie_size;
|
|
|
+ sta_prof_iesection_remlen -= sta_prof_ie_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Copy the link MAC addr */
|
|
|
+ link_frame_hdr = (struct wlan_frame_hdr *)link_frame;
|
|
|
+
|
|
|
+ if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
|
|
|
+ (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
|
|
|
+ qdf_mem_copy(link_frame_hdr->i_addr3, &link_addr,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+ qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+ qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+
|
|
|
+ link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_REQ_FC0;
|
|
|
+ link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_REQ_FC1;
|
|
|
+ } else {
|
|
|
+ /* This is a (re)association response */
|
|
|
+
|
|
|
+ qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+ qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+ qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
+
|
|
|
+ link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_RESP_FC0;
|
|
|
+ link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_RESP_FC1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Seq num not used so not populated */
|
|
|
+
|
|
|
+ qdf_mem_free(orig_mlie_copy);
|
|
|
+
|
|
|
+ *link_frame_len = link_frame_currlen;
|
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
}
|
|
|
|
|
|
+QDF_STATUS
|
|
|
+util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
|
|
|
+ struct qdf_mac_addr link_addr,
|
|
|
+ uint8_t *link_frame,
|
|
|
+ qdf_size_t link_frame_maxsize,
|
|
|
+ qdf_size_t *link_frame_len)
|
|
|
+{
|
|
|
+ return util_gen_link_assoc_reqrsp_cmn(frame, frame_len,
|
|
|
+ (isreassoc ? WLAN_FC0_STYPE_REASSOC_REQ :
|
|
|
+ WLAN_FC0_STYPE_ASSOC_REQ),
|
|
|
+ link_addr, link_frame, link_frame_maxsize,
|
|
|
+ link_frame_len);
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS
|
|
|
+util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
|
|
|
+ struct qdf_mac_addr link_addr,
|
|
|
+ uint8_t *link_frame,
|
|
|
+ qdf_size_t link_frame_maxsize,
|
|
|
+ qdf_size_t *link_frame_len)
|
|
|
+{
|
|
|
+ return util_gen_link_assoc_reqrsp_cmn(frame, frame_len,
|
|
|
+ (isreassoc ? WLAN_FC0_STYPE_REASSOC_RESP :
|
|
|
+ WLAN_FC0_STYPE_ASSOC_RESP),
|
|
|
+ link_addr, link_frame, link_frame_maxsize,
|
|
|
+ link_frame_len);
|
|
|
+}
|
|
|
+
|
|
|
QDF_STATUS
|
|
|
util_find_mlie(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
|
|
|
qdf_size_t *mlieseqlen)
|