Skip to content

Commit

Permalink
mac80211: add explicit monitor interface if needed
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Apr 11, 2012
1 parent 3edaf3e commit 4b6f1dd
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 5 deletions.
3 changes: 2 additions & 1 deletion drivers/net/wireless/mac80211_hwsim.c
Original file line number Diff line number Diff line change
Expand Up @@ -1789,7 +1789,8 @@ static int __init init_mac80211_hwsim(void)
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_AMPDU_AGGREGATION;
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_WANT_MONITOR_VIF;

hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
Expand Down
6 changes: 5 additions & 1 deletion include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,10 @@ enum sta_notify_cmd {
* @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while
* being idle (i.e. mac80211 doesn't have to go idle-off during the
* the scan).
*
* @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of
* a virtual monitor interface when monitor interfaces are the only
* active interfaces.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
Expand All @@ -1191,7 +1195,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11,
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
/* reuse bit 14 */
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
Expand Down
3 changes: 2 additions & 1 deletion net/mac80211/driver-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ static inline int drv_add_interface(struct ieee80211_local *local,
might_sleep();

if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
sdata->vif.type == NL80211_IFTYPE_MONITOR))
(sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))))
return -EINVAL;

trace_drv_add_interface(local, sdata);
Expand Down
3 changes: 3 additions & 0 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,9 @@ struct ieee80211_local {
struct net_device napi_dev;

struct napi_struct napi;

/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
};

static inline struct ieee80211_sub_if_data *
Expand Down
65 changes: 65 additions & 0 deletions net/mac80211/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,59 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
#undef ADJUST
}

static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
int ret;

if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
return 0;

if (local->monitor_sdata)
return 0;

sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
if (!sdata)
return -ENOMEM;

/* set up data */
sdata->local = local;
sdata->vif.type = NL80211_IFTYPE_MONITOR;
snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
wiphy_name(local->hw.wiphy));

ret = drv_add_interface(local, sdata);
if (WARN_ON(ret)) {
/* ok .. stupid driver, it asked for this! */
kfree(sdata);
return ret;
}

rcu_assign_pointer(local->monitor_sdata, sdata);

return 0;
}

static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;

if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
return;

sdata = rtnl_dereference(local->monitor_sdata);

if (!sdata)
return;

rcu_assign_pointer(local->monitor_sdata, NULL);
synchronize_net();

drv_remove_interface(local, sdata);

kfree(sdata);
}

/*
* 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
Expand Down Expand Up @@ -266,6 +319,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break;
}

if (local->monitors == 0 && local->open_count == 0) {
res = ieee80211_add_virtual_monitor(local);
if (res)
goto err_stop;
}

/* must be before the call to ieee80211_configure_filter */
local->monitors++;
if (local->monitors == 1) {
Expand All @@ -280,6 +339,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break;
default:
if (coming_up) {
ieee80211_del_virtual_monitor(local);

res = drv_add_interface(local, sdata);
if (res)
goto err_stop;
Expand Down Expand Up @@ -511,6 +572,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
if (local->monitors == 0) {
local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
ieee80211_del_virtual_monitor(local);
}

ieee80211_adjust_monitor_flags(sdata, -1);
Expand Down Expand Up @@ -584,6 +646,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
}
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);

if (local->monitors == local->open_count && local->monitors > 0)
ieee80211_add_virtual_monitor(local);
}

static int ieee80211_stop(struct net_device *dev)
Expand Down
4 changes: 4 additions & 0 deletions net/mac80211/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
drv_remove_interface(local, sdata);
}

sdata = rtnl_dereference(local->monitor_sdata);
if (sdata)
drv_remove_interface(local, sdata);

/* stop hardware - this must stop RX */
if (local->open_count)
ieee80211_stop_device(local);
Expand Down
7 changes: 5 additions & 2 deletions net/mac80211/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1283,8 +1283,11 @@ static bool __ieee80211_tx(struct ieee80211_local *local,

switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR:
sdata = NULL;
vif = NULL;
sdata = rcu_dereference(local->monitor_sdata);
if (sdata)
vif = &sdata->vif;
else
vif = NULL;
break;
case NL80211_IFTYPE_AP_VLAN:
sdata = container_of(sdata->bss,
Expand Down
10 changes: 10 additions & 0 deletions net/mac80211/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,16 @@ int ieee80211_reconfig(struct ieee80211_local *local)
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);

/* add interfaces */
sdata = rtnl_dereference(local->monitor_sdata);
if (sdata) {
res = drv_add_interface(local, sdata);
if (WARN_ON(res)) {
rcu_assign_pointer(local->monitor_sdata, NULL);
synchronize_net();
kfree(sdata);
}
}

list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
Expand Down

0 comments on commit 4b6f1dd

Please sign in to comment.