Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * NXP Wireless LAN device driver: management IE handling- setting and
0004  * deleting IE.
0005  *
0006  * Copyright 2011-2020 NXP
0007  */
0008 
0009 #include "main.h"
0010 
0011 /* This function checks if current IE index is used by any on other interface.
0012  * Return: -1: yes, current IE index is used by someone else.
0013  *          0: no, current IE index is NOT used by other interface.
0014  */
0015 static int
0016 mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
0017 {
0018     int i;
0019     struct mwifiex_adapter *adapter = priv->adapter;
0020     struct mwifiex_ie *ie;
0021 
0022     for (i = 0; i < adapter->priv_num; i++) {
0023         if (adapter->priv[i] != priv) {
0024             ie = &adapter->priv[i]->mgmt_ie[idx];
0025             if (ie->mgmt_subtype_mask && ie->ie_length)
0026                 return -1;
0027         }
0028     }
0029 
0030     return 0;
0031 }
0032 
0033 /* Get unused IE index. This index will be used for setting new IE */
0034 static int
0035 mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
0036                struct mwifiex_ie *ie, u16 *index)
0037 {
0038     u16 mask, len, i;
0039 
0040     for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
0041         mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
0042         len = le16_to_cpu(ie->ie_length);
0043 
0044         if (mask == MWIFIEX_AUTO_IDX_MASK)
0045             continue;
0046 
0047         if (mask == subtype_mask) {
0048             if (len > IEEE_MAX_IE_SIZE)
0049                 continue;
0050 
0051             *index = i;
0052             return 0;
0053         }
0054 
0055         if (!priv->mgmt_ie[i].ie_length) {
0056             if (mwifiex_ie_index_used_by_other_intf(priv, i))
0057                 continue;
0058 
0059             *index = i;
0060             return 0;
0061         }
0062     }
0063 
0064     return -1;
0065 }
0066 
0067 /* This function prepares IE data buffer for command to be sent to FW */
0068 static int
0069 mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
0070                  struct mwifiex_ie_list *ie_list)
0071 {
0072     u16 travel_len, index, mask;
0073     s16 input_len, tlv_len;
0074     struct mwifiex_ie *ie;
0075     u8 *tmp;
0076 
0077     input_len = le16_to_cpu(ie_list->len);
0078     travel_len = sizeof(struct mwifiex_ie_types_header);
0079 
0080     ie_list->len = 0;
0081 
0082     while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
0083         ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
0084         tlv_len = le16_to_cpu(ie->ie_length);
0085         travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
0086 
0087         if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
0088             return -1;
0089         index = le16_to_cpu(ie->ie_index);
0090         mask = le16_to_cpu(ie->mgmt_subtype_mask);
0091 
0092         if (index == MWIFIEX_AUTO_IDX_MASK) {
0093             /* automatic addition */
0094             if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
0095                 return -1;
0096             if (index == MWIFIEX_AUTO_IDX_MASK)
0097                 return -1;
0098 
0099             tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
0100             memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
0101             priv->mgmt_ie[index].ie_length = ie->ie_length;
0102             priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
0103             priv->mgmt_ie[index].mgmt_subtype_mask =
0104                             cpu_to_le16(mask);
0105 
0106             ie->ie_index = cpu_to_le16(index);
0107         } else {
0108             if (mask != MWIFIEX_DELETE_MASK)
0109                 return -1;
0110             /*
0111              * Check if this index is being used on any
0112              * other interface.
0113              */
0114             if (mwifiex_ie_index_used_by_other_intf(priv, index))
0115                 return -1;
0116 
0117             ie->ie_length = 0;
0118             memcpy(&priv->mgmt_ie[index], ie,
0119                    sizeof(struct mwifiex_ie));
0120         }
0121 
0122         le16_unaligned_add_cpu(&ie_list->len,
0123                        le16_to_cpu(
0124                         priv->mgmt_ie[index].ie_length) +
0125                        MWIFIEX_IE_HDR_SIZE);
0126         input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
0127     }
0128 
0129     if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
0130         return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
0131                     HostCmd_ACT_GEN_SET,
0132                     UAP_CUSTOM_IE_I, ie_list, true);
0133 
0134     return 0;
0135 }
0136 
0137 /* Copy individual custom IEs for beacon, probe response and assoc response
0138  * and prepare single structure for IE setting.
0139  * This function also updates allocated IE indices from driver.
0140  */
0141 static int
0142 mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
0143                  struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
0144                  struct mwifiex_ie *pr_ie, u16 *probe_idx,
0145                  struct mwifiex_ie *ar_ie, u16 *assoc_idx)
0146 {
0147     struct mwifiex_ie_list *ap_custom_ie;
0148     u8 *pos;
0149     u16 len;
0150     int ret;
0151 
0152     ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
0153     if (!ap_custom_ie)
0154         return -ENOMEM;
0155 
0156     ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
0157     pos = (u8 *)ap_custom_ie->ie_list;
0158 
0159     if (beacon_ie) {
0160         len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
0161               le16_to_cpu(beacon_ie->ie_length);
0162         memcpy(pos, beacon_ie, len);
0163         pos += len;
0164         le16_unaligned_add_cpu(&ap_custom_ie->len, len);
0165     }
0166     if (pr_ie) {
0167         len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
0168               le16_to_cpu(pr_ie->ie_length);
0169         memcpy(pos, pr_ie, len);
0170         pos += len;
0171         le16_unaligned_add_cpu(&ap_custom_ie->len, len);
0172     }
0173     if (ar_ie) {
0174         len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
0175               le16_to_cpu(ar_ie->ie_length);
0176         memcpy(pos, ar_ie, len);
0177         pos += len;
0178         le16_unaligned_add_cpu(&ap_custom_ie->len, len);
0179     }
0180 
0181     ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
0182 
0183     pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
0184     if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
0185         /* save beacon ie index after auto-indexing */
0186         *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
0187         len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
0188               le16_to_cpu(beacon_ie->ie_length);
0189         pos += len;
0190     }
0191     if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
0192         /* save probe resp ie index after auto-indexing */
0193         *probe_idx = *((u16 *)pos);
0194         len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
0195               le16_to_cpu(pr_ie->ie_length);
0196         pos += len;
0197     }
0198     if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
0199         /* save assoc resp ie index after auto-indexing */
0200         *assoc_idx = *((u16 *)pos);
0201 
0202     kfree(ap_custom_ie);
0203     return ret;
0204 }
0205 
0206 /* This function checks if the vendor specified IE is present in passed buffer
0207  * and copies it to mwifiex_ie structure.
0208  * Function takes pointer to struct mwifiex_ie pointer as argument.
0209  * If the vendor specified IE is present then memory is allocated for
0210  * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
0211  * this memory.
0212  */
0213 static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
0214                 struct mwifiex_ie **ie_ptr, u16 mask,
0215                 unsigned int oui, u8 oui_type)
0216 {
0217     struct ieee_types_header *vs_ie;
0218     struct mwifiex_ie *ie = *ie_ptr;
0219     const u8 *vendor_ie;
0220 
0221     vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
0222     if (vendor_ie) {
0223         if (!*ie_ptr) {
0224             *ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
0225                       GFP_KERNEL);
0226             if (!*ie_ptr)
0227                 return -ENOMEM;
0228             ie = *ie_ptr;
0229         }
0230 
0231         vs_ie = (struct ieee_types_header *)vendor_ie;
0232         if (le16_to_cpu(ie->ie_length) + vs_ie->len + 2 >
0233             IEEE_MAX_IE_SIZE)
0234             return -EINVAL;
0235         memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
0236                vs_ie, vs_ie->len + 2);
0237         le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2);
0238         ie->mgmt_subtype_mask = cpu_to_le16(mask);
0239         ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
0240     }
0241 
0242     *ie_ptr = ie;
0243     return 0;
0244 }
0245 
0246 /* This function parses beacon IEs, probe response IEs, association response IEs
0247  * from cfg80211_ap_settings->beacon and sets these IE to FW.
0248  */
0249 static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
0250                         struct cfg80211_beacon_data *data)
0251 {
0252     struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
0253     u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
0254     u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
0255     int ret = 0;
0256 
0257     if (data->beacon_ies && data->beacon_ies_len) {
0258         mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
0259                      &beacon_ie, MGMT_MASK_BEACON,
0260                      WLAN_OUI_MICROSOFT,
0261                      WLAN_OUI_TYPE_MICROSOFT_WPS);
0262         mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
0263                      &beacon_ie, MGMT_MASK_BEACON,
0264                      WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
0265     }
0266 
0267     if (data->proberesp_ies && data->proberesp_ies_len) {
0268         mwifiex_update_vs_ie(data->proberesp_ies,
0269                      data->proberesp_ies_len, &pr_ie,
0270                      MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
0271                      WLAN_OUI_TYPE_MICROSOFT_WPS);
0272         mwifiex_update_vs_ie(data->proberesp_ies,
0273                      data->proberesp_ies_len, &pr_ie,
0274                      MGMT_MASK_PROBE_RESP,
0275                      WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
0276     }
0277 
0278     if (data->assocresp_ies && data->assocresp_ies_len) {
0279         mwifiex_update_vs_ie(data->assocresp_ies,
0280                      data->assocresp_ies_len, &ar_ie,
0281                      MGMT_MASK_ASSOC_RESP |
0282                      MGMT_MASK_REASSOC_RESP,
0283                      WLAN_OUI_MICROSOFT,
0284                      WLAN_OUI_TYPE_MICROSOFT_WPS);
0285         mwifiex_update_vs_ie(data->assocresp_ies,
0286                      data->assocresp_ies_len, &ar_ie,
0287                      MGMT_MASK_ASSOC_RESP |
0288                      MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
0289                      WLAN_OUI_TYPE_WFA_P2P);
0290     }
0291 
0292     if (beacon_ie || pr_ie || ar_ie) {
0293         ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
0294                            &beacon_idx, pr_ie,
0295                            &pr_idx, ar_ie, &ar_idx);
0296         if (ret)
0297             goto done;
0298     }
0299 
0300     priv->beacon_idx = beacon_idx;
0301     priv->proberesp_idx = pr_idx;
0302     priv->assocresp_idx = ar_idx;
0303 
0304 done:
0305     kfree(beacon_ie);
0306     kfree(pr_ie);
0307     kfree(ar_ie);
0308 
0309     return ret;
0310 }
0311 
0312 /* This function parses  head and tail IEs, from cfg80211_beacon_data and sets
0313  * these IE to FW.
0314  */
0315 static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
0316                       struct cfg80211_beacon_data *info)
0317 {
0318     struct mwifiex_ie *gen_ie;
0319     struct ieee_types_header *hdr;
0320     struct ieee80211_vendor_ie *vendorhdr;
0321     u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
0322     int left_len, parsed_len = 0;
0323     unsigned int token_len;
0324     int err = 0;
0325 
0326     if (!info->tail || !info->tail_len)
0327         return 0;
0328 
0329     gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
0330     if (!gen_ie)
0331         return -ENOMEM;
0332 
0333     left_len = info->tail_len;
0334 
0335     /* Many IEs are generated in FW by parsing bss configuration.
0336      * Let's not add them here; else we may end up duplicating these IEs
0337      */
0338     while (left_len > sizeof(struct ieee_types_header)) {
0339         hdr = (void *)(info->tail + parsed_len);
0340         token_len = hdr->len + sizeof(struct ieee_types_header);
0341         if (token_len > left_len) {
0342             err = -EINVAL;
0343             goto out;
0344         }
0345 
0346         switch (hdr->element_id) {
0347         case WLAN_EID_SSID:
0348         case WLAN_EID_SUPP_RATES:
0349         case WLAN_EID_COUNTRY:
0350         case WLAN_EID_PWR_CONSTRAINT:
0351         case WLAN_EID_ERP_INFO:
0352         case WLAN_EID_EXT_SUPP_RATES:
0353         case WLAN_EID_HT_CAPABILITY:
0354         case WLAN_EID_HT_OPERATION:
0355         case WLAN_EID_VHT_CAPABILITY:
0356         case WLAN_EID_VHT_OPERATION:
0357             break;
0358         case WLAN_EID_VENDOR_SPECIFIC:
0359             /* Skip only Microsoft WMM IE */
0360             if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
0361                             WLAN_OUI_TYPE_MICROSOFT_WMM,
0362                             (const u8 *)hdr,
0363                             token_len))
0364                 break;
0365             fallthrough;
0366         default:
0367             if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
0368                 err = -EINVAL;
0369                 goto out;
0370             }
0371             memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
0372             ie_len += token_len;
0373             break;
0374         }
0375         left_len -= token_len;
0376         parsed_len += token_len;
0377     }
0378 
0379     /* parse only WPA vendor IE from tail, WMM IE is configured by
0380      * bss_config command
0381      */
0382     vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
0383                             WLAN_OUI_TYPE_MICROSOFT_WPA,
0384                             info->tail, info->tail_len);
0385     if (vendorhdr) {
0386         token_len = vendorhdr->len + sizeof(struct ieee_types_header);
0387         if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
0388             err = -EINVAL;
0389             goto out;
0390         }
0391         memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
0392         ie_len += token_len;
0393     }
0394 
0395     if (!ie_len)
0396         goto out;
0397 
0398     gen_ie->ie_index = cpu_to_le16(gen_idx);
0399     gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
0400                         MGMT_MASK_PROBE_RESP |
0401                         MGMT_MASK_ASSOC_RESP);
0402     gen_ie->ie_length = cpu_to_le16(ie_len);
0403 
0404     if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
0405                      NULL, NULL)) {
0406         err = -EINVAL;
0407         goto out;
0408     }
0409 
0410     priv->gen_idx = gen_idx;
0411 
0412  out:
0413     kfree(gen_ie);
0414     return err;
0415 }
0416 
0417 /* This function parses different IEs-head & tail IEs, beacon IEs,
0418  * probe response IEs, association response IEs from cfg80211_ap_settings
0419  * function and sets these IE to FW.
0420  */
0421 int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
0422              struct cfg80211_beacon_data *info)
0423 {
0424     int ret;
0425 
0426     ret = mwifiex_uap_parse_tail_ies(priv, info);
0427 
0428     if (ret)
0429         return ret;
0430 
0431     return mwifiex_set_mgmt_beacon_data_ies(priv, info);
0432 }
0433 
0434 /* This function removes management IE set */
0435 int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
0436 {
0437     struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
0438     struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
0439     int ret = 0;
0440 
0441     if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
0442         gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
0443         if (!gen_ie)
0444             return -ENOMEM;
0445 
0446         gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
0447         gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
0448         gen_ie->ie_length = 0;
0449         if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
0450                          NULL, &priv->proberesp_idx,
0451                          NULL, &priv->assocresp_idx)) {
0452             ret = -1;
0453             goto done;
0454         }
0455 
0456         priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
0457     }
0458 
0459     if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
0460         beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
0461         if (!beacon_ie) {
0462             ret = -ENOMEM;
0463             goto done;
0464         }
0465         beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
0466         beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
0467         beacon_ie->ie_length = 0;
0468     }
0469     if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
0470         pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
0471         if (!pr_ie) {
0472             ret = -ENOMEM;
0473             goto done;
0474         }
0475         pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
0476         pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
0477         pr_ie->ie_length = 0;
0478     }
0479     if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
0480         ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
0481         if (!ar_ie) {
0482             ret = -ENOMEM;
0483             goto done;
0484         }
0485         ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
0486         ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
0487         ar_ie->ie_length = 0;
0488     }
0489 
0490     if (beacon_ie || pr_ie || ar_ie)
0491         ret = mwifiex_update_uap_custom_ie(priv,
0492                            beacon_ie, &priv->beacon_idx,
0493                            pr_ie, &priv->proberesp_idx,
0494                            ar_ie, &priv->assocresp_idx);
0495 
0496 done:
0497     kfree(gen_ie);
0498     kfree(beacon_ie);
0499     kfree(pr_ie);
0500     kfree(ar_ie);
0501 
0502     return ret;
0503 }