mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-29 21:14:28 -04:00 
			
		
		
		
	- add support for configuring allowed radios for a vif - add support for monitor mode on multiple channels Signed-off-by: Felix Fietkau <nbd@nbd.name>
		
			
				
	
	
		
			338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From: Felix Fietkau <nbd@nbd.name>
 | |
| Date: Mon, 30 Sep 2024 15:09:45 +0200
 | |
| Subject: [PATCH] wifi: mac80211: add flag to opt out of virtual monitor
 | |
|  support
 | |
| 
 | |
| This is useful for multi-radio devices that are capable of monitoring on
 | |
| multiple channels simultanenously. When this flag is set, each monitor
 | |
| interface is passed to the driver individually and can have a configured
 | |
| channel.
 | |
| 
 | |
| Signed-off-by: Felix Fietkau <nbd@nbd.name>
 | |
| ---
 | |
| 
 | |
| --- a/include/net/mac80211.h
 | |
| +++ b/include/net/mac80211.h
 | |
| @@ -2679,6 +2679,11 @@ struct ieee80211_txq {
 | |
|   *	a virtual monitor interface when monitor interfaces are the only
 | |
|   *	active interfaces.
 | |
|   *
 | |
| + * @IEEE80211_HW_NO_VIRTUAL_MONITOR: The driver would like to be informed
 | |
| + *	of any monitor interface, as well as their configured channel.
 | |
| + *	This is useful for supporting multiple monitor interfaces on different
 | |
| + *	channels.
 | |
| + *
 | |
|   * @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
 | |
|   *	be created.  It is expected user-space will create vifs as
 | |
|   *	desired (and thus have them named as desired).
 | |
| @@ -2838,6 +2843,7 @@ enum ieee80211_hw_flags {
 | |
|  	IEEE80211_HW_SUPPORTS_DYNAMIC_PS,
 | |
|  	IEEE80211_HW_MFP_CAPABLE,
 | |
|  	IEEE80211_HW_WANT_MONITOR_VIF,
 | |
| +	IEEE80211_HW_NO_VIRTUAL_MONITOR,
 | |
|  	IEEE80211_HW_NO_AUTO_VIF,
 | |
|  	IEEE80211_HW_SW_CRYPTO_CONTROL,
 | |
|  	IEEE80211_HW_SUPPORT_FAST_XMIT,
 | |
| --- a/net/mac80211/cfg.c
 | |
| +++ b/net/mac80211/cfg.c
 | |
| @@ -105,8 +105,11 @@ static int ieee80211_set_mon_options(str
 | |
|  	}
 | |
|  
 | |
|  	/* also validate MU-MIMO change */
 | |
| -	monitor_sdata = wiphy_dereference(local->hw.wiphy,
 | |
| -					  local->monitor_sdata);
 | |
| +	if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
| +		monitor_sdata = sdata;
 | |
| +	else
 | |
| +		monitor_sdata = wiphy_dereference(local->hw.wiphy,
 | |
| +						  local->monitor_sdata);
 | |
|  
 | |
|  	if (!monitor_sdata &&
 | |
|  	    (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
 | |
| @@ -114,7 +117,9 @@ static int ieee80211_set_mon_options(str
 | |
|  
 | |
|  	/* apply all changes now - no failures allowed */
 | |
|  
 | |
| -	if (monitor_sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
 | |
| +	if (monitor_sdata &&
 | |
| +		(ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) ||
 | |
| +		 ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)))
 | |
|  		ieee80211_set_mu_mimo_follow(monitor_sdata, params);
 | |
|  
 | |
|  	if (params->flags) {
 | |
| @@ -889,22 +894,25 @@ static int ieee80211_set_monitor_channel
 | |
|  
 | |
|  	lockdep_assert_wiphy(local->hw.wiphy);
 | |
|  
 | |
| -	if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
 | |
| -				       &chanreq.oper))
 | |
| -		return 0;
 | |
| +	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 | |
| +	if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
 | |
| +		if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
 | |
| +						   &chanreq.oper))
 | |
| +			return 0;
 | |
|  
 | |
| -	sdata = wiphy_dereference(local->hw.wiphy,
 | |
| -				  local->monitor_sdata);
 | |
| -	if (!sdata)
 | |
| -		goto done;
 | |
| +		sdata = wiphy_dereference(wiphy, local->monitor_sdata);
 | |
| +		if (!sdata)
 | |
| +			goto done;
 | |
| +	}
 | |
