Skip to content

Commit

Permalink
mac80211: add beacon configuration via cfg80211
Browse files Browse the repository at this point in the history
This patch implements the cfg80211 hooks for configuring beaconing
on an access point interface in mac80211. While doing so, it fixes
a number of races that could badly crash the machine when the
beacon is changed while being requested by the driver.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and David S. Miller committed Jan 28, 2008
1 parent 51fb61e commit 5dfdaf5
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 62 deletions.
156 changes: 156 additions & 0 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <net/net_namespace.h>
#include <linux/rcupdate.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"
#include "cfg.h"
Expand Down Expand Up @@ -294,6 +295,158 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
return 0;
}

/*
* This handles both adding a beacon and setting new beacon info
*/
static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
struct beacon_parameters *params)
{
struct beacon_data *new, *old;
int new_head_len, new_tail_len;
int size;
int err = -EINVAL;

old = sdata->u.ap.beacon;

/* head must not be zero-length */
if (params->head && !params->head_len)
return -EINVAL;

/*
* This is a kludge. beacon interval should really be part
* of the beacon information.
*/
if (params->interval) {
sdata->local->hw.conf.beacon_int = params->interval;
if (ieee80211_hw_config(sdata->local))
return -EINVAL;
/*
* We updated some parameter so if below bails out
* it's not an error.
*/
err = 0;
}

/* Need to have a beacon head if we don't have one yet */
if (!params->head && !old)
return err;

/* sorry, no way to start beaconing without dtim period */
if (!params->dtim_period && !old)
return err;

/* new or old head? */
if (params->head)
new_head_len = params->head_len;
else
new_head_len = old->head_len;

/* new or old tail? */
if (params->tail || !old)
/* params->tail_len will be zero for !params->tail */
new_tail_len = params->tail_len;
else
new_tail_len = old->tail_len;

size = sizeof(*new) + new_head_len + new_tail_len;

new = kzalloc(size, GFP_KERNEL);
if (!new)
return -ENOMEM;

/* start filling the new info now */

/* new or old dtim period? */
if (params->dtim_period)
new->dtim_period = params->dtim_period;
else
new->dtim_period = old->dtim_period;

/*
* pointers go into the block we allocated,
* memory is | beacon_data | head | tail |
*/
new->head = ((u8 *) new) + sizeof(*new);
new->tail = new->head + new_head_len;
new->head_len = new_head_len;
new->tail_len = new_tail_len;

/* copy in head */
if (params->head)
memcpy(new->head, params->head, new_head_len);
else
memcpy(new->head, old->head, new_head_len);

/* copy in optional tail */
if (params->tail)
memcpy(new->tail, params->tail, new_tail_len);
else
if (old)
memcpy(new->tail, old->tail, new_tail_len);

rcu_assign_pointer(sdata->u.ap.beacon, new);

synchronize_rcu();

kfree(old);

return ieee80211_if_config_beacon(sdata->dev);
}

static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct beacon_data *old;

if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
return -EINVAL;

old = sdata->u.ap.beacon;

if (old)
return -EALREADY;

return ieee80211_config_beacon(sdata, params);
}

static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct beacon_data *old;

if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
return -EINVAL;

old = sdata->u.ap.beacon;

if (!old)
return -ENOENT;

return ieee80211_config_beacon(sdata, params);
}

static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct beacon_data *old;

if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
return -EINVAL;

old = sdata->u.ap.beacon;

if (!old)
return -ENOENT;

rcu_assign_pointer(sdata->u.ap.beacon, NULL);
synchronize_rcu();
kfree(old);

return ieee80211_if_config_beacon(dev);
}

struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
Expand All @@ -302,5 +455,8 @@ struct cfg80211_ops mac80211_config_ops = {
.del_key = ieee80211_del_key,
.get_key = ieee80211_get_key,
.set_default_key = ieee80211_config_default_key,
.add_beacon = ieee80211_add_beacon,
.set_beacon = ieee80211_set_beacon,
.del_beacon = ieee80211_del_beacon,
.get_station = ieee80211_get_station,
};
27 changes: 0 additions & 27 deletions net/mac80211/debugfs_netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ __IEEE80211_IF_FILE(flags);

/* AP attributes */
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
IEEE80211_IF_FILE(dtim_period, u.ap.dtim_period, DEC);
IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
IEEE80211_IF_FILE(num_beacons, u.ap.num_beacons, DEC);
IEEE80211_IF_FILE(force_unicast_rateidx, u.ap.force_unicast_rateidx, DEC);
Expand All @@ -138,26 +137,6 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
}
__IEEE80211_IF_FILE(num_buffered_multicast);

static ssize_t ieee80211_if_fmt_beacon_head_len(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
if (sdata->u.ap.beacon_head)
return scnprintf(buf, buflen, "%d\n",
sdata->u.ap.beacon_head_len);
return scnprintf(buf, buflen, "\n");
}
__IEEE80211_IF_FILE(beacon_head_len);

