@@ -3921,6 +3921,10 @@ struct ieee80211_prep_tx_info {
* See the section "Frame filtering" for more information.
* This callback must be implemented and can sleep.
*
+ * @config_radio_filter: Configure the radio's RX filter.
+ * See the section "Frame filtering" for more information.
+ * This callback is optional and can sleep.
+ *
* @config_iface_filter: Configure the interface's RX filter.
* This callback is optional and is used to configure which frames
* should be passed to mac80211. The filter_flags is the combination
@@ -4489,6 +4493,10 @@ struct ieee80211_ops {
unsigned int changed_flags,
unsigned int *total_flags,
u64 multicast);
+ void (*config_radio_filter)(struct ieee80211_hw *hw,
+ unsigned int radio_idx,
+ unsigned int changed_flags,
+ unsigned int *total_flags);
void (*config_iface_filter)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
unsigned int filter_flags,
@@ -242,6 +242,24 @@ static inline void drv_configure_filter(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline void drv_config_radio_filter(struct ieee80211_local *local,
+ unsigned int radio_idx,
+ unsigned int changed_flags,
+ unsigned int *total_flags)
+{
+ might_sleep();
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ if (!local->ops->config_radio_filter)
+ return;
+
+ trace_drv_config_radio_filter(local, radio_idx, changed_flags,
+ total_flags);
+ local->ops->config_radio_filter(&local->hw, radio_idx, changed_flags,
+ total_flags);
+ trace_drv_return_void(local);
+}
+
static inline void drv_config_iface_filter(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
unsigned int filter_flags,
@@ -1330,7 +1330,19 @@ enum mac80211_scan_state {
DECLARE_STATIC_KEY_FALSE(aql_disable);
+struct ieee80211_radio_filter {
+ /* number of interfaces with corresponding FIF_ flags */
+ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
+ fif_probe_req;
+
+ /* number of interfaces with allmulti RX */
+ atomic_t iff_allmultis;
+
+ unsigned int filter_flags; /* FIF_* */
+};
+
struct ieee80211_radio_data {
+ struct ieee80211_radio_filter filter;
u32 monitors;
u32 open_count;
bool started;
@@ -1378,12 +1390,10 @@ struct ieee80211_local {
int open_count;
int monitors, cooked_mntrs;
- /* number of interfaces with corresponding FIF_ flags */
- int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
- fif_probe_req;
+ struct ieee80211_radio_filter filter;
+
bool probe_req_reg;
bool rx_mcast_action_reg;
- unsigned int filter_flags; /* FIF_* */
bool wiphy_ciphers_allocated;
@@ -1478,9 +1488,6 @@ struct ieee80211_local {
atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES];
- /* number of interfaces with allmulti RX */
- atomic_t iff_allmultis;
-
struct rate_control_ref *rate_ctrl;
struct arc4_ctx wep_tx_ctx;
@@ -458,6 +458,39 @@ static int ieee80211_open(struct net_device *dev)
return err;
}
+static bool
+__ieee80211_sdata_set_filter(struct ieee80211_radio_filter *filter,
+ struct ieee80211_sub_if_data *sdata, int ofs)
+{
+ if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+ atomic_add(ofs, &filter->iff_allmultis);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ filter->fif_pspoll += ofs;
+ filter->fif_probe_req += ofs;
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ filter->fif_probe_req += ofs;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+ieee80211_sdata_set_filter(struct ieee80211_sub_if_data *sdata, int ofs)
+{
+ struct ieee80211_local *local = sdata->local;
+ int i;
+
+ for (i = 0; i < local->hw.wiphy->n_radio; i++)
+ if (sdata->wdev.radio_mask & BIT(i))
+ __ieee80211_sdata_set_filter(&local->radio_data[i].filter,
+ sdata, ofs);
+
+ return __ieee80211_sdata_set_filter(&local->filter, sdata, ofs);
+}
+
static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down)
{
struct ieee80211_local *local = sdata->local;
@@ -514,16 +547,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
flushed = sta_info_flush(sdata, -1);
WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && flushed > 0);
- /* don't count this interface for allmulti while it is down */
- if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
- atomic_dec(&local->iff_allmultis);
-
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- local->fif_pspoll--;
- local->fif_probe_req--;
- } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- local->fif_probe_req--;
- }
+ ieee80211_sdata_set_filter(sdata, -1);
if (sdata->dev) {
netif_addr_lock_bh(sdata->dev);
@@ -796,16 +820,22 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_radio_filter *filter = &local->filter;
int allmulti, sdata_allmulti;
+ int i, ofs;
allmulti = !!(dev->flags & IFF_ALLMULTI);
sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
if (allmulti != sdata_allmulti) {
- if (dev->flags & IFF_ALLMULTI)
- atomic_inc(&local->iff_allmultis);
- else
- atomic_dec(&local->iff_allmultis);
+ ofs = (dev->flags & IFF_ALLMULTI) ? 1 : -1;
+ atomic_add(ofs, &filter->iff_allmultis);
+ for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+ if (!(sdata->wdev.radio_mask & BIT(i)))
+ continue;
+ filter = &local->radio_data[i].filter;
+ atomic_add(ofs, &filter->iff_allmultis);
+ }
sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
}
@@ -1080,15 +1110,16 @@ void ieee80211_recalc_offload(struct ieee80211_local *local)
}
}
-void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
- const int offset)
+static void
+__ieee80211_adjust_monitor_flags(struct ieee80211_radio_filter *filter,
+ struct ieee80211_sub_if_data *sdata,
+ const int offset)
{
- struct ieee80211_local *local = sdata->local;
u32 flags = sdata->u.mntr.flags;
#define ADJUST(_f, _s) do { \
if (flags & MONITOR_FLAG_##_f) \
- local->fif_##_s += offset; \
+ filter->fif_##_s += offset; \
} while (0)
ADJUST(FCSFAIL, fcsfail);
@@ -1100,6 +1131,14 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
#undef ADJUST
}
+void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
+ const int offset)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ __ieee80211_adjust_monitor_flags(&local->filter, sdata, offset);
+}
+
static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -1399,15 +1438,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
goto err_del_interface;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- local->fif_pspoll++;
- local->fif_probe_req++;
-
- ieee80211_configure_filter(local);
- } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- local->fif_probe_req++;
- }
-
if (sdata->vif.probe_req_reg)
drv_config_iface_filter(local, sdata,
FIF_PROBE_REQ,
@@ -1445,6 +1475,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
sdata->vif.type != NL80211_IFTYPE_STATION);
}
+ if (ieee80211_sdata_set_filter(sdata, 1))
+ ieee80211_configure_filter(local);
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_P2P_DEVICE:
rcu_assign_pointer(local->p2p_sdata, sdata);
@@ -1458,14 +1491,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
break;
}
- /*
- * set_multicast_list will be invoked by the networking core
- * which will check whether any increments here were done in
- * error and sync them down to the hardware as filter flags.
- */
- if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
- atomic_inc(&local->iff_allmultis);
-
if (coming_up) {
local->open_count++;
for (i = 0; i < local->hw.wiphy->n_radio; i++) {
@@ -34,54 +34,80 @@
#include "led.h"
#include "debugfs.h"
-void ieee80211_configure_filter(struct ieee80211_local *local)
+static void
+__ieee80211_configure_filter(struct ieee80211_local *local,
+ int radio_idx)
{
- u64 mc;
+ struct ieee80211_radio_filter *filter;
unsigned int changed_flags;
unsigned int new_flags = 0;
+ u32 monitors;
+ u64 mc;
- if (atomic_read(&local->iff_allmultis))
+ if (radio_idx >= 0) {
+ filter = &local->radio_data[radio_idx].filter;
+ monitors = local->radio_data[radio_idx].monitors;
+ } else {
+ filter = &local->filter;
+ monitors = local->monitors;
+ }
+
+ if (atomic_read(&filter->iff_allmultis))
new_flags |= FIF_ALLMULTI;
- if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ if (monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) ||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning))
new_flags |= FIF_BCN_PRBRESP_PROMISC;
- if (local->fif_probe_req || local->probe_req_reg)
+ if (filter->fif_probe_req || local->probe_req_reg)
new_flags |= FIF_PROBE_REQ;
- if (local->fif_fcsfail)
+ if (filter->fif_fcsfail)
new_flags |= FIF_FCSFAIL;
- if (local->fif_plcpfail)
+ if (filter->fif_plcpfail)
new_flags |= FIF_PLCPFAIL;
- if (local->fif_control)
+ if (filter->fif_control)
new_flags |= FIF_CONTROL;
- if (local->fif_other_bss)
+ if (filter->fif_other_bss)
new_flags |= FIF_OTHER_BSS;
- if (local->fif_pspoll)
+ if (filter->fif_pspoll)
new_flags |= FIF_PSPOLL;
if (local->rx_mcast_action_reg)
new_flags |= FIF_MCAST_ACTION;
spin_lock_bh(&local->filter_lock);
- changed_flags = local->filter_flags ^ new_flags;
+ changed_flags = filter->filter_flags ^ new_flags;
- mc = drv_prepare_multicast(local, &local->mc_list);
+ if (radio_idx < 0)
+ mc = drv_prepare_multicast(local, &local->mc_list);
spin_unlock_bh(&local->filter_lock);
- /* be a bit nasty */
- new_flags |= (1<<31);
- drv_configure_filter(local, changed_flags, &new_flags, mc);
+ if (radio_idx < 0) {
+ /* be a bit nasty */
+ new_flags |= (1<<31);
+
+ drv_configure_filter(local, changed_flags, &new_flags, mc);
- WARN_ON(new_flags & (1<<31));
+ WARN_ON(new_flags & (1<<31));
+ } else {
+ drv_config_radio_filter(local, radio_idx, changed_flags, &new_flags);
+ }
+
+ filter->filter_flags = new_flags & ~(1<<31);
+}
+
+void ieee80211_configure_filter(struct ieee80211_local *local)
+{
+ int i;
- local->filter_flags = new_flags & ~(1<<31);
+ for (i = -1; i < local->hw.wiphy->n_radio; i++)
+ __ieee80211_configure_filter(local, i);
}
static void ieee80211_reconfig_filter(struct wiphy *wiphy,
@@ -1170,6 +1170,29 @@ void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
}
+static void
+__ieee80211_mesh_set_filter(struct ieee80211_radio_filter *filter, int ofs)
+{
+ filter->fif_other_bss += ofs;
+
+ /* mesh ifaces must set allmulti to forward mcast traffic */
+ atomic_add(ofs, &filter->iff_allmultis);
+}
+
+static void
+ieee80211_mesh_set_filter(struct ieee80211_sub_if_data *sdata, int ofs)
+{
+ struct ieee80211_local *local = sdata->local;
+ int i;
+
+ __ieee80211_mesh_set_filter(&local->filter, ofs);
+ for (i = 0; i < local->hw.wiphy->n_radio; i++)
+ if (sdata->wdev.radio_mask & BIT(i))
+ __ieee80211_mesh_set_filter(&local->radio_data[i].filter,
+ ofs);
+ ieee80211_configure_filter(local);
+}
+
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
@@ -1181,11 +1204,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
BSS_CHANGED_BEACON_INT |
BSS_CHANGED_MCAST_RATE;
- local->fif_other_bss++;
- /* mesh ifaces must set allmulti to forward mcast traffic */
- atomic_inc(&local->iff_allmultis);
- ieee80211_configure_filter(local);
-
+ ieee80211_mesh_set_filter(sdata, 1);
ifmsh->mesh_cc_id = 0; /* Disabled */
/* register sync ops from extensible synchronization framework */
ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
@@ -1249,9 +1268,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
ifmsh->wrkq_flags = 0;
memset(ifmsh->mbss_changed, 0, sizeof(ifmsh->mbss_changed));
- local->fif_other_bss--;
- atomic_dec(&local->iff_allmultis);
- ieee80211_configure_filter(local);
+ ieee80211_mesh_set_filter(sdata, -1);
}
static void ieee80211_mesh_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
@@ -610,6 +610,34 @@ TRACE_EVENT(drv_configure_filter,
)
);
+TRACE_EVENT(drv_config_radio_filter,
+ TP_PROTO(struct ieee80211_local *local,
+ unsigned int radio,
+ unsigned int changed_flags,
+ unsigned int *total_flags),
+
+ TP_ARGS(local, radio, changed_flags, total_flags),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(unsigned int, radio)
+ __field(unsigned int, changed)
+ __field(unsigned int, total)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->radio = radio;
+ __entry->changed = changed_flags;
+ __entry->total = *total_flags;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " radio:%d, changed:%#x total:%#x",
+ LOCAL_PR_ARG, __entry->radio, __entry->changed, __entry->total
+ )
+);
+
TRACE_EVENT(drv_config_iface_filter,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
This allows drivers to improve filtering of unwanted packets when using different combinations of filter/monitor settings on radios. Signed-off-by: Felix Fietkau <nbd@nbd.name> --- include/net/mac80211.h | 8 +++- net/mac80211/driver-ops.h | 18 +++++++- net/mac80211/ieee80211_i.h | 21 ++++++--- net/mac80211/iface.c | 95 ++++++++++++++++++++++++--------------- net/mac80211/main.c | 60 +++++++++++++++++-------- net/mac80211/mesh.c | 33 ++++++++++---- net/mac80211/trace.h | 28 +++++++++++- 7 files changed, 196 insertions(+), 67 deletions(-)