diff options
Diffstat (limited to 'net/mac80211/ibss.c')
| -rw-r--r-- | net/mac80211/ibss.c | 175 | 
1 files changed, 76 insertions, 99 deletions
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 40b71dfcc79..170f9a7fa31 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -44,7 +44,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local;  	int rates, i; -	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt;  	u8 *pos;  	struct ieee80211_supported_band *sband; @@ -52,20 +51,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	u32 bss_change;  	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];  	struct cfg80211_chan_def chandef; +	struct beacon_data *presp; +	int frame_len;  	lockdep_assert_held(&ifibss->mtx);  	/* Reset own TSF to allow time synchronization work. */  	drv_reset_tsf(local, sdata); -	skb = ifibss->skb; -	RCU_INIT_POINTER(ifibss->presp, NULL); -	synchronize_rcu(); -	skb->data = skb->head; -	skb->len = 0; -	skb_reset_tail_pointer(skb); -	skb_reserve(skb, sdata->local->hw.extra_tx_headroom); -  	if (!ether_addr_equal(ifibss->bssid, bssid))  		sta_info_flush(sdata); @@ -73,10 +66,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	if (sdata->vif.bss_conf.ibss_joined) {  		sdata->vif.bss_conf.ibss_joined = false;  		sdata->vif.bss_conf.ibss_creator = false; +		sdata->vif.bss_conf.enable_beacon = false;  		netif_carrier_off(sdata->dev); -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); +		ieee80211_bss_info_change_notify(sdata, +						 BSS_CHANGED_IBSS | +						 BSS_CHANGED_BEACON_ENABLED);  	} +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&ifibss->mtx)); +	rcu_assign_pointer(ifibss->presp, NULL); +	if (presp) +		kfree_rcu(presp, rcu_head); +  	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;  	cfg80211_chandef_create(&chandef, chan, ifibss->channel_type); @@ -98,19 +100,24 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	sband = local->hw.wiphy->bands[chan->band]; -	/* build supported rates array */ -	pos = supp_rates; -	for (i = 0; i < sband->n_bitrates; i++) { -		int rate = sband->bitrates[i].bitrate; -		u8 basic = 0; -		if (basic_rates & BIT(i)) -			basic = 0x80; -		*pos++ = basic | (u8) (rate / 5); -	} -  	/* Build IBSS probe response */ -	mgmt = (void *) skb_put(skb, 24 + sizeof(mgmt->u.beacon)); -	memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); +	frame_len = sizeof(struct ieee80211_hdr_3addr) + +		    12 /* struct ieee80211_mgmt.u.beacon */ + +		    2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + +		    2 + 8 /* max Supported Rates */ + +		    3 /* max DS params */ + +		    4 /* IBSS params */ + +		    2 + (IEEE80211_MAX_SUPP_RATES - 8) + +		    2 + sizeof(struct ieee80211_ht_cap) + +		    2 + sizeof(struct ieee80211_ht_operation) + +		    ifibss->ie_len; +	presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL); +	if (!presp) +		return; + +	presp->head = (void *)(presp + 1); + +	mgmt = (void *) presp->head;  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_PROBE_RESP);  	eth_broadcast_addr(mgmt->da); @@ -120,27 +127,30 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	mgmt->u.beacon.timestamp = cpu_to_le64(tsf);  	mgmt->u.beacon.capab_info = cpu_to_le16(capability); -	pos = skb_put(skb, 2 + ifibss->ssid_len); +	pos = (u8 *)mgmt + offsetof(struct ieee80211_mgmt, u.beacon.variable); +  	*pos++ = WLAN_EID_SSID;  	*pos++ = ifibss->ssid_len;  	memcpy(pos, ifibss->ssid, ifibss->ssid_len); +	pos += ifibss->ssid_len; -	rates = sband->n_bitrates; -	if (rates > 8) -		rates = 8; -	pos = skb_put(skb, 2 + rates); +	rates = min_t(int, 8, sband->n_bitrates);  	*pos++ = WLAN_EID_SUPP_RATES;  	*pos++ = rates; -	memcpy(pos, supp_rates, rates); +	for (i = 0; i < rates; i++) { +		int rate = sband->bitrates[i].bitrate; +		u8 basic = 0; +		if (basic_rates & BIT(i)) +			basic = 0x80; +		*pos++ = basic | (u8) (rate / 5); +	}  	if (sband->band == IEEE80211_BAND_2GHZ) { -		pos = skb_put(skb, 2 + 1);  		*pos++ = WLAN_EID_DS_PARAMS;  		*pos++ = 1;  		*pos++ = ieee80211_frequency_to_channel(chan->center_freq);  	} -	pos = skb_put(skb, 2 + 2);  	*pos++ = WLAN_EID_IBSS_PARAMS;  	*pos++ = 2;  	/* FIX: set ATIM window based on scan results */ @@ -148,23 +158,25 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	*pos++ = 0;  	if (sband->n_bitrates > 8) { -		rates = sband->n_bitrates - 8; -		pos = skb_put(skb, 2 + rates);  		*pos++ = WLAN_EID_EXT_SUPP_RATES; -		*pos++ = rates; -		memcpy(pos, &supp_rates[8], rates); +		*pos++ = sband->n_bitrates - 8; +		for (i = 8; i < sband->n_bitrates; i++) { +			int rate = sband->bitrates[i].bitrate; +			u8 basic = 0; +			if (basic_rates & BIT(i)) +				basic = 0x80; +			*pos++ = basic | (u8) (rate / 5); +		}  	} -	if (ifibss->ie_len) -		memcpy(skb_put(skb, ifibss->ie_len), -		       ifibss->ie, ifibss->ie_len); +	if (ifibss->ie_len) { +		memcpy(pos, ifibss->ie, ifibss->ie_len); +		pos += ifibss->ie_len; +	}  	/* add HT capability and information IEs */  	if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&  	    sband->ht_cap.ht_supported) { -		pos = skb_put(skb, 4 + -				   sizeof(struct ieee80211_ht_cap) + -				   sizeof(struct ieee80211_ht_operation));  		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,  						sband->ht_cap.cap);  		/* @@ -177,7 +189,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	}  	if (local->hw.queues >= IEEE80211_NUM_ACS) { -		pos = skb_put(skb, 9);  		*pos++ = WLAN_EID_VENDOR_SPECIFIC;  		*pos++ = 7; /* len */  		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ @@ -189,11 +200,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		*pos++ = 0; /* U-APSD no in use */  	} -	rcu_assign_pointer(ifibss->presp, skb); +	presp->head_len = pos - presp->head; +	if (WARN_ON(presp->head_len > frame_len)) +		return; + +	rcu_assign_pointer(ifibss->presp, presp);  	sdata->vif.bss_conf.enable_beacon = true;  	sdata->vif.bss_conf.beacon_int = beacon_int;  	sdata->vif.bss_conf.basic_rates = basic_rates; +	sdata->vif.bss_conf.ssid_len = ifibss->ssid_len; +	memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len);  	bss_change = BSS_CHANGED_BEACON_INT;  	bss_change |= ieee80211_reset_erp_info(sdata);  	bss_change |= BSS_CHANGED_BSSID; @@ -202,6 +219,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	bss_change |= BSS_CHANGED_BASIC_RATES;  	bss_change |= BSS_CHANGED_HT;  	bss_change |= BSS_CHANGED_IBSS; +	bss_change |= BSS_CHANGED_SSID;  	/*  	 * In 5 GHz/802.11a, we can always use short slot time. @@ -227,7 +245,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));  	bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan, -					mgmt, skb->len, 0, GFP_KERNEL); +					mgmt, presp->head_len, 0, GFP_KERNEL);  	cfg80211_put_bss(local->hw.wiphy, bss);  	netif_carrier_on(sdata->dev);  	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); @@ -448,7 +466,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];  	bool rates_updated = false; -	if (elems->ds_params && elems->ds_params_len == 1) +	if (elems->ds_params)  		freq = ieee80211_channel_to_frequency(elems->ds_params[0],  						      band);  	else @@ -822,8 +840,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_local *local = sdata->local;  	int tx_last_beacon, len = req->len;  	struct sk_buff *skb; -	struct ieee80211_mgmt *resp; -	struct sk_buff *presp; +	struct beacon_data *presp;  	u8 *pos, *end;  	lockdep_assert_held(&ifibss->mtx); @@ -864,13 +881,15 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,  	}  	/* Reply with ProbeResp */ -	skb = skb_copy(presp, GFP_KERNEL); +	skb = dev_alloc_skb(local->tx_headroom + presp->head_len);  	if (!skb)  		return; -	resp = (struct ieee80211_mgmt *) skb->data; -	memcpy(resp->da, mgmt->sa, ETH_ALEN); -	ibss_dbg(sdata, "Sending ProbeResp to %pM\n", resp->da); +	skb_reserve(skb, local->tx_headroom); +	memcpy(skb_put(skb, presp->head_len), presp->head, presp->head_len); + +	memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN); +	ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);  	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;  	ieee80211_tx_skb(sdata, skb);  } @@ -895,7 +914,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -				&elems); +			       false, &elems);  	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);  } @@ -985,36 +1004,9 @@ static void ieee80211_ibss_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	struct ieee80211_local *local = sdata->local; - -	if (local->quiescing) { -		ifibss->timer_running = true; -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); -} - -#ifdef CONFIG_PM -void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	if (del_timer_sync(&ifibss->timer)) -		ifibss->timer_running = true; -} - -void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - -	if (ifibss->timer_running) { -		add_timer(&ifibss->timer); -		ifibss->timer_running = false; -	} +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  } -#endif  void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)  { @@ -1047,23 +1039,8 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)  int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_ibss_params *params)  { -	struct sk_buff *skb;  	u32 changed = 0; -	skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + -			    sizeof(struct ieee80211_hdr_3addr) + -			    12 /* struct ieee80211_mgmt.u.beacon */ + -			    2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + -			    2 + 8 /* max Supported Rates */ + -			    3 /* max DS params */ + -			    4 /* IBSS params */ + -			    2 + (IEEE80211_MAX_SUPP_RATES - 8) + -			    2 + sizeof(struct ieee80211_ht_cap) + -			    2 + sizeof(struct ieee80211_ht_operation) + -			    params->ie_len); -	if (!skb) -		return -ENOMEM; -  	mutex_lock(&sdata->u.ibss.mtx);  	if (params->bssid) { @@ -1092,7 +1069,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			sdata->u.ibss.ie_len = params->ie_len;  	} -	sdata->u.ibss.skb = skb;  	sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;  	sdata->u.ibss.ibss_join_req = jiffies; @@ -1128,13 +1104,13 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  { -	struct sk_buff *skb;  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local;  	struct cfg80211_bss *cbss;  	u16 capability;  	int active_ibss;  	struct sta_info *sta; +	struct beacon_data *presp;  	mutex_lock(&sdata->u.ibss.mtx); @@ -1180,17 +1156,18 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  	/* remove beacon */  	kfree(sdata->u.ibss.ie); -	skb = rcu_dereference_protected(sdata->u.ibss.presp, -					lockdep_is_held(&sdata->u.ibss.mtx)); +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->u.ibss.mtx));  	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);  	sdata->vif.bss_conf.ibss_joined = false;  	sdata->vif.bss_conf.ibss_creator = false;  	sdata->vif.bss_conf.enable_beacon = false; +	sdata->vif.bss_conf.ssid_len = 0;  	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);  	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |  						BSS_CHANGED_IBSS);  	synchronize_rcu(); -	kfree_skb(skb); +	kfree(presp);  	skb_queue_purge(&sdata->skb_queue);  |