|  
 | |
| -	if (cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper,
 | |
| +	if (rcu_access_pointer(sdata->deflink.conf->chanctx_conf) &&
 | |
| +		cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper,
 | |
|  				       &chanreq.oper))
 | |
|  		return 0;
 | |
|  
 | |
|  	ieee80211_link_release_channel(&sdata->deflink);
 | |
|  	ret = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
 | |
| -					 IEEE80211_CHANCTX_EXCLUSIVE);
 | |
| +					 IEEE80211_CHANCTX_SHARED);
 | |
|  	if (ret)
 | |
|  		return ret;
 | |
|  done:
 | |
| @@ -3049,7 +3057,8 @@ static int ieee80211_set_tx_power(struct
 | |
|  	if (wdev) {
 | |
|  		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 | |
|  
 | |
| -		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
 | |
| +		if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 | |
| +		    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
 | |
|  			if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
 | |
|  				return -EOPNOTSUPP;
 | |
|  
 | |
| @@ -3097,7 +3106,8 @@ static int ieee80211_set_tx_power(struct
 | |
|  	}
 | |
|  
 | |
|  	list_for_each_entry(sdata, &local->interfaces, list) {
 | |
| -		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
 | |
| +		if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 | |
| +		    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
 | |
|  			has_monitor = true;
 | |
|  			continue;
 | |
|  		}
 | |
| @@ -3107,7 +3117,8 @@ static int ieee80211_set_tx_power(struct
 | |
|  		sdata->vif.bss_conf.txpower_type = txp_type;
 | |
|  	}
 | |
|  	list_for_each_entry(sdata, &local->interfaces, list) {
 | |
| -		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
 | |
| +		if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 | |
| +		    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
|  			continue;
 | |
|  		ieee80211_recalc_txpower(sdata, update_txp_type);
 | |
|  	}
 | |
| @@ -4299,7 +4310,8 @@ static int ieee80211_cfg_get_channel(str
 | |
|  	if (chanctx_conf) {
 | |
|  		*chandef = link->conf->chanreq.oper;
 | |
|  		ret = 0;
 | |
| -	} else if (local->open_count > 0 &&
 | |
| +	} else if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
 | |
| +		   local->open_count > 0 &&
 | |
|  		   local->open_count == local->monitors &&
 | |
|  		   sdata->vif.type == NL80211_IFTYPE_MONITOR) {
 | |
|  		*chandef = local->monitor_chanreq.oper;
 | |
| --- a/net/mac80211/chan.c
 | |
| +++ b/net/mac80211/chan.c
 | |
| @@ -337,6 +337,10 @@ ieee80211_get_chanctx_max_required_bw(st
 | |
|  		case NL80211_IFTYPE_P2P_DEVICE:
 | |
|  		case NL80211_IFTYPE_NAN:
 | |
|  			continue;
 | |
| +		case NL80211_IFTYPE_MONITOR:
 | |
| +			WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
 | |
| +							 NO_VIRTUAL_MONITOR));
 | |
| +			fallthrough;
 | |
|  		case NL80211_IFTYPE_ADHOC:
 | |
|  		case NL80211_IFTYPE_MESH_POINT:
 | |
|  		case NL80211_IFTYPE_OCB:
 | |
| @@ -345,7 +349,6 @@ ieee80211_get_chanctx_max_required_bw(st
 | |
|  		case NL80211_IFTYPE_WDS:
 | |
|  		case NL80211_IFTYPE_UNSPECIFIED:
 | |
|  		case NUM_NL80211_IFTYPES:
 | |
| -		case NL80211_IFTYPE_MONITOR:
 | |
|  		case NL80211_IFTYPE_P2P_CLIENT:
 | |
|  		case NL80211_IFTYPE_P2P_GO:
 | |
|  			WARN_ON_ONCE(1);
 | |
| @@ -954,6 +957,10 @@ void ieee80211_recalc_smps_chanctx(struc
 | |
|  			if (!link->sdata->u.mgd.associated)
 | |
|  				continue;
 | |
|  			break;
 | |
| +		case NL80211_IFTYPE_MONITOR:
 | |
| +			if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
| +				continue;
 | |
| +			break;
 | |
|  		case NL80211_IFTYPE_AP:
 | |
|  		case NL80211_IFTYPE_ADHOC:
 | |
|  		case NL80211_IFTYPE_MESH_POINT:
 | |
| @@ -966,6 +973,11 @@ void ieee80211_recalc_smps_chanctx(struc
 | |
|  		if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
 | |
|  			continue;
 | |
|  
 | |
| +		if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
 | |
| +			rx_chains_dynamic = rx_chains_static = local->rx_chains;
 | |
| +			break;
 | |
| +		}
 | |
| +
 | |
|  		switch (link->smps_mode) {
 | |
|  		default:
 | |
|  			WARN_ONCE(1, "Invalid SMPS mode %d\n",
 | |
| --- a/net/mac80211/debugfs.c
 | |
| +++ b/net/mac80211/debugfs.c
 | |
| @@ -465,6 +465,7 @@ static const char *hw_flag_names[] = {
 | |
|  	FLAG(SUPPORTS_DYNAMIC_PS),
 | |
|  	FLAG(MFP_CAPABLE),
 | |
|  	FLAG(WANT_MONITOR_VIF),
 | |
| +	FLAG(NO_VIRTUAL_MONITOR),
 | |
|  	FLAG(NO_AUTO_VIF),
 | |
|  	FLAG(SW_CRYPTO_CONTROL),
 | |
|  	FLAG(SUPPORT_FAST_XMIT),
 | |
| --- a/net/mac80211/driver-ops.c
 | |
| +++ b/net/mac80211/driver-ops.c
 | |
| @@ -65,6 +65,7 @@ int drv_add_interface(struct ieee80211_l
 | |
|  	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
 | |
|  		    (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 | |
|  		     !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
 | |
| +		     !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
 | |
|  		     !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
 | |
|  		return -EINVAL;
 | |
|  
 | |
| --- a/net/mac80211/iface.c
 | |
| +++ b/net/mac80211/iface.c
 | |
| @@ -279,8 +279,13 @@ static int _ieee80211_change_mac(struct
 | |
|  	ret = eth_mac_addr(sdata->dev, sa);
 | |
|  
 | |
|  	if (ret == 0) {
 | |
| -		memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
 | |
| -		ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
 | |
| +		if (check_dup) {
 | |
| +			memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
 | |
| +			ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
 | |
| +		} else {
 | |
| +			memset(sdata->vif.addr, 0, ETH_ALEN);
 | |
| +			memset(sdata->vif.bss_conf.addr, 0, ETH_ALEN);
 | |
| +		}
 | |
|  	}
 | |
|  
 | |
|  	/* Regardless of eth_mac_addr() return we still want to add the
 | |
| @@ -699,9 +704,11 @@ static void ieee80211_do_stop(struct iee
 | |
|  		ieee80211_recalc_idle(local);
 | |
|  		ieee80211_recalc_offload(local);
 | |
|  
 | |
| -		if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
 | |
| +		if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
 | |
| +		    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
|  			break;
 | |
|  
 | |
| +		ieee80211_link_release_channel(&sdata->deflink);
 | |
|  		fallthrough;
 | |
|  	default:
 | |
|  		if (!going_down)
 | |
| @@ -1131,7 +1138,8 @@ int ieee80211_add_virtual_monitor(struct
 | |
|  	ASSERT_RTNL();
 | |
|  	lockdep_assert_wiphy(local->hw.wiphy);
 | |
|  
 | |
| -	if (local->monitor_sdata)
 | |
| +	if (local->monitor_sdata ||
 | |
| +	    ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
|  		return 0;
 | |
|  
 | |
|  	sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
 | |
| @@ -1193,6 +1201,9 @@ void ieee80211_del_virtual_monitor(struc
 | |
|  {
 | |
|  	struct ieee80211_sub_if_data *sdata;
 | |
|  
 | |
| +	if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
| +		return;
 | |
| +
 | |
|  	ASSERT_RTNL();
 | |
|  	lockdep_assert_wiphy(local->hw.wiphy);
 | |
|  
 | |
| @@ -1328,7 +1339,8 @@ int ieee80211_do_open(struct wireless_de
 | |
|  			break;
 | |
|  		}
 | |
|  
 | |
| -		if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
 | |
| +		if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
 | |
| +		    ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
 | |
|  			res = drv_add_interface(local, sdata);
 | |
|  			if (res)
 | |
|  				goto err_stop;
 | |
| --- a/net/mac80211/rx.c
 | |
| +++ b/net/mac80211/rx.c
 | |
| @@ -840,6 +840,9 @@ ieee80211_rx_monitor(struct ieee80211_lo
 | |
|  		bool last_monitor = list_is_last(&sdata->u.mntr.list,
 | |
|  						 &local->mon_list);
 | |
|  
 | |
| +		if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
| +			ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space);
 | |
| +
 | |
|  		if (!monskb)
 | |
|  			monskb = ieee80211_make_monitor_skb(local, &origskb,
 | |
|  							    rate, rtap_space,
 | |
| --- a/net/mac80211/tx.c
 | |
| +++ b/net/mac80211/tx.c
 | |
| @@ -1763,7 +1763,8 @@ static bool __ieee80211_tx(struct ieee80
 | |
|  
 | |
|  	switch (sdata->vif.type) {
 | |
|  	case NL80211_IFTYPE_MONITOR:
 | |
| -		if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
 | |
| +		if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
 | |
| +		    ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
 | |
|  			vif = &sdata->vif;
 | |
|  			break;
 | |
|  		}
 | |
| @@ -3952,7 +3953,8 @@ begin:
 | |
|  
 | |
|  	switch (tx.sdata->vif.type) {
 | |
|  	case NL80211_IFTYPE_MONITOR:
 | |
| -		if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
 | |
| +		if ((tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
 | |
| +		    ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
 | |
|  			vif = &tx.sdata->vif;
 | |
|  			break;
 | |
|  		}
 | |
| --- a/net/mac80211/util.c
 | |
| +++ b/net/mac80211/util.c
 | |
| @@ -754,7 +754,8 @@ static void __iterate_interfaces(struct
 | |
|  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 | |
|  		switch (sdata->vif.type) {
 | |
|  		case NL80211_IFTYPE_MONITOR:
 | |
| -			if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
 | |
| +			if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
 | |
| +			    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
|  				continue;
 | |
|  			break;
 | |
|  		case NL80211_IFTYPE_AP_VLAN:
 | |
| @@ -1857,8 +1858,10 @@ int ieee80211_reconfig(struct ieee80211_
 | |
|  	}
 | |
|  
 | |
|  	list_for_each_entry(sdata, &local->interfaces, list) {
 | |
| +		if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 | |
| +		    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
| +			continue;
 | |
|  		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
 | |
| -		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
 | |
|  		    ieee80211_sdata_running(sdata)) {
 | |
|  			res = drv_add_interface(local, sdata);
 | |
|  			if (WARN_ON(res))
 | |
| @@ -1871,11 +1874,14 @@ int ieee80211_reconfig(struct ieee80211_
 | |
|  	 */
 | |
|  	if (res) {
 | |
|  		list_for_each_entry_continue_reverse(sdata, &local->interfaces,
 | |
| -						     list)
 | |
| +						     list) {
 | |
| +			if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 | |
| +			    !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
 | |
| +				continue;
 | |
|  			if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
 | |
| -			    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
 | |
|  			    ieee80211_sdata_running(sdata))
 | |
|  				drv_remove_interface(local, sdata);
 | |
| +		}
 | |
|  		ieee80211_handle_reconfig_failure(local);
 | |
|  		return res;
 | |
|  	}
 |