From 87033c3a0b22376a043fe5eb5f3690524efd271d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 26 Sep 2024 14:15:14 +0200 Subject: [PATCH] mac80211: improve single-wiphy multi-radio support - add support for configuring allowed radios for a vif - add support for monitor mode on multiple channels Signed-off-by: Felix Fietkau --- ...eck-radio-iface-combination-for-mult.patch | 122 +++++++ ...11-add-option-for-vif-allowed-radios.patch | 309 ++++++++++++++++ ...e-vif-radio-mask-to-limit-ibss-scan-.patch | 79 ++++ ...e-vif-radio-mask-to-limit-chanctx-an.patch | 52 +++ ...11-remove-status-ampdu_delimiter_crc.patch | 67 ++++ ...s-net_device-to-.set_monitor_channel.patch | 165 +++++++++ ...d-flag-to-opt-out-of-virtual-monitor.patch | 337 ++++++++++++++++++ ...fi-cfg80211-add-monitor-SKIP_TX-flag.patch | 56 +++ ...d-support-for-the-monitor-SKIP_TX-fl.patch | 54 +++ ...c80211-refactor-ieee80211_rx_monitor.patch | 94 +++++ ...lter-on-monitor-interfaces-based-on-.patch | 29 ++ ...-report-per-wiphy-radio-antenna-mask.patch | 64 ++++ 12 files changed, 1428 insertions(+) create mode 100644 package/kernel/mac80211/patches/subsys/331-wifi-cfg80211-check-radio-iface-combination-for-mult.patch create mode 100644 package/kernel/mac80211/patches/subsys/350-wifi-cfg80211-add-option-for-vif-allowed-radios.patch create mode 100644 package/kernel/mac80211/patches/subsys/351-wifi-mac80211-use-vif-radio-mask-to-limit-ibss-scan-.patch create mode 100644 package/kernel/mac80211/patches/subsys/352-wifi-mac80211-use-vif-radio-mask-to-limit-chanctx-an.patch create mode 100644 package/kernel/mac80211/patches/subsys/353-wifi-mac80211-remove-status-ampdu_delimiter_crc.patch create mode 100644 package/kernel/mac80211/patches/subsys/354-wifi-cfg80211-pass-net_device-to-.set_monitor_channel.patch create mode 100644 package/kernel/mac80211/patches/subsys/355-wifi-mac80211-add-flag-to-opt-out-of-virtual-monitor.patch create mode 100644 package/kernel/mac80211/patches/subsys/356-wifi-cfg80211-add-monitor-SKIP_TX-flag.patch create mode 100644 package/kernel/mac80211/patches/subsys/357-wifi-mac80211-add-support-for-the-monitor-SKIP_TX-fl.patch create mode 100644 package/kernel/mac80211/patches/subsys/358-wifi-mac80211-refactor-ieee80211_rx_monitor.patch create mode 100644 package/kernel/mac80211/patches/subsys/359-wifi-mac80211-filter-on-monitor-interfaces-based-on-.patch create mode 100644 package/kernel/mac80211/patches/subsys/360-wifi-cfg80211-report-per-wiphy-radio-antenna-mask.patch diff --git a/package/kernel/mac80211/patches/subsys/331-wifi-cfg80211-check-radio-iface-combination-for-mult.patch b/package/kernel/mac80211/patches/subsys/331-wifi-cfg80211-check-radio-iface-combination-for-mult.patch new file mode 100644 index 0000000000..76c351aa83 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/331-wifi-cfg80211-check-radio-iface-combination-for-mult.patch @@ -0,0 +1,122 @@ +From: Karthikeyan Periyasamy +Date: Tue, 17 Sep 2024 19:32:39 +0530 +Subject: [PATCH] wifi: cfg80211: check radio iface combination for multi radio + per wiphy + +Currently, wiphy_verify_combinations() fails for the multi-radio per wiphy +due to the condition check on new global interface combination that DFS +only works on one channel. In a multi-radio scenario, new global interface +combination encompasses the capabilities of all radio combinations, so it +supports more than one channel with DFS. For multi-radio per wiphy, +interface combination verification needs to be performed for radio specific +interface combinations. This is necessary as the new global interface +combination combines the capabilities of all radio combinations. + +Fixes: a01b1e9f9955 ("wifi: mac80211: add support for DFS with multiple radios") +Signed-off-by: Karthikeyan Periyasamy +--- + +--- a/net/wireless/core.c ++++ b/net/wireless/core.c +@@ -599,16 +599,20 @@ use_default_name: + } + EXPORT_SYMBOL(wiphy_new_nm); + +-static int wiphy_verify_combinations(struct wiphy *wiphy) ++static ++int wiphy_verify_iface_combinations(struct wiphy *wiphy, ++ const struct ieee80211_iface_combination *iface_comb, ++ int n_iface_comb, ++ bool combined_radio) + { + const struct ieee80211_iface_combination *c; + int i, j; + +- for (i = 0; i < wiphy->n_iface_combinations; i++) { ++ for (i = 0; i < n_iface_comb; i++) { + u32 cnt = 0; + u16 all_iftypes = 0; + +- c = &wiphy->iface_combinations[i]; ++ c = &iface_comb[i]; + + /* + * Combinations with just one interface aren't real, +@@ -621,9 +625,13 @@ static int wiphy_verify_combinations(str + if (WARN_ON(!c->num_different_channels)) + return -EINVAL; + +- /* DFS only works on one channel. */ +- if (WARN_ON(c->radar_detect_widths && +- (c->num_different_channels > 1))) ++ /* DFS only works on one channel. Avoid this check ++ * for multi-radio global combination, since it hold ++ * the capabilities of all radio combinations. ++ */ ++ if (!combined_radio && ++ WARN_ON(c->radar_detect_widths && ++ c->num_different_channels > 1)) + return -EINVAL; + + if (WARN_ON(!c->n_limits)) +@@ -644,13 +652,21 @@ static int wiphy_verify_combinations(str + if (WARN_ON(wiphy->software_iftypes & types)) + return -EINVAL; + +- /* Only a single P2P_DEVICE can be allowed */ +- if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && ++ /* Only a single P2P_DEVICE can be allowed, avoid this ++ * check for multi-radio global combination, since it ++ * hold the capabilities of all radio combinations. ++ */ ++ if (!combined_radio && ++ WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && + c->limits[j].max > 1)) + return -EINVAL; + +- /* Only a single NAN can be allowed */ +- if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) && ++ /* Only a single NAN can be allowed, avoid this ++ * check for multi-radio global combination, since it ++ * hold the capabilities of all radio combinations. ++ */ ++ if (!combined_radio && ++ WARN_ON(types & BIT(NL80211_IFTYPE_NAN) && + c->limits[j].max > 1)) + return -EINVAL; + +@@ -674,6 +690,34 @@ static int wiphy_verify_combinations(str + return 0; + } + ++static int wiphy_verify_combinations(struct wiphy *wiphy) ++{ ++ int i, ret; ++ bool combined_radio = false; ++ ++ if (wiphy->n_radio) { ++ for (i = 0; i < wiphy->n_radio; i++) { ++ const struct wiphy_radio *radio = &wiphy->radio[i]; ++ ++ ret = wiphy_verify_iface_combinations(wiphy, ++ radio->iface_combinations, ++ radio->n_iface_combinations, ++ false); ++ if (ret) ++ return ret; ++ } ++ ++ combined_radio = true; ++ } ++ ++ ret = wiphy_verify_iface_combinations(wiphy, ++ wiphy->iface_combinations, ++ wiphy->n_iface_combinations, ++ combined_radio); ++ ++ return ret; ++} ++ + int wiphy_register(struct wiphy *wiphy) + { + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); diff --git a/package/kernel/mac80211/patches/subsys/350-wifi-cfg80211-add-option-for-vif-allowed-radios.patch b/package/kernel/mac80211/patches/subsys/350-wifi-cfg80211-add-option-for-vif-allowed-radios.patch new file mode 100644 index 0000000000..d5b42f3a3a --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/350-wifi-cfg80211-add-option-for-vif-allowed-radios.patch @@ -0,0 +1,309 @@ +From: Felix Fietkau +Date: Wed, 17 Jul 2024 15:43:52 +0200 +Subject: [PATCH] wifi: cfg80211: add option for vif allowed radios + +This allows users to prevent a vif from affecting radios other than the +configured ones. This can be useful in cases where e.g. an AP is running +on one radio, and triggering a scan on another radio should not disturb it. + +Changing the allowed radios list for a vif is supported, but only while +it is down. + +While it is possible to achieve the same by always explicitly specifying +a frequency list for scan requests and ensuring that the wrong channel/band +is never accidentally set on an unrelated interface, this change makes +multi-radio wiphy setups a lot easier to deal with for CLI users. + +By itself, this patch only enforces the radio mask for scanning requests +and remain-on-channel. Follow-up changes build on this to limit configured +frequencies. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -6227,6 +6227,7 @@ enum ieee80211_ap_reg_power { + * entered. + * @links[].cac_time_ms: CAC time in ms + * @valid_links: bitmap describing what elements of @links are valid ++ * @radio_mask: Bitmask of radios that this interface is allowed to operate on. + */ + struct wireless_dev { + struct wiphy *wiphy; +@@ -6339,6 +6340,8 @@ struct wireless_dev { + unsigned int cac_time_ms; + } links[IEEE80211_MLD_MAX_NUM_LINKS]; + u16 valid_links; ++ ++ u32 radio_mask; + }; + + static inline const u8 *wdev_address(struct wireless_dev *wdev) +@@ -6525,6 +6528,17 @@ bool cfg80211_radio_chandef_valid(const + const struct cfg80211_chan_def *chandef); + + /** ++ * cfg80211_wdev_channel_allowed - Check if the wdev may use the channel ++ * ++ * @wdev: the wireless device ++ * @chan: channel to check ++ * ++ * Return: whether or not the wdev may use the channel ++ */ ++bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev, ++ struct ieee80211_channel *chan); ++ ++/** + * ieee80211_get_response_rate - get basic rate for a given rate + * + * @sband: the band to look for rates in +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -2868,6 +2868,9 @@ enum nl80211_commands { + * nested item, it contains attributes defined in + * &enum nl80211_if_combination_attrs. + * ++ * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32). ++ * A value of 0 means all radios. ++ * + * @NUM_NL80211_ATTR: total number of nl80211_attrs available + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use +@@ -3416,6 +3419,8 @@ enum nl80211_attrs { + NL80211_ATTR_WIPHY_RADIOS, + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS, + ++ NL80211_ATTR_VIF_RADIO_MASK, ++ + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -829,6 +829,7 @@ static const struct nla_policy nl80211_p + [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), + [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), + [NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG }, ++ [NL80211_ATTR_VIF_RADIO_MASK] = { .type = NLA_U32 }, + }; + + /* policy for the key attributes */ +@@ -3996,7 +3997,8 @@ static int nl80211_send_iface(struct sk_ + nla_put_u32(msg, NL80211_ATTR_GENERATION, + rdev->devlist_generation ^ + (cfg80211_rdev_list_generation << 2)) || +- nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr)) ++ nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr) || ++ nla_put_u32(msg, NL80211_ATTR_VIF_RADIO_MASK, wdev->radio_mask)) + goto nla_put_failure; + + if (rdev->ops->get_channel && !wdev->valid_links) { +@@ -4312,6 +4314,29 @@ static int nl80211_valid_4addr(struct cf + return -EOPNOTSUPP; + } + ++static int nl80211_parse_vif_radio_mask(struct genl_info *info, ++ u32 *radio_mask) ++{ ++ struct cfg80211_registered_device *rdev = info->user_ptr[0]; ++ struct nlattr *attr = info->attrs[NL80211_ATTR_VIF_RADIO_MASK]; ++ u32 mask, allowed; ++ ++ if (!attr) { ++ *radio_mask = 0; ++ return 0; ++ } ++ ++ allowed = BIT(rdev->wiphy.n_radio) - 1; ++ mask = nla_get_u32(attr); ++ if (mask & ~allowed) ++ return -EINVAL; ++ if (!mask) ++ mask = allowed; ++ *radio_mask = mask; ++ ++ return 1; ++} ++ + static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) + { + struct cfg80211_registered_device *rdev = info->user_ptr[0]; +@@ -4319,6 +4344,8 @@ static int nl80211_set_interface(struct + int err; + enum nl80211_iftype otype, ntype; + struct net_device *dev = info->user_ptr[1]; ++ struct wireless_dev *wdev = dev->ieee80211_ptr; ++ u32 radio_mask = 0; + bool change = false; + + memset(¶ms, 0, sizeof(params)); +@@ -4332,8 +4359,6 @@ static int nl80211_set_interface(struct + } + + if (info->attrs[NL80211_ATTR_MESH_ID]) { +- struct wireless_dev *wdev = dev->ieee80211_ptr; +- + if (ntype != NL80211_IFTYPE_MESH_POINT) + return -EINVAL; + if (otype != NL80211_IFTYPE_MESH_POINT) +@@ -4364,6 +4389,12 @@ static int nl80211_set_interface(struct + if (err > 0) + change = true; + ++ err = nl80211_parse_vif_radio_mask(info, &radio_mask); ++ if (err < 0) ++ return err; ++ if (err && netif_running(dev)) ++ return -EBUSY; ++ + if (change) + err = cfg80211_change_iface(rdev, dev, ntype, ¶ms); + else +@@ -4372,11 +4403,11 @@ static int nl80211_set_interface(struct + if (!err && params.use_4addr != -1) + dev->ieee80211_ptr->use_4addr = params.use_4addr; + +- if (change && !err) { +- struct wireless_dev *wdev = dev->ieee80211_ptr; ++ if (radio_mask) ++ wdev->radio_mask = radio_mask; + ++ if (change && !err) + nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE); +- } + + return err; + } +@@ -4387,6 +4418,7 @@ static int _nl80211_new_interface(struct + struct vif_params params; + struct wireless_dev *wdev; + struct sk_buff *msg; ++ u32 radio_mask; + int err; + enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; + +@@ -4424,6 +4456,10 @@ static int _nl80211_new_interface(struct + if (err < 0) + return err; + ++ err = nl80211_parse_vif_radio_mask(info, &radio_mask); ++ if (err < 0) ++ return err; ++ + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; +@@ -4465,6 +4501,9 @@ static int _nl80211_new_interface(struct + break; + } + ++ if (radio_mask) ++ wdev->radio_mask = radio_mask; ++ + if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, + rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) { + nlmsg_free(msg); +@@ -9180,6 +9219,9 @@ static bool cfg80211_off_channel_oper_al + + lockdep_assert_wiphy(wdev->wiphy); + ++ if (!cfg80211_wdev_channel_allowed(wdev, chan)) ++ return false; ++ + if (!cfg80211_beaconing_iface_active(wdev)) + return true; + +@@ -9392,7 +9434,8 @@ static int nl80211_trigger_scan(struct s + } + + /* ignore disabled channels */ +- if (chan->flags & IEEE80211_CHAN_DISABLED) ++ if (chan->flags & IEEE80211_CHAN_DISABLED || ++ !cfg80211_wdev_channel_allowed(wdev, chan)) + continue; + + request->channels[i] = chan; +@@ -9412,7 +9455,8 @@ static int nl80211_trigger_scan(struct s + + chan = &wiphy->bands[band]->channels[j]; + +- if (chan->flags & IEEE80211_CHAN_DISABLED) ++ if (chan->flags & IEEE80211_CHAN_DISABLED || ++ !cfg80211_wdev_channel_allowed(wdev, chan)) + continue; + + request->channels[i] = chan; +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -956,7 +956,8 @@ static int cfg80211_scan_6ghz(struct cfg + struct ieee80211_channel *chan = + ieee80211_get_channel(&rdev->wiphy, ap->center_freq); + +- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) ++ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED || ++ !cfg80211_wdev_channel_allowed(rdev_req->wdev, chan)) + continue; + + for (i = 0; i < rdev_req->n_channels; i++) { +@@ -3490,9 +3491,12 @@ int cfg80211_wext_siwscan(struct net_dev + continue; + + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { ++ struct ieee80211_channel *chan; ++ + /* ignore disabled channels */ +- if (wiphy->bands[band]->channels[j].flags & +- IEEE80211_CHAN_DISABLED) ++ chan = &wiphy->bands[band]->channels[j]; ++ if (chan->flags & IEEE80211_CHAN_DISABLED || ++ !cfg80211_wdev_channel_allowed(creq->wdev, chan)) + continue; + + /* If we have a wireless request structure and the +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -2923,3 +2923,32 @@ bool cfg80211_radio_chandef_valid(const + return true; + } + EXPORT_SYMBOL(cfg80211_radio_chandef_valid); ++ ++bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev, ++ struct ieee80211_channel *chan) ++{ ++ struct wiphy *wiphy = wdev->wiphy; ++ const struct wiphy_radio *radio; ++ struct cfg80211_chan_def chandef; ++ u32 radio_mask; ++ int i; ++ ++ radio_mask = wdev->radio_mask; ++ if (!wiphy->n_radio || radio_mask == BIT(wiphy->n_radio) - 1) ++ return true; ++ ++ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); ++ for (i = 0; i < wiphy->n_radio; i++) { ++ if (!(radio_mask & BIT(i))) ++ continue; ++ ++ radio = &wiphy->radio[i]; ++ if (!cfg80211_radio_chandef_valid(radio, &chandef)) ++ continue; ++ ++ return true; ++ } ++ ++ return false; ++} ++EXPORT_SYMBOL(cfg80211_wdev_channel_allowed); +--- a/net/wireless/core.c ++++ b/net/wireless/core.c +@@ -1415,6 +1415,8 @@ void cfg80211_init_wdev(struct wireless_ + /* allow mac80211 to determine the timeout */ + wdev->ps_timeout = -1; + ++ wdev->radio_mask = BIT(wdev->wiphy->n_radio) - 1; ++ + if ((wdev->iftype == NL80211_IFTYPE_STATION || + wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || + wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) diff --git a/package/kernel/mac80211/patches/subsys/351-wifi-mac80211-use-vif-radio-mask-to-limit-ibss-scan-.patch b/package/kernel/mac80211/patches/subsys/351-wifi-mac80211-use-vif-radio-mask-to-limit-ibss-scan-.patch new file mode 100644 index 0000000000..7363c3873f --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/351-wifi-mac80211-use-vif-radio-mask-to-limit-ibss-scan-.patch @@ -0,0 +1,79 @@ +From: Felix Fietkau +Date: Thu, 26 Sep 2024 14:06:11 +0200 +Subject: [PATCH] wifi: mac80211: use vif radio mask to limit ibss scan + frequencies + +Reject frequencies not supported by any radio that the vif is allowed to use. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/scan.c ++++ b/net/mac80211/scan.c +@@ -1178,14 +1178,14 @@ int ieee80211_request_ibss_scan(struct i + unsigned int n_channels) + { + struct ieee80211_local *local = sdata->local; +- int ret = -EBUSY, i, n_ch = 0; ++ int i, n_ch = 0; + enum nl80211_band band; + + lockdep_assert_wiphy(local->hw.wiphy); + + /* busy scanning */ + if (local->scan_req) +- goto unlock; ++ return -EBUSY; + + /* fill internal scan request */ + if (!channels) { +@@ -1202,7 +1202,9 @@ int ieee80211_request_ibss_scan(struct i + &local->hw.wiphy->bands[band]->channels[i]; + + if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR | +- IEEE80211_CHAN_DISABLED)) ++ IEEE80211_CHAN_DISABLED) || ++ !cfg80211_wdev_channel_allowed(&sdata->wdev, ++ tmp_ch)) + continue; + + local->int_scan_req->channels[n_ch] = tmp_ch; +@@ -1211,21 +1213,23 @@ int ieee80211_request_ibss_scan(struct i + } + + if (WARN_ON_ONCE(n_ch == 0)) +- goto unlock; ++ return -EINVAL; + + local->int_scan_req->n_channels = n_ch; + } else { + for (i = 0; i < n_channels; i++) { + if (channels[i]->flags & (IEEE80211_CHAN_NO_IR | +- IEEE80211_CHAN_DISABLED)) ++ IEEE80211_CHAN_DISABLED) || ++ !cfg80211_wdev_channel_allowed(&sdata->wdev, ++ channels[i])) + continue; + + local->int_scan_req->channels[n_ch] = channels[i]; + n_ch++; + } + +- if (WARN_ON_ONCE(n_ch == 0)) +- goto unlock; ++ if (n_ch == 0) ++ return -EINVAL; + + local->int_scan_req->n_channels = n_ch; + } +@@ -1235,9 +1239,7 @@ int ieee80211_request_ibss_scan(struct i + memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); + local->int_scan_req->ssids[0].ssid_len = ssid_len; + +- ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); +- unlock: +- return ret; ++ return __ieee80211_start_scan(sdata, sdata->local->int_scan_req); + } + + void ieee80211_scan_cancel(struct ieee80211_local *local) diff --git a/package/kernel/mac80211/patches/subsys/352-wifi-mac80211-use-vif-radio-mask-to-limit-chanctx-an.patch b/package/kernel/mac80211/patches/subsys/352-wifi-mac80211-use-vif-radio-mask-to-limit-chanctx-an.patch new file mode 100644 index 0000000000..ac3d101148 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/352-wifi-mac80211-use-vif-radio-mask-to-limit-chanctx-an.patch @@ -0,0 +1,52 @@ +From: Felix Fietkau +Date: Thu, 26 Sep 2024 14:07:50 +0200 +Subject: [PATCH] wifi: mac80211: use vif radio mask to limit creating chanctx + +Reject frequencies not supported by any radio that the vif is allowed to use. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/chan.c ++++ b/net/mac80211/chan.c +@@ -1167,7 +1167,7 @@ ieee80211_replace_chanctx(struct ieee802 + static bool + ieee80211_find_available_radio(struct ieee80211_local *local, + const struct ieee80211_chan_req *chanreq, +- int *radio_idx) ++ u32 radio_mask, int *radio_idx) + { + struct wiphy *wiphy = local->hw.wiphy; + const struct wiphy_radio *radio; +@@ -1178,6 +1178,9 @@ ieee80211_find_available_radio(struct ie + return true; + + for (i = 0; i < wiphy->n_radio; i++) { ++ if (!(radio_mask & BIT(i))) ++ continue; ++ + radio = &wiphy->radio[i]; + if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper)) + continue; +@@ -1211,7 +1214,9 @@ int ieee80211_link_reserve_chanctx(struc + new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode); + if (!new_ctx) { + if (ieee80211_can_create_new_chanctx(local, -1) && +- ieee80211_find_available_radio(local, chanreq, &radio_idx)) ++ ieee80211_find_available_radio(local, chanreq, ++ sdata->wdev.radio_mask, ++ &radio_idx)) + new_ctx = ieee80211_new_chanctx(local, chanreq, mode, + false, radio_idx); + else +@@ -1881,7 +1886,9 @@ int _ieee80211_link_use_channel(struct i + /* Note: context is now reserved */ + if (ctx) + reserved = true; +- else if (!ieee80211_find_available_radio(local, chanreq, &radio_idx)) ++ else if (!ieee80211_find_available_radio(local, chanreq, ++ sdata->wdev.radio_mask, ++ &radio_idx)) + ctx = ERR_PTR(-EBUSY); + else + ctx = ieee80211_new_chanctx(local, chanreq, mode, diff --git a/package/kernel/mac80211/patches/subsys/353-wifi-mac80211-remove-status-ampdu_delimiter_crc.patch b/package/kernel/mac80211/patches/subsys/353-wifi-mac80211-remove-status-ampdu_delimiter_crc.patch new file mode 100644 index 0000000000..c0cdcdd6bc --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/353-wifi-mac80211-remove-status-ampdu_delimiter_crc.patch @@ -0,0 +1,67 @@ +From: Felix Fietkau +Date: Wed, 17 Jul 2024 22:49:16 +0200 +Subject: [PATCH] wifi: mac80211: remove status->ampdu_delimiter_crc + +This was never used by any driver, so remove it to free up some space. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -1448,8 +1448,6 @@ ieee80211_tx_info_clear_status(struct ie + * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU + * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected + * on this subframe +- * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC +- * is stored in the @ampdu_delimiter_crc field) + * @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was + * done by the hardware + * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without +@@ -1521,7 +1519,7 @@ enum mac80211_rx_flags { + RX_FLAG_AMPDU_LAST_KNOWN = BIT(12), + RX_FLAG_AMPDU_IS_LAST = BIT(13), + RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14), +- RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(15), ++ /* one free bit at 15 */ + RX_FLAG_MACTIME = BIT(16) | BIT(17), + RX_FLAG_MACTIME_PLCP_START = 1 << 16, + RX_FLAG_MACTIME_START = 2 << 16, +@@ -1618,7 +1616,6 @@ enum mac80211_rx_encoding { + * @rx_flags: internal RX flags for mac80211 + * @ampdu_reference: A-MPDU reference number, must be a different value for + * each A-MPDU but the same for each subframe within one A-MPDU +- * @ampdu_delimiter_crc: A-MPDU delimiter CRC + * @zero_length_psdu_type: radiotap type of the 0-length PSDU + * @link_valid: if the link which is identified by @link_id is valid. This flag + * is set only when connection is MLO. +@@ -1656,7 +1653,6 @@ struct ieee80211_rx_status { + s8 signal; + u8 chains; + s8 chain_signal[IEEE80211_MAX_CHAINS]; +- u8 ampdu_delimiter_crc; + u8 zero_length_psdu_type; + u8 link_valid:1, link_id:4; + }; +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -508,18 +508,13 @@ ieee80211_add_rx_radiotap_header(struct + flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) + flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; +- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) +- flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; + if (status->flag & RX_FLAG_AMPDU_EOF_BIT_KNOWN) + flags |= IEEE80211_RADIOTAP_AMPDU_EOF_KNOWN; + if (status->flag & RX_FLAG_AMPDU_EOF_BIT) + flags |= IEEE80211_RADIOTAP_AMPDU_EOF; + put_unaligned_le16(flags, pos); + pos += 2; +- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) +- *pos++ = status->ampdu_delimiter_crc; +- else +- *pos++ = 0; ++ *pos++ = 0; + *pos++ = 0; + } + diff --git a/package/kernel/mac80211/patches/subsys/354-wifi-cfg80211-pass-net_device-to-.set_monitor_channel.patch b/package/kernel/mac80211/patches/subsys/354-wifi-cfg80211-pass-net_device-to-.set_monitor_channel.patch new file mode 100644 index 0000000000..c2a915963c --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/354-wifi-cfg80211-pass-net_device-to-.set_monitor_channel.patch @@ -0,0 +1,165 @@ +From: Felix Fietkau +Date: Thu, 26 Sep 2024 19:52:30 +0200 +Subject: [PATCH] wifi: cfg80211: pass net_device to .set_monitor_channel + +Preparation for allowing multiple monitor interfaces with different channels +on a multi-radio wiphy. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/wireless/ath/wil6210/cfg80211.c ++++ b/drivers/net/wireless/ath/wil6210/cfg80211.c +@@ -1493,6 +1493,7 @@ out: + } + + static int wil_cfg80211_set_channel(struct wiphy *wiphy, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef) + { + struct wil6210_priv *wil = wiphy_to_wil(wiphy); +--- a/drivers/net/wireless/marvell/libertas/cfg.c ++++ b/drivers/net/wireless/marvell/libertas/cfg.c +@@ -486,6 +486,7 @@ static int lbs_add_wps_enrollee_tlv(u8 * + */ + + static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef) + { + struct lbs_private *priv = wiphy_priv(wiphy); +--- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c ++++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c +@@ -231,6 +231,7 @@ struct wilc_vif *wilc_get_wl_to_vif(stru + } + + static int set_channel(struct wiphy *wiphy, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef) + { + struct wilc *wl = wiphy_priv(wiphy); +@@ -1424,7 +1425,7 @@ static int start_ap(struct wiphy *wiphy, + struct wilc_vif *vif = netdev_priv(dev); + int ret; + +- ret = set_channel(wiphy, &settings->chandef); ++ ret = set_channel(wiphy, dev, &settings->chandef); + if (ret != 0) + netdev_err(dev, "Error in setting channel\n"); + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -4700,6 +4700,7 @@ struct cfg80211_ops { + struct ieee80211_channel *chan); + + int (*set_monitor_channel)(struct wiphy *wiphy, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef); + + int (*scan)(struct wiphy *wiphy, +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -879,6 +879,7 @@ static int ieee80211_get_station(struct + } + + static int ieee80211_set_monitor_channel(struct wiphy *wiphy, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef) + { + struct ieee80211_local *local = wiphy_priv(wiphy); +--- a/net/wireless/chan.c ++++ b/net/wireless/chan.c +@@ -1673,6 +1673,7 @@ bool cfg80211_reg_check_beaconing(struct + EXPORT_SYMBOL(cfg80211_reg_check_beaconing); + + int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef) + { + if (!rdev->ops->set_monitor_channel) +@@ -1680,7 +1681,7 @@ int cfg80211_set_monitor_channel(struct + if (!cfg80211_has_monitors_only(rdev)) + return -EBUSY; + +- return rdev_set_monitor_channel(rdev, chandef); ++ return rdev_set_monitor_channel(rdev, dev, chandef); + } + + bool cfg80211_any_usable_channels(struct wiphy *wiphy, +--- a/net/wireless/core.h ++++ b/net/wireless/core.h +@@ -510,6 +510,7 @@ static inline unsigned int elapsed_jiffi + } + + int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef); + + int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -3562,7 +3562,7 @@ static int __nl80211_set_channel(struct + case NL80211_IFTYPE_MESH_POINT: + return cfg80211_set_mesh_channel(rdev, wdev, &chandef); + case NL80211_IFTYPE_MONITOR: +- return cfg80211_set_monitor_channel(rdev, &chandef); ++ return cfg80211_set_monitor_channel(rdev, dev, &chandef); + default: + break; + } +--- a/net/wireless/rdev-ops.h ++++ b/net/wireless/rdev-ops.h +@@ -445,11 +445,12 @@ rdev_libertas_set_mesh_channel(struct cf + + static inline int + rdev_set_monitor_channel(struct cfg80211_registered_device *rdev, ++ struct net_device *dev, + struct cfg80211_chan_def *chandef) + { + int ret; +- trace_rdev_set_monitor_channel(&rdev->wiphy, chandef); +- ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef); ++ trace_rdev_set_monitor_channel(&rdev->wiphy, dev, chandef); ++ ret = rdev->ops->set_monitor_channel(&rdev->wiphy, dev, chandef); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; + } +--- a/net/wireless/trace.h ++++ b/net/wireless/trace.h +@@ -1318,19 +1318,21 @@ TRACE_EVENT(rdev_libertas_set_mesh_chann + ); + + TRACE_EVENT(rdev_set_monitor_channel, +- TP_PROTO(struct wiphy *wiphy, ++ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_chan_def *chandef), +- TP_ARGS(wiphy, chandef), ++ TP_ARGS(wiphy, netdev, chandef), + TP_STRUCT__entry( + WIPHY_ENTRY ++ NETDEV_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; ++ NETDEV_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), +- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, +- WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ++ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT, ++ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG) + ); + + TRACE_EVENT(rdev_auth, +--- a/net/wireless/wext-compat.c ++++ b/net/wireless/wext-compat.c +@@ -830,7 +830,7 @@ static int cfg80211_wext_siwfreq(struct + ret = -EINVAL; + break; + } +- ret = cfg80211_set_monitor_channel(rdev, &chandef); ++ ret = cfg80211_set_monitor_channel(rdev, dev, &chandef); + break; + case NL80211_IFTYPE_MESH_POINT: + freq = cfg80211_wext_freq(wextfreq); diff --git a/package/kernel/mac80211/patches/subsys/355-wifi-mac80211-add-flag-to-opt-out-of-virtual-monitor.patch b/package/kernel/mac80211/patches/subsys/355-wifi-mac80211-add-flag-to-opt-out-of-virtual-monitor.patch new file mode 100644 index 0000000000..2510cb0dbe --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/355-wifi-mac80211-add-flag-to-opt-out-of-virtual-monitor.patch @@ -0,0 +1,337 @@ +From: Felix Fietkau +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 +--- + +--- 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; + } diff --git a/package/kernel/mac80211/patches/subsys/356-wifi-cfg80211-add-monitor-SKIP_TX-flag.patch b/package/kernel/mac80211/patches/subsys/356-wifi-cfg80211-add-monitor-SKIP_TX-flag.patch new file mode 100644 index 0000000000..dfc01c6c9b --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/356-wifi-cfg80211-add-monitor-SKIP_TX-flag.patch @@ -0,0 +1,56 @@ +From: Felix Fietkau +Date: Mon, 30 Sep 2024 17:04:09 +0200 +Subject: [PATCH] wifi: cfg80211: add monitor SKIP_TX flag + +This can be used to indicate that the user is not interested in receiving +locally sent packets on the monitor interface. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -2272,6 +2272,7 @@ static inline int cfg80211_get_station(s + * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering + * @MONITOR_FLAG_COOK_FRAMES: report frames after processing + * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address ++ * @MONITOR_FLAG_SKIP_TX: do not pass locally transmitted frames + */ + enum monitor_flags { + MONITOR_FLAG_CHANGED = BIT(__NL80211_MNTR_FLAG_INVALID), +@@ -2281,6 +2282,7 @@ enum monitor_flags { + MONITOR_FLAG_OTHER_BSS = BIT(NL80211_MNTR_FLAG_OTHER_BSS), + MONITOR_FLAG_COOK_FRAMES = BIT(NL80211_MNTR_FLAG_COOK_FRAMES), + MONITOR_FLAG_ACTIVE = BIT(NL80211_MNTR_FLAG_ACTIVE), ++ MONITOR_FLAG_SKIP_TX = BIT(NL80211_MNTR_FLAG_SKIP_TX), + }; + + /** +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -4703,6 +4703,7 @@ enum nl80211_survey_info { + * overrides all other flags. + * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address + * and ACK incoming unicast packets. ++ * @NL80211_MNTR_FLAG_SKIP_TX: do not pass local tx packets + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag +@@ -4715,6 +4716,7 @@ enum nl80211_mntr_flags { + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + NL80211_MNTR_FLAG_ACTIVE, ++ NL80211_MNTR_FLAG_SKIP_TX, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -4201,6 +4201,7 @@ static const struct nla_policy mntr_flag + [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, + [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, + [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG }, ++ [NL80211_MNTR_FLAG_SKIP_TX] = { .type = NLA_FLAG }, + }; + + static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) diff --git a/package/kernel/mac80211/patches/subsys/357-wifi-mac80211-add-support-for-the-monitor-SKIP_TX-fl.patch b/package/kernel/mac80211/patches/subsys/357-wifi-mac80211-add-support-for-the-monitor-SKIP_TX-fl.patch new file mode 100644 index 0000000000..e62c15c19d --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/357-wifi-mac80211-add-support-for-the-monitor-SKIP_TX-fl.patch @@ -0,0 +1,54 @@ +From: Felix Fietkau +Date: Mon, 30 Sep 2024 17:05:18 +0200 +Subject: [PATCH] wifi: mac80211: add support for the monitor SKIP_TX flag + +Do not pass locally sent packets to monitor interfaces with this flag set. +Skip processing tx packets on the status call entirely if no monitor +interfaces without this flag are present. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1374,7 +1374,7 @@ struct ieee80211_local { + spinlock_t queue_stop_reason_lock; + + int open_count; +- int monitors, cooked_mntrs; ++ int monitors, cooked_mntrs, tx_mntrs; + /* number of interfaces with corresponding FIF_ flags */ + int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll, + fif_probe_req; +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -1094,6 +1094,8 @@ void ieee80211_adjust_monitor_flags(stru + ADJUST(CONTROL, control); + ADJUST(CONTROL, pspoll); + ADJUST(OTHER_BSS, other_bss); ++ if (!(flags & MONITOR_FLAG_SKIP_TX)) ++ local->tx_mntrs += offset; + + #undef ADJUST + } +--- a/net/mac80211/status.c ++++ b/net/mac80211/status.c +@@ -927,6 +927,9 @@ void ieee80211_tx_monitor(struct ieee802 + if (!ieee80211_sdata_running(sdata)) + continue; + ++ if (sdata->u.mntr.flags & MONITOR_FLAG_SKIP_TX) ++ continue; ++ + if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) && + !send_to_cooked) + continue; +@@ -1099,7 +1102,7 @@ static void __ieee80211_tx_status(struct + * This is a bit racy but we can avoid a lot of work + * with this test... + */ +- if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) { ++ if (!local->tx_mntrs && (!send_to_cooked || !local->cooked_mntrs)) { + if (status->free_list) + list_add_tail(&skb->list, status->free_list); + else diff --git a/package/kernel/mac80211/patches/subsys/358-wifi-mac80211-refactor-ieee80211_rx_monitor.patch b/package/kernel/mac80211/patches/subsys/358-wifi-mac80211-refactor-ieee80211_rx_monitor.patch new file mode 100644 index 0000000000..cc976060d4 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/358-wifi-mac80211-refactor-ieee80211_rx_monitor.patch @@ -0,0 +1,94 @@ +From: Felix Fietkau +Date: Wed, 2 Oct 2024 12:31:22 +0200 +Subject: [PATCH] wifi: mac80211: refactor ieee80211_rx_monitor + +Rework the monitor mode interface iteration to get rid of the last_monitor +condition. Preparation for further filtering received monitor packets. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -762,8 +762,8 @@ ieee80211_rx_monitor(struct ieee80211_lo + struct ieee80211_rate *rate) + { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); +- struct ieee80211_sub_if_data *sdata; +- struct sk_buff *monskb = NULL; ++ struct ieee80211_sub_if_data *sdata, *prev_sdata = NULL; ++ struct sk_buff *skb, *monskb = NULL; + int present_fcs_len = 0; + unsigned int rtap_space = 0; + struct ieee80211_sub_if_data *monitor_sdata = +@@ -837,8 +837,10 @@ ieee80211_rx_monitor(struct ieee80211_lo + ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space); + + list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) { +- bool last_monitor = list_is_last(&sdata->u.mntr.list, +- &local->mon_list); ++ if (!prev_sdata) { ++ prev_sdata = sdata; ++ continue; ++ } + + if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) + ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space); +@@ -846,34 +848,34 @@ ieee80211_rx_monitor(struct ieee80211_lo + if (!monskb) + monskb = ieee80211_make_monitor_skb(local, &origskb, + rate, rtap_space, +- only_monitor && +- last_monitor); +- +- if (monskb) { +- struct sk_buff *skb; ++ false); ++ if (!monskb) ++ continue; + +- if (last_monitor) { +- skb = monskb; +- monskb = NULL; +- } else { +- skb = skb_clone(monskb, GFP_ATOMIC); +- } ++ skb = skb_clone(monskb, GFP_ATOMIC); ++ if (!skb) ++ continue; ++ ++ skb->dev = prev_sdata->dev; ++ dev_sw_netstats_rx_add(skb->dev, skb->len); ++ netif_receive_skb(skb); ++ prev_sdata = sdata; ++ } + +- if (skb) { +- skb->dev = sdata->dev; +- dev_sw_netstats_rx_add(skb->dev, skb->len); +- netif_receive_skb(skb); +- } ++ if (prev_sdata) { ++ if (monskb) ++ skb = monskb; ++ else ++ skb = ieee80211_make_monitor_skb(local, &origskb, ++ rate, rtap_space, ++ only_monitor); ++ if (skb) { ++ skb->dev = prev_sdata->dev; ++ dev_sw_netstats_rx_add(skb->dev, skb->len); ++ netif_receive_skb(skb); + } +- +- if (last_monitor) +- break; + } + +- /* this happens if last_monitor was erroneously false */ +- dev_kfree_skb(monskb); +- +- /* ditto */ + if (!origskb) + return NULL; + diff --git a/package/kernel/mac80211/patches/subsys/359-wifi-mac80211-filter-on-monitor-interfaces-based-on-.patch b/package/kernel/mac80211/patches/subsys/359-wifi-mac80211-filter-on-monitor-interfaces-based-on-.patch new file mode 100644 index 0000000000..4b2c67aeba --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/359-wifi-mac80211-filter-on-monitor-interfaces-based-on-.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Wed, 2 Oct 2024 12:35:13 +0200 +Subject: [PATCH] wifi: mac80211: filter on monitor interfaces based on + configured channel + +When a monitor interface has an assigned channel (only happens with the +NO_VIRTUAL_MONITOR feature), only pass packets received on that channel. +This is useful for monitoring on multiple channels at the same time using +multiple monitor interfaces. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -837,6 +837,13 @@ ieee80211_rx_monitor(struct ieee80211_lo + ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space); + + list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) { ++ struct cfg80211_chan_def *chandef; ++ ++ chandef = &sdata->vif.bss_conf.chanreq.oper; ++ if (chandef->chan && ++ chandef->chan->center_freq != status->freq) ++ continue; ++ + if (!prev_sdata) { + prev_sdata = sdata; + continue; diff --git a/package/kernel/mac80211/patches/subsys/360-wifi-cfg80211-report-per-wiphy-radio-antenna-mask.patch b/package/kernel/mac80211/patches/subsys/360-wifi-cfg80211-report-per-wiphy-radio-antenna-mask.patch new file mode 100644 index 0000000000..178e01dc16 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/360-wifi-cfg80211-report-per-wiphy-radio-antenna-mask.patch @@ -0,0 +1,64 @@ +From: Felix Fietkau +Date: Wed, 7 Aug 2024 13:31:07 +0200 +Subject: [PATCH] wifi: cfg80211: report per wiphy radio antenna mask + +With multi-radio devices, each radio typically gets a fixed set of antennas. +In order to be able to disable specific antennas for some radios, user space +needs to know which antenna mask bits are assigned to which radio. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -5443,6 +5443,8 @@ struct wiphy_radio_freq_range { + * @iface_combinations: Valid interface combinations array, should not + * list single interface types. + * @n_iface_combinations: number of entries in @iface_combinations array. ++ * ++ * @antenna_mask: bitmask of antennas connected to this radio. + */ + struct wiphy_radio { + const struct wiphy_radio_freq_range *freq_range; +@@ -5450,6 +5452,8 @@ struct wiphy_radio { + + const struct ieee80211_iface_combination *iface_combinations; + int n_iface_combinations; ++ ++ u32 antenna_mask; + }; + + #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -8038,6 +8038,8 @@ enum nl80211_ap_settings_flags { + * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface + * combination for this radio. Attribute may be present multiple times + * and contains attributes defined in &enum nl80211_if_combination_attrs. ++ * @NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK: bitmask (u32) of antennas ++ * connected to this radio. + * + * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal + * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute +@@ -8048,6 +8050,7 @@ enum nl80211_wiphy_radio_attrs { + NL80211_WIPHY_RADIO_ATTR_INDEX, + NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE, + NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, ++ NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, + + /* keep last */ + __NL80211_WIPHY_RADIO_ATTR_LAST, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -2431,6 +2431,11 @@ static int nl80211_put_radio(struct wiph + if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx)) + goto nla_put_failure; + ++ if (r->antenna_mask && ++ nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, ++ r->antenna_mask)) ++ goto nla_put_failure; ++ + for (i = 0; i < r->n_freq_range; i++) { + const struct wiphy_radio_freq_range *range = &r->freq_range[i]; + -- 2.30.2