static ssize_t ieee80211_if_fmt_beacon_tail_len(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
if (sdata->u.ap.beacon_tail)
return scnprintf(buf, buflen, "%d\n",
sdata->u.ap.beacon_tail_len);
return scnprintf(buf, buflen, "\n");
}
__IEEE80211_IF_FILE(beacon_tail_len);

/* WDS attributes */
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);

Expand Down Expand Up @@ -192,14 +171,11 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(drop_unencrypted, ap);
DEBUGFS_ADD(ieee802_1x_pac, ap);
DEBUGFS_ADD(num_sta_ps, ap);
DEBUGFS_ADD(dtim_period, ap);
DEBUGFS_ADD(dtim_count, ap);
DEBUGFS_ADD(num_beacons, ap);
DEBUGFS_ADD(force_unicast_rateidx, ap);
DEBUGFS_ADD(max_ratectrl_rateidx, ap);
DEBUGFS_ADD(num_buffered_multicast, ap);
DEBUGFS_ADD(beacon_head_len, ap);
DEBUGFS_ADD(beacon_tail_len, ap);
}

static void add_wds_files(struct ieee80211_sub_if_data *sdata)
Expand Down Expand Up @@ -281,14 +257,11 @@ static void del_ap_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_DEL(drop_unencrypted, ap);
DEBUGFS_DEL(ieee802_1x_pac, ap);
DEBUGFS_DEL(num_sta_ps, ap);
DEBUGFS_DEL(dtim_period, ap);
DEBUGFS_DEL(dtim_count, ap);
DEBUGFS_DEL(num_beacons, ap);
DEBUGFS_DEL(force_unicast_rateidx, ap);
DEBUGFS_DEL(max_ratectrl_rateidx, ap);
DEBUGFS_DEL(num_buffered_multicast, ap);
DEBUGFS_DEL(beacon_head_len, ap);
DEBUGFS_DEL(beacon_tail_len, ap);
}

static void del_wds_files(struct ieee80211_sub_if_data *sdata)
Expand Down
9 changes: 8 additions & 1 deletion net/mac80211/ieee80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,17 @@ static int ieee80211_stop(struct net_device *dev)

dev_mc_unsync(local->mdev, dev);

/* down all dependent devices, that is VLANs */
/* APs need special treatment */
if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmp;
struct beacon_data *old_beacon = sdata->u.ap.beacon;

/* remove beacon */
rcu_assign_pointer(sdata->u.ap.beacon, NULL);
synchronize_rcu();
kfree(old_beacon);

/* down all dependent devices, that is VLANs */
list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
u.vlan.list)
dev_close(vlan->dev);
Expand Down
14 changes: 8 additions & 6 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,14 @@ typedef ieee80211_txrx_result (*ieee80211_tx_handler)
typedef ieee80211_txrx_result (*ieee80211_rx_handler)
(struct ieee80211_txrx_data *rx);

struct beacon_data {
u8 *head, *tail;
int head_len, tail_len;
int dtim_period;
};

struct ieee80211_if_ap {
u8 *beacon_head, *beacon_tail;
int beacon_head_len, beacon_tail_len;
struct beacon_data *beacon;

struct list_head vlans;

Expand All @@ -205,7 +210,7 @@ struct ieee80211_if_ap {
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
atomic_t num_sta_ps; /* number of stations in PS mode */
struct sk_buff_head ps_bc_buf;
int dtim_period, dtim_count;
int dtim_count;
int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
int max_ratectrl_rateidx; /* max TX rateidx for rate control */
int num_beacons; /* number of TXed beacon frames for this BSS */
Expand Down Expand Up @@ -360,14 +365,11 @@ struct ieee80211_sub_if_data {
struct dentry *drop_unencrypted;
struct dentry *ieee802_1x_pac;
struct dentry *num_sta_ps;
struct dentry *dtim_period;
struct dentry *dtim_count;
struct dentry *num_beacons;
struct dentry *force_unicast_rateidx;
struct dentry *max_ratectrl_rateidx;
struct dentry *num_buffered_multicast;
struct dentry *beacon_head_len;
struct dentry *beacon_tail_len;
} ap;
struct {
struct dentry *channel_use;
Expand Down
4 changes: 1 addition & 3 deletions net/mac80211/ieee80211_iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
sdata->u.vlan.ap = NULL;
break;
case IEEE80211_IF_TYPE_AP:
sdata->u.ap.dtim_period = 2;
sdata->u.ap.force_unicast_rateidx = -1;
sdata->u.ap.max_ratectrl_rateidx = -1;
skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
Expand Down Expand Up @@ -207,8 +206,7 @@ void ieee80211_if_reinit(struct net_device *dev)
}
}

kfree(sdata->u.ap.beacon_head);
kfree(sdata->u.ap.beacon_tail);
kfree(sdata->u.ap.beacon);

while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
local->total_ps_buffered--;
Expand Down
Loading

0 comments on commit 5dfdaf5

Please sign in to comment.