aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-04-03 08:35:57 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-11 16:23:49 -0400
commit4b6f1dd6a6faf4ed8d209bbd548e78b15e55aee8 (patch)
tree7d79667ca414d70b99cb82a646d1a3c2cc466670 /net/mac80211/iface.c
parent3edaf3e61fda3aa9ff8d38445bf92f2bec23bf63 (diff)
mac80211: add explicit monitor interface if needed
The queue mapping redesign that I'm planning to do will break pure injection unless we handle monitor interfaces explicitly. One possible option would be to have the driver tell mac80211 about monitor mode queues etc., but that would duplicate the API since we already need to have queue assignments handled per virtual interface. So in order to solve this, have a virtual monitor interface that is added whenever all active vifs are monitors. We could also use the state of one of the monitor interfaces, but managing that would be complicated, so allocate separate state. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 56a38a3088d4..2b88cb278fc4 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -169,6 +169,59 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
169#undef ADJUST 169#undef ADJUST
170} 170}
171 171
172static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
173{
174 struct ieee80211_sub_if_data *sdata;
175 int ret;
176
177 if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
178 return 0;
179
180 if (local->monitor_sdata)
181 return 0;
182
183 sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
184 if (!sdata)
185 return -ENOMEM;
186
187 /* set up data */
188 sdata->local = local;
189 sdata->vif.type = NL80211_IFTYPE_MONITOR;
190 snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
191 wiphy_name(local->hw.wiphy));
192
193 ret = drv_add_interface(local, sdata);
194 if (WARN_ON(ret)) {
195 /* ok .. stupid driver, it asked for this! */
196 kfree(sdata);
197 return ret;
198 }
199
200 rcu_assign_pointer(local->monitor_sdata, sdata);
201
202 return 0;
203}
204
205static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
206{
207 struct ieee80211_sub_if_data *sdata;
208
209 if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
210 return;
211
212 sdata = rtnl_dereference(local->monitor_sdata);
213
214 if (!sdata)
215 return;
216
217 rcu_assign_pointer(local->monitor_sdata, NULL);
218 synchronize_net();
219
220 drv_remove_interface(local, sdata);
221
222 kfree(sdata);
223}
224
172/* 225/*
173 * NOTE: Be very careful when changing this function, it must NOT return 226 * NOTE: Be very careful when changing this function, it must NOT return
174 * an error on interface type changes that have been pre-checked, so most 227 * an error on interface type changes that have been pre-checked, so most
@@ -266,6 +319,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
266 break; 319 break;
267 } 320 }
268 321
322 if (local->monitors == 0 && local->open_count == 0) {
323 res = ieee80211_add_virtual_monitor(local);
324 if (res)
325 goto err_stop;
326 }
327
269 /* must be before the call to ieee80211_configure_filter */ 328 /* must be before the call to ieee80211_configure_filter */
270 local->monitors++; 329 local->monitors++;
271 if (local->monitors == 1) { 330 if (local->monitors == 1) {
@@ -280,6 +339,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
280 break; 339 break;
281 default: 340 default:
282 if (coming_up) { 341 if (coming_up) {
342 ieee80211_del_virtual_monitor(local);
343
283 res = drv_add_interface(local, sdata); 344 res = drv_add_interface(local, sdata);
284 if (res) 345 if (res)
285 goto err_stop; 346 goto err_stop;
@@ -511,6 +572,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
511 if (local->monitors == 0) { 572 if (local->monitors == 0) {
512 local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; 573 local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
513 hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; 574 hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
575 ieee80211_del_virtual_monitor(local);
514 } 576 }
515 577
516 ieee80211_adjust_monitor_flags(sdata, -1); 578 ieee80211_adjust_monitor_flags(sdata, -1);
@@ -584,6 +646,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
584 } 646 }
585 } 647 }
586 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 648 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
649
650 if (local->monitors == local->open_count && local->monitors > 0)
651 ieee80211_add_virtual_monitor(local);
587} 652}
588 653
589static int ieee80211_stop(struct net_device *dev) 654static int ieee80211_stop(struct net_device *dev)