diff options
Diffstat (limited to 'net/mac80211/cfg.c')
| -rw-r--r-- | net/mac80211/cfg.c | 293 | 
1 files changed, 250 insertions, 43 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d06c65fa552..850bb96bd68 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -102,6 +102,16 @@ static int ieee80211_change_iface(struct wiphy *wiphy,  	return 0;  } +static int ieee80211_set_noack_map(struct wiphy *wiphy, +				  struct net_device *dev, +				  u16 noack_map) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	sdata->noack_map = noack_map; +	return 0; +} +  static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  			     u8 key_idx, bool pairwise, const u8 *mac_addr,  			     struct key_params *params) @@ -345,7 +355,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  			STATION_INFO_RX_DROP_MISC |  			STATION_INFO_BSS_PARAM |  			STATION_INFO_CONNECTED_TIME | -			STATION_INFO_STA_FLAGS; +			STATION_INFO_STA_FLAGS | +			STATION_INFO_BEACON_LOSS_COUNT;  	do_posix_clock_monotonic_gettime(&uptime);  	sinfo->connected_time = uptime.tv_sec - sta->last_connected; @@ -358,6 +369,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  	sinfo->tx_retries = sta->tx_retry_count;  	sinfo->tx_failed = sta->tx_retry_failed;  	sinfo->rx_dropped_misc = sta->rx_dropped; +	sinfo->beacon_loss_count = sta->beacon_loss_count;  	if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||  	    (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { @@ -411,7 +423,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  				BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |  				BIT(NL80211_STA_FLAG_WME) |  				BIT(NL80211_STA_FLAG_MFP) | -				BIT(NL80211_STA_FLAG_AUTHENTICATED); +				BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				BIT(NL80211_STA_FLAG_TDLS_PEER);  	if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);  	if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) @@ -422,6 +435,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);  	if (test_sta_flag(sta, WLAN_STA_AUTH))  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); +	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);  } @@ -488,6 +503,31 @@ static void ieee80211_config_ap_ssid(struct ieee80211_sub_if_data *sdata,  		(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);  } +static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, +				    u8 *resp, size_t resp_len) +{ +	struct sk_buff *new, *old; + +	if (!resp || !resp_len) +		return -EINVAL; + +	old = rtnl_dereference(sdata->u.ap.probe_resp); + +	new = dev_alloc_skb(resp_len); +	if (!new) +		return -ENOMEM; + +	memcpy(skb_put(new, resp_len), resp, resp_len); + +	rcu_assign_pointer(sdata->u.ap.probe_resp, new); +	synchronize_rcu(); + +	if (old) +		dev_kfree_skb(old); + +	return 0; +} +  /*   * This handles both adding a beacon and setting new beacon info   */ @@ -498,6 +538,7 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,  	int new_head_len, new_tail_len;  	int size;  	int err = -EINVAL; +	u32 changed = 0;  	old = rtnl_dereference(sdata->u.ap.beacon); @@ -581,11 +622,17 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,  	kfree(old); +	err = ieee80211_set_probe_resp(sdata, params->probe_resp, +				       params->probe_resp_len); +	if (!err) +		changed |= BSS_CHANGED_AP_PROBE_RESP; +  	ieee80211_config_ap_ssid(sdata, params); +	changed |= BSS_CHANGED_BEACON_ENABLED | +		   BSS_CHANGED_BEACON | +		   BSS_CHANGED_SSID; -	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | -						BSS_CHANGED_BEACON | -						BSS_CHANGED_SSID); +	ieee80211_bss_info_change_notify(sdata, changed);  	return 0;  } @@ -594,6 +641,8 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,  {  	struct ieee80211_sub_if_data *sdata;  	struct beacon_data *old; +	struct ieee80211_sub_if_data *vlan; +	int ret;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -601,7 +650,24 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,  	if (old)  		return -EALREADY; -	return ieee80211_config_beacon(sdata, params); +	ret = ieee80211_config_beacon(sdata, params); +	if (ret) +		return ret; + +	/* +	 * Apply control port protocol, this allows us to +	 * not encrypt dynamic WEP control frames. +	 */ +	sdata->control_port_protocol = params->crypto.control_port_ethertype; +	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; +	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { +		vlan->control_port_protocol = +			params->crypto.control_port_ethertype; +		vlan->control_port_no_encrypt = +			params->crypto.control_port_no_encrypt; +	} + +	return 0;  }  static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, @@ -682,10 +748,11 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)  	netif_rx_ni(skb);  } -static void sta_apply_parameters(struct ieee80211_local *local, -				 struct sta_info *sta, -				 struct station_parameters *params) +static int sta_apply_parameters(struct ieee80211_local *local, +				struct sta_info *sta, +				struct station_parameters *params)  { +	int ret = 0;  	u32 rates;  	int i, j;  	struct ieee80211_supported_band *sband; @@ -697,13 +764,59 @@ static void sta_apply_parameters(struct ieee80211_local *local,  	mask = params->sta_flags_mask;  	set = params->sta_flags_set; +	/* +	 * In mesh mode, we can clear AUTHENTICATED flag but must +	 * also make ASSOCIATED follow appropriately for the driver +	 * API. See also below, after AUTHORIZED changes. +	 */ +	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { +		/* cfg80211 should not allow this in non-mesh modes */ +		if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) +			return -EINVAL; + +		if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && +		    !test_sta_flag(sta, WLAN_STA_AUTH)) { +			ret = sta_info_move_state_checked(sta, +					IEEE80211_STA_AUTH); +			if (ret) +				return ret; +			ret = sta_info_move_state_checked(sta, +					IEEE80211_STA_ASSOC); +			if (ret) +				return ret; +		} +	} +  	if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {  		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) -			set_sta_flag(sta, WLAN_STA_AUTHORIZED); +			ret = sta_info_move_state_checked(sta, +					IEEE80211_STA_AUTHORIZED);  		else -			clear_sta_flag(sta, WLAN_STA_AUTHORIZED); +			ret = sta_info_move_state_checked(sta, +					IEEE80211_STA_ASSOC); +		if (ret) +			return ret; +	} + +	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { +		/* cfg80211 should not allow this in non-mesh modes */ +		if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) +			return -EINVAL; + +		if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && +		    test_sta_flag(sta, WLAN_STA_AUTH)) { +			ret = sta_info_move_state_checked(sta, +					IEEE80211_STA_AUTH); +			if (ret) +				return ret; +			ret = sta_info_move_state_checked(sta, +					IEEE80211_STA_NONE); +			if (ret) +				return ret; +		}  	} +  	if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {  		if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))  			set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); @@ -728,13 +841,6 @@ static void sta_apply_parameters(struct ieee80211_local *local,  			clear_sta_flag(sta, WLAN_STA_MFP);  	} -	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { -		if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) -			set_sta_flag(sta, WLAN_STA_AUTH); -		else -			clear_sta_flag(sta, WLAN_STA_AUTH); -	} -  	if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {  		if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))  			set_sta_flag(sta, WLAN_STA_TDLS_PEER); @@ -778,7 +884,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,  	}  	if (params->ht_capa) -		ieee80211_ht_cap_ie_to_sta_ht_cap(sband, +		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,  						  params->ht_capa,  						  &sta->sta.ht_cap); @@ -806,6 +912,8 @@ static void sta_apply_parameters(struct ieee80211_local *local,  			}  #endif  	} + +	return 0;  }  static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, @@ -832,22 +940,25 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	if (is_multicast_ether_addr(mac))  		return -EINVAL; -	/* Only TDLS-supporting stations can add TDLS peers */ -	if ((params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && -	    !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) && -	      sdata->vif.type == NL80211_IFTYPE_STATION)) -		return -ENOTSUPP; -  	sta = sta_info_alloc(sdata, mac, GFP_KERNEL);  	if (!sta)  		return -ENOMEM; -	set_sta_flag(sta, WLAN_STA_AUTH); -	set_sta_flag(sta, WLAN_STA_ASSOC); +	sta_info_move_state(sta, IEEE80211_STA_AUTH); +	sta_info_move_state(sta, IEEE80211_STA_ASSOC); -	sta_apply_parameters(local, sta, params); +	err = sta_apply_parameters(local, sta, params); +	if (err) { +		sta_info_free(local, sta); +		return err; +	} -	rate_control_rate_init(sta); +	/* +	 * for TDLS, rate control should be initialized only when supported +	 * rates are known. +	 */ +	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		rate_control_rate_init(sta);  	layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||  		sdata->vif.type == NL80211_IFTYPE_AP; @@ -891,19 +1002,19 @@ static int ieee80211_change_station(struct wiphy *wiphy,  	struct sta_info *sta;  	struct ieee80211_sub_if_data *vlansdata; -	rcu_read_lock(); +	mutex_lock(&local->sta_mtx);  	sta = sta_info_get_bss(sdata, mac);  	if (!sta) { -		rcu_read_unlock(); +		mutex_unlock(&local->sta_mtx);  		return -ENOENT;  	} -	/* The TDLS bit cannot be toggled after the STA was added */ -	if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) && -	    !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) != -	    !!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { -		rcu_read_unlock(); +	/* in station mode, supported rates are only valid with TDLS */ +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    params->supported_rates && +	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { +		mutex_unlock(&local->sta_mtx);  		return -EINVAL;  	} @@ -912,13 +1023,13 @@ static int ieee80211_change_station(struct wiphy *wiphy,  		if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&  		    vlansdata->vif.type != NL80211_IFTYPE_AP) { -			rcu_read_unlock(); +			mutex_unlock(&local->sta_mtx);  			return -EINVAL;  		}  		if (params->vlan->ieee80211_ptr->use_4addr) {  			if (vlansdata->u.vlan.sta) { -				rcu_read_unlock(); +				mutex_unlock(&local->sta_mtx);  				return -EBUSY;  			} @@ -931,7 +1042,10 @@ static int ieee80211_change_station(struct wiphy *wiphy,  	sta_apply_parameters(local, sta, params); -	rcu_read_unlock(); +	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) +		rate_control_rate_init(sta); + +	mutex_unlock(&local->sta_mtx);  	if (sdata->vif.type == NL80211_IFTYPE_STATION &&  	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) @@ -1123,6 +1237,8 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,  {  	u8 *new_ie;  	const u8 *old_ie; +	struct ieee80211_sub_if_data *sdata = container_of(ifmsh, +					struct ieee80211_sub_if_data, u.mesh);  	/* allocate information elements */  	new_ie = NULL; @@ -1149,6 +1265,10 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,  	if (setup->is_secure)  		ifmsh->security |= IEEE80211_MESH_SEC_SECURED; +	/* mcast rate setting in Mesh Node */ +	memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, +						sizeof(setup->mcast_rate)); +  	return 0;  } @@ -1194,6 +1314,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,  	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))  		conf->dot11MeshHWMPpreqMinInterval =  			nconf->dot11MeshHWMPpreqMinInterval; +	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask)) +		conf->dot11MeshHWMPperrMinInterval = +			nconf->dot11MeshHWMPperrMinInterval;  	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,  			   mask))  		conf->dot11MeshHWMPnetDiameterTraversalTime = @@ -1394,7 +1517,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,  	    (old_oper_type != local->_oper_channel_type))  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); -	if ((sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) && +	if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR &&  	    old_vif_oper_type != sdata->vif.bss_conf.channel_type)  		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); @@ -1917,7 +2040,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,  			     enum nl80211_channel_type channel_type,  			     bool channel_type_valid, unsigned int wait,  			     const u8 *buf, size_t len, bool no_cck, -			     u64 *cookie) +			     bool dont_wait_for_ack, u64 *cookie)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; @@ -1925,10 +2048,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,  	struct sta_info *sta;  	struct ieee80211_work *wk;  	const struct ieee80211_mgmt *mgmt = (void *)buf; -	u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | -		    IEEE80211_TX_CTL_REQ_TX_STATUS; +	u32 flags;  	bool is_offchan = false; +	if (dont_wait_for_ack) +		flags = IEEE80211_TX_CTL_NO_ACK; +	else +		flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | +			IEEE80211_TX_CTL_REQ_TX_STATUS; +  	/* Check that we are on the requested channel for transmission */  	if (chan != local->tmp_channel &&  	    chan != local->oper_channel) @@ -2488,6 +2616,82 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,  	return 0;  } +static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, +				  const u8 *peer, u64 *cookie) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_qos_hdr *nullfunc; +	struct sk_buff *skb; +	int size = sizeof(*nullfunc); +	__le16 fc; +	bool qos; +	struct ieee80211_tx_info *info; +	struct sta_info *sta; + +	rcu_read_lock(); +	sta = sta_info_get(sdata, peer); +	if (sta) { +		qos = test_sta_flag(sta, WLAN_STA_WME); +		rcu_read_unlock(); +	} else { +		rcu_read_unlock(); +		return -ENOLINK; +	} + +	if (qos) { +		fc = cpu_to_le16(IEEE80211_FTYPE_DATA | +				 IEEE80211_STYPE_QOS_NULLFUNC | +				 IEEE80211_FCTL_FROMDS); +	} else { +		size -= 2; +		fc = cpu_to_le16(IEEE80211_FTYPE_DATA | +				 IEEE80211_STYPE_NULLFUNC | +				 IEEE80211_FCTL_FROMDS); +	} + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); +	if (!skb) +		return -ENOMEM; + +	skb->dev = dev; + +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	nullfunc = (void *) skb_put(skb, size); +	nullfunc->frame_control = fc; +	nullfunc->duration_id = 0; +	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); +	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); +	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); +	nullfunc->seq_ctrl = 0; + +	info = IEEE80211_SKB_CB(skb); + +	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | +		       IEEE80211_TX_INTFL_NL80211_FRAME_TX; + +	skb_set_queue_mapping(skb, IEEE80211_AC_VO); +	skb->priority = 7; +	if (qos) +		nullfunc->qos_ctrl = cpu_to_le16(7); + +	local_bh_disable(); +	ieee80211_xmit(sdata, skb); +	local_bh_enable(); + +	*cookie = (unsigned long) skb; +	return 0; +} + +static struct ieee80211_channel * +ieee80211_wiphy_get_channel(struct wiphy *wiphy) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); + +	return local->oper_channel; +} +  struct cfg80211_ops mac80211_config_ops = {  	.add_virtual_intf = ieee80211_add_iface,  	.del_virtual_intf = ieee80211_del_iface, @@ -2553,4 +2757,7 @@ struct cfg80211_ops mac80211_config_ops = {  	.set_rekey_data = ieee80211_set_rekey_data,  	.tdls_oper = ieee80211_tdls_oper,  	.tdls_mgmt = ieee80211_tdls_mgmt, +	.probe_client = ieee80211_probe_client, +	.get_channel = ieee80211_wiphy_get_channel, +	.set_noack_map = ieee80211_set_noack_map,  };  |