]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: add flag to opt out of virtual monitor support
authorFelix Fietkau <nbd@nbd.name>
Wed, 9 Oct 2024 08:25:48 +0000 (10:25 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 23 Oct 2024 14:45:43 +0000 (16:45 +0200)
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>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/driver-ops.c
net/mac80211/iface.c
net/mac80211/rx.c
net/mac80211/tx.c
net/mac80211/util.c

index d4f23224eff98b34e431ba03f385dd86550a226c..b4f246cdcca4b346a65c0927e345a55baa0cc22a 100644 (file)
@@ -2694,6 +2694,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).
@@ -2853,6 +2858,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,
index 492349d6f7bb3cb45ef1c0588ba34d4e8dc07f6c..6c0b228523cb4e2c496c7fc10361e355fdd5b0d8 100644 (file)
@@ -105,8 +105,11 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
        }
 
        /* 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(struct ieee80211_sub_if_data *sdata,
 
        /* 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) {
@@ -907,22 +912,25 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 
        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:
@@ -3084,7 +3092,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
        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;
 
@@ -3118,7 +3127,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
        local->user_power_level = user_power_level;
 
        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;
                }
@@ -3139,7 +3149,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
                }
        }
        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;
 
                for (int link_id = 0;
@@ -4342,7 +4353,8 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
        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;
index 996965005d49a86ed05d31d2043c008f0509bc48..a442cb66752032206b271696b42eb3cde39e4366 100644 (file)
@@ -347,6 +347,10 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
                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:
@@ -355,7 +359,6 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
                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);
@@ -964,6 +967,10 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                        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:
@@ -976,6 +983,11 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                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",
index 02b5476a4376c0778b57ec73d382ec96dd2b12ef..b777240c924ea4c1531f846511cb72ba8022b0ae 100644 (file)
@@ -456,6 +456,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),
index 84d048339113a27399e45ad0959cc97769bfdf25..299d38e9e8630b1c58255c87612ad6c66ed25b33 100644 (file)
@@ -65,6 +65,7 @@ int drv_add_interface(struct ieee80211_local *local,
        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;
 
index 7a99fa057cd9481db7d58ecd60491cadaae80202..683cc50cc26655adc6e9ef5c4807cb3457c848f1 100644 (file)
@@ -279,8 +279,13 @@ static int _ieee80211_change_mac(struct ieee80211_sub_if_data *sdata,
        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 ieee80211_sub_if_data *sdata, bool going_do
                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 ieee80211_local *local)
        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(struct ieee80211_local *local)
 {
        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_dev *wdev, bool coming_up)
                        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;
index a34523bbd156e910e6c988356503626e877cb47c..d032bfb00ade24366db549bbc6a0be5d0deee127 100644 (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,
                                                 &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,
index f10379bef9fb64b3d4088245f1769966c3c43fa7..a24636bda67936b4dc0f6a730b7aaa2986e56003 100644 (file)
@@ -1763,7 +1763,8 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
 
        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;
                }
index bd93de637f9441ccdc18938277973830e8baf94b..a4e1301cc999d24d6ab1bd899742a2ff04229040 100644 (file)
@@ -756,7 +756,8 @@ static void __iterate_interfaces(struct ieee80211_local *local,
                                lockdep_is_held(&local->hw.wiphy->mtx)) {
                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:
@@ -1873,8 +1874,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        }
 
        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))
@@ -1887,11 +1890,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         */
        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;
        }