diff options
author | Christian Lamparter <chunkeey@googlemail.com> | 2010-10-02 07:17:07 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-10-05 13:35:21 -0400 |
commit | 85416a4fa193754ef36e12b20bb02fe661cb7f17 (patch) | |
tree | 45228e888bd76a5f1251c55ea28377ddedfe2914 /net | |
parent | 5a254ffe3ffdfa84fe076009bd8e88da412180d2 (diff) |
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: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 32 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/iface.c | 44 |
3 files changed, 54 insertions, 24 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c981604b71e6..9e63fc28f859 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -68,8 +68,36 @@ static int ieee80211_change_iface(struct wiphy *wiphy, | |||
68 | params && params->use_4addr >= 0) | 68 | params && params->use_4addr >= 0) |
69 | sdata->u.mgd.use_4addr = params->use_4addr; | 69 | sdata->u.mgd.use_4addr = params->use_4addr; |
70 | 70 | ||
71 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) | 71 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) { |
72 | sdata->u.mntr_flags = *flags; | 72 | struct ieee80211_local *local = sdata->local; |
73 | |||
74 | if (ieee80211_sdata_running(sdata)) { | ||
75 | /* | ||
76 | * Prohibit MONITOR_FLAG_COOK_FRAMES to be | ||
77 | * changed while the interface is up. | ||
78 | * Else we would need to add a lot of cruft | ||
79 | * to update everything: | ||
80 | * cooked_mntrs, monitor and all fif_* counters | ||
81 | * reconfigure hardware | ||
82 | */ | ||
83 | if ((*flags & MONITOR_FLAG_COOK_FRAMES) != | ||
84 | (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) | ||
85 | return -EBUSY; | ||
86 | |||
87 | ieee80211_adjust_monitor_flags(sdata, -1); | ||
88 | sdata->u.mntr_flags = *flags; | ||
89 | ieee80211_adjust_monitor_flags(sdata, 1); | ||
90 | |||
91 | ieee80211_configure_filter(local); | ||
92 | } else { | ||
93 | /* | ||
94 | * Because the interface is down, ieee80211_do_stop | ||
95 | * and ieee80211_do_open take care of "everything" | ||
96 | * mentioned in the comment above. | ||
97 | */ | ||
98 | sdata->u.mntr_flags = *flags; | ||
99 | } | ||
100 | } | ||
73 | 101 | ||
74 | return 0; | 102 | return 0; |
75 | } | 103 | } |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 945fbf29719d..f6a6d78efcf0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1132,6 +1132,8 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata); | |||
1132 | void ieee80211_remove_interfaces(struct ieee80211_local *local); | 1132 | void ieee80211_remove_interfaces(struct ieee80211_local *local); |
1133 | u32 __ieee80211_recalc_idle(struct ieee80211_local *local); | 1133 | u32 __ieee80211_recalc_idle(struct ieee80211_local *local); |
1134 | void ieee80211_recalc_idle(struct ieee80211_local *local); | 1134 | void ieee80211_recalc_idle(struct ieee80211_local *local); |
1135 | void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, | ||
1136 | const int offset); | ||
1135 | 1137 | ||
1136 | static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) | 1138 | static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) |
1137 | { | 1139 | { |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 66785739dad3..1300e8859ea7 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -148,6 +148,26 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, | |||
148 | return 0; | 148 | return 0; |
149 | } | 149 | } |
150 | 150 | ||
151 | void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, | ||
152 | const int offset) | ||
153 | { | ||
154 | struct ieee80211_local *local = sdata->local; | ||
155 | u32 flags = sdata->u.mntr_flags; | ||
156 | |||
157 | #define ADJUST(_f, _s) do { \ | ||
158 | if (flags & MONITOR_FLAG_##_f) \ | ||
159 | local->fif_##_s += offset; \ | ||
160 | } while (0) | ||
161 | |||
162 | ADJUST(FCSFAIL, fcsfail); | ||
163 | ADJUST(PLCPFAIL, plcpfail); | ||
164 | ADJUST(CONTROL, control); | ||
165 | ADJUST(CONTROL, pspoll); | ||
166 | ADJUST(OTHER_BSS, other_bss); | ||
167 | |||
168 | #undef ADJUST | ||
169 | } | ||
170 | |||
151 | /* | 171 | /* |
152 | * NOTE: Be very careful when changing this function, it must NOT return | 172 | * NOTE: Be very careful when changing this function, it must NOT return |
153 | * an error on interface type changes that have been pre-checked, so most | 173 | * 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 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; | 260 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; |
241 | } | 261 | } |
242 | 262 | ||
243 | if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL) | 263 | ieee80211_adjust_monitor_flags(sdata, 1); |
244 | local->fif_fcsfail++; | ||
245 | if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL) | ||
246 | local->fif_plcpfail++; | ||
247 | if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) { | ||
248 | local->fif_control++; | ||
249 | local->fif_pspoll++; | ||
250 | } | ||
251 | if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) | ||
252 | local->fif_other_bss++; | ||
253 | |||
254 | ieee80211_configure_filter(local); | 264 | ieee80211_configure_filter(local); |
255 | 265 | ||
256 | netif_carrier_on(dev); | 266 | netif_carrier_on(dev); |
@@ -477,17 +487,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
477 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; | 487 | hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; |
478 | } | 488 | } |
479 | 489 | ||
480 | if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL) | 490 | ieee80211_adjust_monitor_flags(sdata, -1); |
481 | local->fif_fcsfail--; | ||
482 | if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL) | ||
483 | local->fif_plcpfail--; | ||
484 | if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) { | ||
485 | local->fif_pspoll--; | ||
486 | local->fif_control--; | ||
487 | } | ||
488 | if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) | ||
489 | local->fif_other_bss--; | ||
490 | |||
491 | ieee80211_configure_filter(local); | 491 | ieee80211_configure_filter(local); |
492 | break; | 492 | break; |
493 | case NL80211_IFTYPE_MESH_POINT: | 493 | case NL80211_IFTYPE_MESH_POINT: |