1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

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.
The vif mac address for non-active monitor interfaces is cleared, in order
to allow the driver to tell them apart from active ones.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://patch.msgid.link/3c55505ee0cf0a5f141fbcb30d1e8be8d9f40373.1728462320.git-series.nbd@nbd.name
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Felix Fietkau 2024-10-09 10:25:48 +02:00 committed by Johannes Berg
parent 9c4f830927
commit 9d40f7e327
9 changed files with 83 additions and 28 deletions

View file

@ -2694,6 +2694,11 @@ struct ieee80211_txq {
* a virtual monitor interface when monitor interfaces are the only * a virtual monitor interface when monitor interfaces are the only
* active interfaces. * 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 * @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
* be created. It is expected user-space will create vifs as * be created. It is expected user-space will create vifs as
* desired (and thus have them named as desired). * desired (and thus have them named as desired).
@ -2853,6 +2858,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_PS, IEEE80211_HW_SUPPORTS_DYNAMIC_PS,
IEEE80211_HW_MFP_CAPABLE, IEEE80211_HW_MFP_CAPABLE,
IEEE80211_HW_WANT_MONITOR_VIF, IEEE80211_HW_WANT_MONITOR_VIF,
IEEE80211_HW_NO_VIRTUAL_MONITOR,
IEEE80211_HW_NO_AUTO_VIF, IEEE80211_HW_NO_AUTO_VIF,
IEEE80211_HW_SW_CRYPTO_CONTROL, IEEE80211_HW_SW_CRYPTO_CONTROL,
IEEE80211_HW_SUPPORT_FAST_XMIT, IEEE80211_HW_SUPPORT_FAST_XMIT,

View file

@ -105,8 +105,11 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
} }
/* also validate MU-MIMO change */ /* also validate MU-MIMO change */
monitor_sdata = wiphy_dereference(local->hw.wiphy, if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
local->monitor_sdata); monitor_sdata = sdata;
else
monitor_sdata = wiphy_dereference(local->hw.wiphy,
local->monitor_sdata);
if (!monitor_sdata && if (!monitor_sdata &&
(params->vht_mumimo_groups || params->vht_mumimo_follow_addr)) (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
@ -114,7 +117,9 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
/* apply all changes now - no failures allowed */ /* 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); ieee80211_set_mu_mimo_follow(monitor_sdata, params);
if (params->flags) { if (params->flags) {
@ -907,22 +912,25 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
if (cfg80211_chandef_identical(&local->monitor_chanreq.oper, sdata = IEEE80211_DEV_TO_SUB_IF(dev);
&chanreq.oper)) if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
return 0; if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
&chanreq.oper))
return 0;
sdata = wiphy_dereference(local->hw.wiphy, sdata = wiphy_dereference(wiphy, local->monitor_sdata);
local->monitor_sdata); if (!sdata)
if (!sdata) goto done;
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)) &chanreq.oper))
return 0; return 0;
ieee80211_link_release_channel(&sdata->deflink); ieee80211_link_release_channel(&sdata->deflink);
ret = ieee80211_link_use_channel(&sdata->deflink, &chanreq, ret = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
IEEE80211_CHANCTX_EXCLUSIVE); IEEE80211_CHANCTX_SHARED);
if (ret) if (ret)
return ret; return ret;
done: done:
@ -3084,7 +3092,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
if (wdev) { if (wdev) {
sdata = IEEE80211_WDEV_TO_SUB_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)) if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -3118,7 +3127,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
local->user_power_level = user_power_level; local->user_power_level = user_power_level;
list_for_each_entry(sdata, &local->interfaces, list) { 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; has_monitor = true;
continue; continue;
} }
@ -3139,7 +3149,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
} }
} }
list_for_each_entry(sdata, &local->interfaces, list) { 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; continue;
for (int link_id = 0; for (int link_id = 0;
@ -4342,7 +4353,8 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
if (chanctx_conf) { if (chanctx_conf) {
*chandef = link->conf->chanreq.oper; *chandef = link->conf->chanreq.oper;
ret = 0; 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 && local->open_count == local->monitors &&
sdata->vif.type == NL80211_IFTYPE_MONITOR) { sdata->vif.type == NL80211_IFTYPE_MONITOR) {
*chandef = local->monitor_chanreq.oper; *chandef = local->monitor_chanreq.oper;

View file

@ -347,6 +347,10 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_NAN:
continue; continue;
case NL80211_IFTYPE_MONITOR:
WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
NO_VIRTUAL_MONITOR));
fallthrough;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_OCB:
@ -355,7 +359,6 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES: case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_GO:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
@ -964,6 +967,10 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
if (!link->sdata->u.mgd.associated) if (!link->sdata->u.mgd.associated)
continue; continue;
break; break;
case NL80211_IFTYPE_MONITOR:
if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
continue;
break;
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
@ -976,6 +983,11 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf) if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
continue; continue;
if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
rx_chains_dynamic = rx_chains_static = local->rx_chains;
break;
}
switch (link->smps_mode) { switch (link->smps_mode) {
default: default:
WARN_ONCE(1, "Invalid SMPS mode %d\n", WARN_ONCE(1, "Invalid SMPS mode %d\n",

View file

@ -456,6 +456,7 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_DYNAMIC_PS), FLAG(SUPPORTS_DYNAMIC_PS),
FLAG(MFP_CAPABLE), FLAG(MFP_CAPABLE),
FLAG(WANT_MONITOR_VIF), FLAG(WANT_MONITOR_VIF),
FLAG(NO_VIRTUAL_MONITOR),
FLAG(NO_AUTO_VIF), FLAG(NO_AUTO_VIF),
FLAG(SW_CRYPTO_CONTROL), FLAG(SW_CRYPTO_CONTROL),
FLAG(SUPPORT_FAST_XMIT), FLAG(SUPPORT_FAST_XMIT),

View file

@ -65,6 +65,7 @@ int drv_add_interface(struct ieee80211_local *local,
if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
(sdata->vif.type == NL80211_IFTYPE_MONITOR && (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)))) !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
return -EINVAL; return -EINVAL;

View file

@ -279,8 +279,13 @@ static int _ieee80211_change_mac(struct ieee80211_sub_if_data *sdata,
ret = eth_mac_addr(sdata->dev, sa); ret = eth_mac_addr(sdata->dev, sa);
if (ret == 0) { if (ret == 0) {
memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN); if (check_dup) {
ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr); 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 /* Regardless of eth_mac_addr() return we still want to add the
@ -699,9 +704,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
ieee80211_recalc_idle(local); ieee80211_recalc_idle(local);
ieee80211_recalc_offload(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; break;
ieee80211_link_release_channel(&sdata->deflink);
fallthrough; fallthrough;
default: default:
if (!going_down) if (!going_down)
@ -1131,7 +1138,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
ASSERT_RTNL(); ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
if (local->monitor_sdata) if (local->monitor_sdata ||
ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
return 0; return 0;
sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
@ -1193,6 +1201,9 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
return;
ASSERT_RTNL(); ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
@ -1328,7 +1339,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
break; 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); res = drv_add_interface(local, sdata);
if (res) if (res)
goto err_stop; goto err_stop;

View file

@ -840,6 +840,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
bool last_monitor = list_is_last(&sdata->u.mntr.list, bool last_monitor = list_is_last(&sdata->u.mntr.list,
&local->mon_list); &local->mon_list);
if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space);
if (!monskb) if (!monskb)
monskb = ieee80211_make_monitor_skb(local, &origskb, monskb = ieee80211_make_monitor_skb(local, &origskb,
rate, rtap_space, rate, rtap_space,

View file

@ -1763,7 +1763,8 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: 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; vif = &sdata->vif;
break; break;
} }
@ -3952,7 +3953,8 @@ begin:
switch (tx.sdata->vif.type) { switch (tx.sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: 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; vif = &tx.sdata->vif;
break; break;
} }

View file

@ -756,7 +756,8 @@ static void __iterate_interfaces(struct ieee80211_local *local,
lockdep_is_held(&local->hw.wiphy->mtx)) { lockdep_is_held(&local->hw.wiphy->mtx)) {
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: 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; continue;
break; break;
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
@ -1873,8 +1874,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
} }
list_for_each_entry(sdata, &local->interfaces, list) { 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 && if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
ieee80211_sdata_running(sdata)) { ieee80211_sdata_running(sdata)) {
res = drv_add_interface(local, sdata); res = drv_add_interface(local, sdata);
if (WARN_ON(res)) if (WARN_ON(res))
@ -1887,11 +1890,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
*/ */
if (res) { if (res) {
list_for_each_entry_continue_reverse(sdata, &local->interfaces, 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 && if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
ieee80211_sdata_running(sdata)) ieee80211_sdata_running(sdata))
drv_remove_interface(local, sdata); drv_remove_interface(local, sdata);
}
ieee80211_handle_reconfig_failure(local); ieee80211_handle_reconfig_failure(local);
return res; return res;
} }