|
|
@@ -0,0 +1,337 @@
|
|
|
+From: Felix Fietkau <[email protected]>
|
|
|
+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 <[email protected]>
|
|
|
+---
|
|
|
+
|
|
|
+--- 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;
|
|
|
+ }
|