diff options
Diffstat (limited to 'package/mac80211/patches/021-mac80211-beacon-via-nl80211.patch')
-rw-r--r-- | package/mac80211/patches/021-mac80211-beacon-via-nl80211.patch | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/package/mac80211/patches/021-mac80211-beacon-via-nl80211.patch b/package/mac80211/patches/021-mac80211-beacon-via-nl80211.patch new file mode 100644 index 0000000000..702c68f98f --- /dev/null +++ b/package/mac80211/patches/021-mac80211-beacon-via-nl80211.patch @@ -0,0 +1,484 @@ +Subject: mac80211: add beacon configuration via cfg80211 + +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> + +--- +The dtim_count field should possibly also be part of the beacon +structure, but the possible race there doesn't really matter, +worst thing is that one beacon will be sent with a wrong dtim +count if (and only if) userspace changes the dtim period during +operation. + + net/mac80211/cfg.c | 156 +++++++++++++++++++++++++++++++++++++++++ + net/mac80211/debugfs_netdev.c | 27 ------- + net/mac80211/ieee80211_i.h | 14 ++- + net/mac80211/ieee80211_iface.c | 4 - + net/mac80211/tx.c | 63 ++++++++++------ + 5 files changed, 204 insertions(+), 60 deletions(-) + +Index: mac80211/net/mac80211/cfg.c +=================================================================== +--- mac80211.orig/net/mac80211/cfg.c 2007-11-11 15:17:12.837164411 +0100 ++++ mac80211/net/mac80211/cfg.c 2007-11-11 15:18:36.853952256 +0100 +@@ -9,6 +9,7 @@ + #include <linux/ieee80211.h> + #include <linux/nl80211.h> + #include <linux/rtnetlink.h> ++#include <linux/rcupdate.h> + #include <net/cfg80211.h> + #include "ieee80211_i.h" + #include "cfg.h" +@@ -274,6 +275,158 @@ + 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->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->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->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, +@@ -282,4 +435,7 @@ + .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, + }; +Index: mac80211/net/mac80211/debugfs_netdev.c +=================================================================== +--- mac80211.orig/net/mac80211/debugfs_netdev.c 2007-10-14 00:42:30.054156000 +0200 ++++ mac80211/net/mac80211/debugfs_netdev.c 2007-11-11 15:18:11.852527505 +0100 +@@ -124,7 +124,6 @@ + + /* 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); +@@ -138,26 +137,6 @@ + } + __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); + +@@ -194,14 +173,11 @@ + DEBUGFS_ADD(eapol, ap); + DEBUGFS_ADD(ieee8021_x, 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) +@@ -287,14 +263,11 @@ + DEBUGFS_DEL(eapol, ap); + DEBUGFS_DEL(ieee8021_x, 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) +Index: mac80211/net/mac80211/ieee80211_i.h +=================================================================== +--- mac80211.orig/net/mac80211/ieee80211_i.h 2007-11-11 15:15:53.792659922 +0100 ++++ mac80211/net/mac80211/ieee80211_i.h 2007-11-11 15:18:11.864528190 +0100 +@@ -190,9 +190,14 @@ + 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; + +@@ -205,7 +210,7 @@ + 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 */ +@@ -361,14 +366,11 @@ + struct dentry *eapol; + struct dentry *ieee8021_x; + 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; +Index: mac80211/net/mac80211/ieee80211_iface.c +=================================================================== +--- mac80211.orig/net/mac80211/ieee80211_iface.c 2007-11-11 15:15:53.796660158 +0100 ++++ mac80211/net/mac80211/ieee80211_iface.c 2007-11-11 15:18:11.868528415 +0100 +@@ -187,7 +187,6 @@ + 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); +@@ -271,8 +270,7 @@ + } + } + +- 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--; +Index: mac80211/net/mac80211/tx.c +=================================================================== +--- mac80211.orig/net/mac80211/tx.c 2007-11-11 15:15:53.804660611 +0100 ++++ mac80211/net/mac80211/tx.c 2007-11-11 15:18:11.868528415 +0100 +@@ -1656,7 +1656,8 @@ + + static void ieee80211_beacon_add_tim(struct ieee80211_local *local, + struct ieee80211_if_ap *bss, +- struct sk_buff *skb) ++ struct sk_buff *skb, ++ struct beacon_data *beacon) + { + u8 *pos, *tim; + int aid0 = 0; +@@ -1672,7 +1673,7 @@ + IEEE80211_MAX_AID+1); + + if (bss->dtim_count == 0) +- bss->dtim_count = bss->dtim_period - 1; ++ bss->dtim_count = beacon->dtim_period - 1; + else + bss->dtim_count--; + +@@ -1680,7 +1681,7 @@ + *pos++ = WLAN_EID_TIM; + *pos++ = 4; + *pos++ = bss->dtim_count; +- *pos++ = bss->dtim_period; ++ *pos++ = beacon->dtim_period; + + if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) + aid0 = 1; +@@ -1728,8 +1729,9 @@ + struct ieee80211_if_ap *ap = NULL; + struct ieee80211_rate *rate; + struct rate_control_extra extra; +- u8 *b_head, *b_tail; +- int bh_len, bt_len; ++ struct beacon_data *beacon; ++ ++ rcu_read_lock(); + + bdev = dev_get_by_index(if_id); + if (bdev) { +@@ -1738,37 +1740,35 @@ + dev_put(bdev); + } + +- if (!ap || sdata->type != IEEE80211_IF_TYPE_AP || +- !ap->beacon_head) { ++ beacon = rcu_dereference(ap->beacon); ++ ++ if (!ap || sdata->type != IEEE80211_IF_TYPE_AP || !beacon) { + #ifdef CONFIG_MAC80211_VERBOSE_DEBUG + if (net_ratelimit()) + printk(KERN_DEBUG "no beacon data avail for idx=%d " + "(%s)\n", if_id, bdev ? bdev->name : "N/A"); + #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ +- return NULL; ++ skb = NULL; ++ goto out; + } + +- /* Assume we are generating the normal beacon locally */ +- b_head = ap->beacon_head; +- b_tail = ap->beacon_tail; +- bh_len = ap->beacon_head_len; +- bt_len = ap->beacon_tail_len; +- +- skb = dev_alloc_skb(local->tx_headroom + +- bh_len + bt_len + 256 /* maximum TIM len */); ++ /* headroom, head length, tail length and maximum TIM length */ ++ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + ++ beacon->tail_len + 256); + if (!skb) +- return NULL; ++ goto out; + + skb_reserve(skb, local->tx_headroom); +- memcpy(skb_put(skb, bh_len), b_head, bh_len); ++ memcpy(skb_put(skb, beacon->head_len), beacon->head, ++ beacon->head_len); + + ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data); + +- ieee80211_beacon_add_tim(local, ap, skb); ++ ieee80211_beacon_add_tim(local, ap, skb, beacon); + +- if (b_tail) { +- memcpy(skb_put(skb, bt_len), b_tail, bt_len); +- } ++ if (beacon->tail) ++ memcpy(skb_put(skb, beacon->tail_len), beacon->tail, ++ beacon->tail_len); + + if (control) { + memset(&extra, 0, sizeof(extra)); +@@ -1781,7 +1781,8 @@ + "found\n", wiphy_name(local->hw.wiphy)); + } + dev_kfree_skb(skb); +- return NULL; ++ skb = NULL; ++ goto out; + } + + control->tx_rate = +@@ -1796,6 +1797,9 @@ + } + + ap->num_beacons++; ++ ++ out: ++ rcu_read_unlock(); + return skb; + } + EXPORT_SYMBOL(ieee80211_beacon_get); +@@ -1844,6 +1848,7 @@ + struct net_device *bdev; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_ap *bss = NULL; ++ struct beacon_data *beacon; + + bdev = dev_get_by_index(if_id); + if (bdev) { +@@ -1851,9 +1856,19 @@ + bss = &sdata->u.ap; + dev_put(bdev); + } +- if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head) ++ ++ if (!bss) + return NULL; + ++ rcu_read_lock(); ++ beacon = rcu_dereference(bss->beacon); ++ ++ if (sdata->type != IEEE80211_IF_TYPE_AP || !beacon || !beacon->head) { ++ rcu_read_unlock(); ++ return NULL; ++ } ++ rcu_read_unlock(); ++ + if (bss->dtim_count != 0) + return NULL; /* send buffered bc/mc only after DTIM beacon */ + memset(control, 0, sizeof(*control)); |