mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	cfg80211: support profile split between elements
Since an element is limited to 255 octets, a profile may be split split to several elements. Support the split as defined in the 11ax draft 3. Detect legacy split and print a net-rate limited warning, since there is no ROI in supporting this probably non-existent split. Signed-off-by: Sara Sharon <sara.sharon@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
							parent
							
								
									671042a4fb
								
							
						
					
					
						commit
						fe806e4992
					
				
					 2 changed files with 117 additions and 6 deletions
				
			
		| 
						 | 
					@ -5542,6 +5542,20 @@ static inline void cfg80211_gen_new_bssid(const u8 *bssid, u8 max_bssid,
 | 
				
			||||||
bool cfg80211_is_element_inherited(const struct element *element,
 | 
					bool cfg80211_is_element_inherited(const struct element *element,
 | 
				
			||||||
				   const struct element *non_inherit_element);
 | 
									   const struct element *non_inherit_element);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * cfg80211_merge_profile - merges a MBSSID profile if it is split between IEs
 | 
				
			||||||
 | 
					 * @ie: ies
 | 
				
			||||||
 | 
					 * @ielen: length of IEs
 | 
				
			||||||
 | 
					 * @mbssid_elem: current MBSSID element
 | 
				
			||||||
 | 
					 * @sub_elem: current MBSSID subelement (profile)
 | 
				
			||||||
 | 
					 * @merged_ie: location of the merged profile
 | 
				
			||||||
 | 
					 * @max_copy_len: max merged profile length
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
 | 
				
			||||||
 | 
								      const struct element *mbssid_elem,
 | 
				
			||||||
 | 
								      const struct element *sub_elem,
 | 
				
			||||||
 | 
								      u8 **merged_ie, size_t max_copy_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * enum cfg80211_bss_frame_type - frame type that the BSS data came from
 | 
					 * enum cfg80211_bss_frame_type - frame type that the BSS data came from
 | 
				
			||||||
 * @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
 | 
					 * @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1456,6 +1456,78 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
 | 
				
			||||||
	return &res->pub;
 | 
						return &res->pub;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct element
 | 
				
			||||||
 | 
					*cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
 | 
				
			||||||
 | 
									   const struct element *mbssid_elem,
 | 
				
			||||||
 | 
									   const struct element *sub_elem)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
 | 
				
			||||||
 | 
						const struct element *next_mbssid;
 | 
				
			||||||
 | 
						const struct element *next_sub;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						next_mbssid = cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
 | 
				
			||||||
 | 
										 mbssid_end,
 | 
				
			||||||
 | 
										 ielen - (mbssid_end - ie));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * If is is not the last subelement in current MBSSID IE or there isn't
 | 
				
			||||||
 | 
						 * a next MBSSID IE - profile is complete.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
 | 
				
			||||||
 | 
						    !next_mbssid)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* For any length error, just return NULL */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (next_mbssid->datalen < 4)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						next_sub = (void *)&next_mbssid->data[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (next_mbssid->data + next_mbssid->datalen <
 | 
				
			||||||
 | 
						    next_sub->data + next_sub->datalen)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (next_sub->id != 0 || next_sub->datalen < 2)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Check if the first element in the next sub element is a start
 | 
				
			||||||
 | 
						 * of a new profile
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
 | 
				
			||||||
 | 
						       NULL : next_mbssid;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
 | 
				
			||||||
 | 
								      const struct element *mbssid_elem,
 | 
				
			||||||
 | 
								      const struct element *sub_elem,
 | 
				
			||||||
 | 
								      u8 **merged_ie, size_t max_copy_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t copied_len = sub_elem->datalen;
 | 
				
			||||||
 | 
						const struct element *next_mbssid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sub_elem->datalen > max_copy_len)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memcpy(*merged_ie, sub_elem->data, sub_elem->datalen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
 | 
				
			||||||
 | 
													mbssid_elem,
 | 
				
			||||||
 | 
													sub_elem))) {
 | 
				
			||||||
 | 
							const struct element *next_sub = (void *)&next_mbssid->data[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (copied_len + next_sub->datalen > max_copy_len)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							memcpy(*merged_ie + copied_len, next_sub->data,
 | 
				
			||||||
 | 
							       next_sub->datalen);
 | 
				
			||||||
 | 
							copied_len += next_sub->datalen;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return copied_len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(cfg80211_merge_profile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
					static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
				
			||||||
				       struct cfg80211_inform_bss *data,
 | 
									       struct cfg80211_inform_bss *data,
 | 
				
			||||||
				       enum cfg80211_bss_frame_type ftype,
 | 
									       enum cfg80211_bss_frame_type ftype,
 | 
				
			||||||
| 
						 | 
					@ -1469,7 +1541,8 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
				
			||||||
	const struct element *elem, *sub;
 | 
						const struct element *elem, *sub;
 | 
				
			||||||
	size_t new_ie_len;
 | 
						size_t new_ie_len;
 | 
				
			||||||
	u8 new_bssid[ETH_ALEN];
 | 
						u8 new_bssid[ETH_ALEN];
 | 
				
			||||||
	u8 *new_ie;
 | 
						u8 *new_ie, *profile;
 | 
				
			||||||
 | 
						u64 seen_indices = 0;
 | 
				
			||||||
	u16 capability;
 | 
						u16 capability;
 | 
				
			||||||
	struct cfg80211_bss *bss;
 | 
						struct cfg80211_bss *bss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1487,10 +1560,16 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
				
			||||||
	if (!new_ie)
 | 
						if (!new_ie)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						profile = kmalloc(ielen, gfp);
 | 
				
			||||||
 | 
						if (!profile)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
 | 
						for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
 | 
				
			||||||
		if (elem->datalen < 4)
 | 
							if (elem->datalen < 4)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		for_each_element(sub, elem->data + 1, elem->datalen - 1) {
 | 
							for_each_element(sub, elem->data + 1, elem->datalen - 1) {
 | 
				
			||||||
 | 
								u8 profile_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (sub->id != 0 || sub->datalen < 4) {
 | 
								if (sub->id != 0 || sub->datalen < 4) {
 | 
				
			||||||
				/* not a valid BSS profile */
 | 
									/* not a valid BSS profile */
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
| 
						 | 
					@ -1505,16 +1584,31 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								memset(profile, 0, ielen);
 | 
				
			||||||
 | 
								profile_len = cfg80211_merge_profile(ie, ielen,
 | 
				
			||||||
 | 
												     elem,
 | 
				
			||||||
 | 
												     sub,
 | 
				
			||||||
 | 
												     &profile,
 | 
				
			||||||
 | 
												     ielen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* found a Nontransmitted BSSID Profile */
 | 
								/* found a Nontransmitted BSSID Profile */
 | 
				
			||||||
			mbssid_index_ie = cfg80211_find_ie
 | 
								mbssid_index_ie = cfg80211_find_ie
 | 
				
			||||||
				(WLAN_EID_MULTI_BSSID_IDX,
 | 
									(WLAN_EID_MULTI_BSSID_IDX,
 | 
				
			||||||
				 sub->data, sub->datalen);
 | 
									 profile, profile_len);
 | 
				
			||||||
			if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
 | 
								if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
 | 
				
			||||||
			    mbssid_index_ie[2] == 0) {
 | 
								    mbssid_index_ie[2] == 0 ||
 | 
				
			||||||
 | 
								    mbssid_index_ie[2] > 46) {
 | 
				
			||||||
				/* No valid Multiple BSSID-Index element */
 | 
									/* No valid Multiple BSSID-Index element */
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (seen_indices & BIT(mbssid_index_ie[2]))
 | 
				
			||||||
 | 
									/* We don't support legacy split of a profile */
 | 
				
			||||||
 | 
									net_dbg_ratelimited("Partial info for BSSID index %d\n",
 | 
				
			||||||
 | 
											    mbssid_index_ie[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								seen_indices |= BIT(mbssid_index_ie[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			non_tx_data->bssid_index = mbssid_index_ie[2];
 | 
								non_tx_data->bssid_index = mbssid_index_ie[2];
 | 
				
			||||||
			non_tx_data->max_bssid_indicator = elem->data[0];
 | 
								non_tx_data->max_bssid_indicator = elem->data[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1523,13 +1617,14 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
				
			||||||
					       non_tx_data->bssid_index,
 | 
										       non_tx_data->bssid_index,
 | 
				
			||||||
					       new_bssid);
 | 
										       new_bssid);
 | 
				
			||||||
			memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
 | 
								memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
 | 
				
			||||||
			new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data,
 | 
								new_ie_len = cfg80211_gen_new_ie(ie, ielen,
 | 
				
			||||||
							 sub->datalen, new_ie,
 | 
												 profile,
 | 
				
			||||||
 | 
												 profile_len, new_ie,
 | 
				
			||||||
							 gfp);
 | 
												 gfp);
 | 
				
			||||||
			if (!new_ie_len)
 | 
								if (!new_ie_len)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			capability = get_unaligned_le16(sub->data + 2);
 | 
								capability = get_unaligned_le16(profile + 2);
 | 
				
			||||||
			bss = cfg80211_inform_single_bss_data(wiphy, data,
 | 
								bss = cfg80211_inform_single_bss_data(wiphy, data,
 | 
				
			||||||
							      ftype,
 | 
												      ftype,
 | 
				
			||||||
							      new_bssid, tsf,
 | 
												      new_bssid, tsf,
 | 
				
			||||||
| 
						 | 
					@ -1545,7 +1640,9 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
	kfree(new_ie);
 | 
						kfree(new_ie);
 | 
				
			||||||
 | 
						kfree(profile);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct cfg80211_bss *
 | 
					struct cfg80211_bss *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue