Commit 85416a4f authored by Christian Lamparter's avatar Christian Lamparter Committed by John W. Linville

mac80211: fix rx monitor filter refcounters

This patch fixes an refcounting bug. Previously it
was possible to corrupt the per-device recv. filter
and monitor management counters when:
	iw dev wlanX set monitor [new flags]
was issued on an active monitor interface.
Acked-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarChristian Lamparter <chunkeey@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5a254ffe
...@@ -68,8 +68,36 @@ static int ieee80211_change_iface(struct wiphy *wiphy, ...@@ -68,8 +68,36 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
params && params->use_4addr >= 0) params && params->use_4addr >= 0)
sdata->u.mgd.use_4addr = params->use_4addr; sdata->u.mgd.use_4addr = params->use_4addr;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
sdata->u.mntr_flags = *flags; struct ieee80211_local *local = sdata->local;
if (ieee80211_sdata_running(sdata)) {
/*
* Prohibit MONITOR_FLAG_COOK_FRAMES to be
* changed while the interface is up.
* Else we would need to add a lot of cruft
* to update everything:
* cooked_mntrs, monitor and all fif_* counters
* reconfigure hardware
*/
if ((*flags & MONITOR_FLAG_COOK_FRAMES) !=
(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
return -EBUSY;
ieee80211_adjust_monitor_flags(sdata, -1);
sdata->u.mntr_flags = *flags;
ieee80211_adjust_monitor_flags(sdata, 1);
ieee80211_configure_filter(local);
} else {
/*
* Because the interface is down, ieee80211_do_stop
* and ieee80211_do_open take care of "everything"
* mentioned in the comment above.
*/
sdata->u.mntr_flags = *flags;
}
}
return 0; return 0;
} }
......
...@@ -1132,6 +1132,8 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata); ...@@ -1132,6 +1132,8 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
void ieee80211_remove_interfaces(struct ieee80211_local *local); void ieee80211_remove_interfaces(struct ieee80211_local *local);
u32 __ieee80211_recalc_idle(struct ieee80211_local *local); u32 __ieee80211_recalc_idle(struct ieee80211_local *local);
void ieee80211_recalc_idle(struct ieee80211_local *local); void ieee80211_recalc_idle(struct ieee80211_local *local);
void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
const int offset);
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{ {
......
...@@ -148,6 +148,26 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, ...@@ -148,6 +148,26 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
return 0; return 0;
} }
void ieee80211_adjust_monitor_flags(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; \
} while (0)
ADJUST(FCSFAIL, fcsfail);
ADJUST(PLCPFAIL, plcpfail);
ADJUST(CONTROL, control);
ADJUST(CONTROL, pspoll);
ADJUST(OTHER_BSS, other_bss);
#undef ADJUST
}
/* /*
* NOTE: Be very careful when changing this function, it must NOT return * NOTE: Be very careful when changing this function, it must NOT return
* an error on interface type changes that have been pre-checked, so most * an error on interface type changes that have been pre-checked, so most
...@@ -240,17 +260,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) ...@@ -240,17 +260,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
} }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL) ieee80211_adjust_monitor_flags(sdata, 1);
local->fif_fcsfail++;
if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
local->fif_plcpfail++;
if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) {
local->fif_control++;
local->fif_pspoll++;
}
if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
local->fif_other_bss++;
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
netif_carrier_on(dev); netif_carrier_on(dev);
...@@ -477,17 +487,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -477,17 +487,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
} }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL) ieee80211_adjust_monitor_flags(sdata, -1);
local->fif_fcsfail--;
if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
local->fif_plcpfail--;
if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) {
local->fif_pspoll--;
local->fif_control--;
}
if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
local->fif_other_bss--;
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
break; break;
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment