Newer
Older
/*
* Intersil Prism2 driver with Host AP (software access point) support
* Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
* <j@w1.fi>
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
* This file is to be included into hostap.c when S/W AP functionality is
* compiled.
*
* AP: FIX:
* - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
* unauthenticated STA, send deauth. frame (8802.11: 5.5)
* - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
* from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
* - if unicast Class 3 received from unauthenticated STA, send deauth. frame
* (8802.11: 5.5)
*/
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/random.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/moduleparam.h>
#include "hostap_wlan.h"
#include "hostap.h"
#include "hostap_ap.h"
static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
DEF_INTS };
module_param_array(other_ap_policy, int, NULL, 0444);
MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
DEF_INTS };
module_param_array(ap_max_inactivity, int, NULL, 0444);
MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
"inactivity");
static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
module_param_array(ap_bridge_packets, int, NULL, 0444);
MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
"stations");
static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
module_param_array(autom_ap_wds, int, NULL, 0444);
MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
"automatically");
static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
static void hostap_event_expired_sta(struct net_device *dev,
struct sta_info *sta);
static void handle_add_proc_queue(struct work_struct *work);
static void handle_wds_oper_queue(struct work_struct *work);
static void prism2_send_mgmt(struct net_device *dev,
u16 type_subtype, char *body,
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
int body_len, u8 *addr, u16 tx_cb_idx);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
#ifndef PRISM2_NO_PROCFS_DEBUG
static int ap_debug_proc_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *p = page;
struct ap_data *ap = (struct ap_data *) data;
if (off != 0) {
*eof = 1;
return 0;
}
p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ);
p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets);
p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack);
p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds);
p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs);
p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
return (p - page);
}
#endif /* PRISM2_NO_PROCFS_DEBUG */
static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
{
sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
ap->sta_hash[STA_HASH(sta->addr)] = sta;
}
static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
{
struct sta_info *s;
s = ap->sta_hash[STA_HASH(sta->addr)];
if (s == NULL) return;
if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
return;
}
while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN)
!= 0)
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
else
printk("AP: could not remove STA %pM from hash table\n",
sta->addr);
}
static void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
{
if (sta->ap && sta->local)
hostap_event_expired_sta(sta->local->dev, sta);
if (ap->proc != NULL) {
char name[20];
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
remove_proc_entry(name, ap->proc);
}
if (sta->crypt) {
sta->crypt->ops->deinit(sta->crypt->priv);
kfree(sta->crypt);
sta->crypt = NULL;
}
skb_queue_purge(&sta->tx_buf);
ap->num_sta--;
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if (sta->aid > 0)
ap->sta_aid[sta->aid - 1] = NULL;
if (!sta->ap && sta->u.sta.challenge)
kfree(sta->u.sta.challenge);
del_timer(&sta->timer);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
kfree(sta);
}
static void hostap_set_tim(local_info_t *local, int aid, int set)
{
if (local->func->set_tim)
local->func->set_tim(local->dev, aid, set);
}
static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
{
union iwreq_data wrqu;
memset(&wrqu, 0, sizeof(wrqu));
memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
wrqu.addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
}
static void hostap_event_expired_sta(struct net_device *dev,
struct sta_info *sta)
{
union iwreq_data wrqu;
memset(&wrqu, 0, sizeof(wrqu));
memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
wrqu.addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
}
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
static void ap_handle_timer(unsigned long data)
{
struct sta_info *sta = (struct sta_info *) data;
local_info_t *local;
struct ap_data *ap;
unsigned long next_time = 0;
int was_assoc;
if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
return;
}
local = sta->local;
ap = local->ap;
was_assoc = sta->flags & WLAN_STA_ASSOC;
if (atomic_read(&sta->users) != 0)
next_time = jiffies + HZ;
else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
next_time = jiffies + ap->max_inactivity;
if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
/* station activity detected; reset timeout state */
sta->timeout_next = STA_NULLFUNC;
next_time = sta->last_rx + ap->max_inactivity;
} else if (sta->timeout_next == STA_DISASSOC &&
!(sta->flags & WLAN_STA_PENDING_POLL)) {
/* STA ACKed data nullfunc frame poll */
sta->timeout_next = STA_NULLFUNC;
next_time = jiffies + ap->max_inactivity;
}
if (next_time) {
sta->timer.expires = next_time;
add_timer(&sta->timer);
return;
}
if (sta->ap)
sta->timeout_next = STA_DEAUTH;
if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
spin_lock(&ap->sta_table_lock);
ap_sta_hash_del(ap, sta);
list_del(&sta->list);
spin_unlock(&ap->sta_table_lock);
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
} else if (sta->timeout_next == STA_DISASSOC)
sta->flags &= ~WLAN_STA_ASSOC;
if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
hostap_event_expired_sta(local->dev, sta);
if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
!skb_queue_empty(&sta->tx_buf)) {
hostap_set_tim(local, sta->aid, 0);
sta->flags &= ~WLAN_STA_TIM;
}
if (sta->ap) {
if (ap->autom_ap_wds) {
PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
"connection to AP %pM\n",
local->dev->name, sta->addr);
hostap_wds_link_oper(local, sta->addr, WDS_DEL);
}
} else if (sta->timeout_next == STA_NULLFUNC) {
/* send data frame to poll STA and check whether this frame
* is ACKed */
/* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
* it is apparently not retried so TX Exc events are not
* received for it */
sta->flags |= WLAN_STA_PENDING_POLL;
prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_DATA, NULL, 0,
sta->addr, ap->tx_callback_poll);
} else {
int deauth = sta->timeout_next == STA_DEAUTH;
PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM"
"(last=%lu, jiffies=%lu)\n",
local->dev->name,
deauth ? "deauthentication" : "disassociation",
resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
(deauth ? IEEE80211_STYPE_DEAUTH :
IEEE80211_STYPE_DISASSOC),
(char *) &resp, 2, sta->addr, 0);
}
if (sta->timeout_next == STA_DEAUTH) {
if (sta->flags & WLAN_STA_PERM) {
" would have been removed, "
"but it has 'perm' flag\n",
} else
ap_free_sta(ap, sta);
return;
}
if (sta->timeout_next == STA_NULLFUNC) {
sta->timeout_next = STA_DISASSOC;
sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
} else {
sta->timeout_next = STA_DEAUTH;
sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
}
add_timer(&sta->timer);
}
void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
int resend)
{
u8 addr[ETH_ALEN];
int i;
PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
memset(addr, 0xff, ETH_ALEN);
/* deauth message sent; try to resend it few times; the message is
* broadcast, so it may be delayed until next DTIM; there is not much
* else we can do at this point since the driver is going to be shut
* down */
for (i = 0; i < 5; i++) {
prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_DEAUTH,
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
(char *) &resp, 2, addr, 0);
if (!resend || ap->num_sta <= 0)
return;
mdelay(50);
}
}
static int ap_control_proc_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *p = page;
struct ap_data *ap = (struct ap_data *) data;
char *policy_txt;
struct mac_entry *entry;
if (off != 0) {
*eof = 1;
return 0;
}
switch (ap->mac_restrictions.policy) {
case MAC_POLICY_OPEN:
policy_txt = "open";
break;
case MAC_POLICY_ALLOW:
policy_txt = "allow";
break;
case MAC_POLICY_DENY:
policy_txt = "deny";
break;
default:
policy_txt = "unknown";
break;
p += sprintf(p, "MAC policy: %s\n", policy_txt);
p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries);
p += sprintf(p, "MAC list:\n");
spin_lock_bh(&ap->mac_restrictions.lock);
list_for_each_entry(entry, &ap->mac_restrictions.mac_list, list) {
if (p - page > PAGE_SIZE - 80) {
p += sprintf(p, "All entries did not fit one page.\n");
break;
}
}
spin_unlock_bh(&ap->mac_restrictions.lock);
return (p - page);
}
int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
{
struct mac_entry *entry;
entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
if (entry == NULL)

Kumar Amit Mehta
committed
return -ENOMEM;
memcpy(entry->addr, mac, ETH_ALEN);
spin_lock_bh(&mac_restrictions->lock);
list_add_tail(&entry->list, &mac_restrictions->mac_list);
mac_restrictions->entries++;
spin_unlock_bh(&mac_restrictions->lock);
return 0;
}
int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac)
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
{
struct list_head *ptr;
struct mac_entry *entry;
spin_lock_bh(&mac_restrictions->lock);
for (ptr = mac_restrictions->mac_list.next;
ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
entry = list_entry(ptr, struct mac_entry, list);
if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
list_del(ptr);
kfree(entry);
mac_restrictions->entries--;
spin_unlock_bh(&mac_restrictions->lock);
return 0;
}
}
spin_unlock_bh(&mac_restrictions->lock);
return -1;
}
static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
u8 *mac)
{
struct mac_entry *entry;
int found = 0;
if (mac_restrictions->policy == MAC_POLICY_OPEN)
return 0;
spin_lock_bh(&mac_restrictions->lock);
list_for_each_entry(entry, &mac_restrictions->mac_list, list) {
if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
found = 1;
break;
}
}
spin_unlock_bh(&mac_restrictions->lock);
if (mac_restrictions->policy == MAC_POLICY_ALLOW)
return !found;
else
return found;
}
void ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
{
struct list_head *ptr, *n;
struct mac_entry *entry;
if (mac_restrictions->entries == 0)
return;
spin_lock_bh(&mac_restrictions->lock);
for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
ptr != &mac_restrictions->mac_list;
ptr = n, n = ptr->next) {
entry = list_entry(ptr, struct mac_entry, list);
list_del(ptr);
kfree(entry);
}
mac_restrictions->entries = 0;
spin_unlock_bh(&mac_restrictions->lock);
}
int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac)
spin_lock_bh(&ap->sta_table_lock);
sta = ap_get_sta(ap, mac);
if (sta) {
ap_sta_hash_del(ap, sta);
list_del(&sta->list);
}
spin_unlock_bh(&ap->sta_table_lock);
if (!sta)
return -EINVAL;
resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
(char *) &resp, 2, sta->addr, 0);
if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
hostap_event_expired_sta(dev, sta);
ap_free_sta(ap, sta);
return 0;
}
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
void ap_control_kickall(struct ap_data *ap)
{
struct list_head *ptr, *n;
struct sta_info *sta;
spin_lock_bh(&ap->sta_table_lock);
for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
ptr = n, n = ptr->next) {
sta = list_entry(ptr, struct sta_info, list);
ap_sta_hash_del(ap, sta);
list_del(&sta->list);
if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
hostap_event_expired_sta(sta->local->dev, sta);
ap_free_sta(ap, sta);
}
spin_unlock_bh(&ap->sta_table_lock);
}
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
#define PROC_LIMIT (PAGE_SIZE - 80)
static int prism2_ap_proc_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *p = page;
struct ap_data *ap = (struct ap_data *) data;
int i;
if (off > PROC_LIMIT) {
*eof = 1;
return 0;
}
p += sprintf(p, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
spin_lock_bh(&ap->sta_table_lock);
list_for_each_entry(sta, &ap->sta_list, list) {
p += sprintf(p, "%pM %d %d %d %d '",
sta->addr,
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
sta->u.ap.channel, sta->last_rx_signal,
sta->last_rx_silence, sta->last_rx_rate);
for (i = 0; i < sta->u.ap.ssid_len; i++)
p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
sta->u.ap.ssid[i] < 127) ?
"%c" : "<%02x>"),
sta->u.ap.ssid[i]);
p += sprintf(p, "'");
if (sta->capability & WLAN_CAPABILITY_ESS)
p += sprintf(p, " [ESS]");
if (sta->capability & WLAN_CAPABILITY_IBSS)
p += sprintf(p, " [IBSS]");
if (sta->capability & WLAN_CAPABILITY_PRIVACY)
p += sprintf(p, " [WEP]");
p += sprintf(p, "\n");
if ((p - page) > PROC_LIMIT) {
printk(KERN_DEBUG "hostap: ap proc did not fit\n");
break;
}
}
spin_unlock_bh(&ap->sta_table_lock);
if ((p - page) <= off) {
*eof = 1;
return 0;
}
*start = page + off;
return (p - page - off);
}
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
{
if (!ap)
return;
if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
"firmware upgrade recommended\n");
ap->nullfunc_ack = 1;
} else
ap->nullfunc_ack = 0;
if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
printk(KERN_WARNING "%s: Warning: secondary station firmware "
"version 1.4.2 does not seem to work in Host AP mode\n",
ap->local->dev->name);
}
}
/* Called only as a tasklet (software IRQ) */
static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
{
struct ap_data *ap = data;
struct ieee80211_hdr *hdr;
if (!ap->local->hostapd || !ap->local->apdev) {
dev_kfree_skb(skb);
return;
}
/* Pass the TX callback frame to the hostapd; use 802.11 header version
* 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
hdr = (struct ieee80211_hdr *) skb->data;
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS);
hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0));
skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control));
skb->protocol = cpu_to_be16(ETH_P_802_2);
memset(skb->cb, 0, sizeof(skb->cb));
netif_rx(skb);
}
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
/* Called only as a tasklet (software IRQ) */
static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
{
struct ap_data *ap = data;
struct net_device *dev = ap->local->dev;
struct ieee80211_hdr *hdr;
u16 auth_alg, auth_transaction, status;
struct sta_info *sta = NULL;
char *txt = NULL;
if (ap->local->hostapd) {
dev_kfree_skb(skb);
return;
}
hdr = (struct ieee80211_hdr *) skb->data;
if (!ieee80211_is_auth(hdr->frame_control) ||
skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
"frame\n", dev->name);
dev_kfree_skb(skb);
return;
}
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
auth_alg = le16_to_cpu(*pos++);
auth_transaction = le16_to_cpu(*pos++);
status = le16_to_cpu(*pos++);
if (!ok) {
txt = "frame was not ACKed";
goto done;
}
spin_lock(&ap->sta_table_lock);
sta = ap_get_sta(ap, hdr->addr1);
if (sta)
atomic_inc(&sta->users);
spin_unlock(&ap->sta_table_lock);
if (!sta) {
txt = "STA not found";
goto done;
}
if (status == WLAN_STATUS_SUCCESS &&
((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
(auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
txt = "STA authenticated";
sta->flags |= WLAN_STA_AUTH;
sta->last_auth = jiffies;
} else if (status != WLAN_STATUS_SUCCESS)
txt = "authentication failed";
done:
if (sta)
atomic_dec(&sta->users);
if (txt) {
PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d "
"trans#=%d status=%d - %s\n",
auth_alg, auth_transaction, status, txt);
}
dev_kfree_skb(skb);
}
/* Called only as a tasklet (software IRQ) */
static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
{
struct ap_data *ap = data;
struct net_device *dev = ap->local->dev;
struct ieee80211_hdr *hdr;
u16 status;
struct sta_info *sta = NULL;
char *txt = NULL;
if (ap->local->hostapd) {
dev_kfree_skb(skb);
return;
}
hdr = (struct ieee80211_hdr *) skb->data;
if ((!ieee80211_is_assoc_resp(hdr->frame_control) &&
!ieee80211_is_reassoc_resp(hdr->frame_control)) ||
skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
"frame\n", dev->name);
dev_kfree_skb(skb);
return;
}
if (!ok) {
txt = "frame was not ACKed";
goto done;
}
spin_lock(&ap->sta_table_lock);
sta = ap_get_sta(ap, hdr->addr1);
if (sta)
atomic_inc(&sta->users);
spin_unlock(&ap->sta_table_lock);
if (!sta) {
txt = "STA not found";
goto done;
}
pos++;
status = le16_to_cpu(*pos++);
if (status == WLAN_STATUS_SUCCESS) {
if (!(sta->flags & WLAN_STA_ASSOC))
hostap_event_new_sta(dev, sta);
txt = "STA associated";
sta->flags |= WLAN_STA_ASSOC;
sta->last_assoc = jiffies;
} else
txt = "association failed";
done:
if (sta)
atomic_dec(&sta->users);
if (txt) {
PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n",
dev->name, hdr->addr1, txt);
}
dev_kfree_skb(skb);
}
/* Called only as a tasklet (software IRQ); TX callback for poll frames used
* in verifying whether the STA is still present. */
static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
{
struct ap_data *ap = data;
struct ieee80211_hdr *hdr;
struct sta_info *sta;
if (skb->len < 24)
goto fail;
hdr = (struct ieee80211_hdr *) skb->data;
if (ok) {
spin_lock(&ap->sta_table_lock);
sta = ap_get_sta(ap, hdr->addr1);
if (sta)
sta->flags &= ~WLAN_STA_PENDING_POLL;
spin_unlock(&ap->sta_table_lock);
} else {
PDEBUG(DEBUG_AP,
"%s: STA %pM did not ACK activity poll frame\n",
ap->local->dev->name, hdr->addr1);
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
}
fail:
dev_kfree_skb(skb);
}
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
void hostap_init_data(local_info_t *local)
{
struct ap_data *ap = local->ap;
if (ap == NULL) {
printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
return;
}
memset(ap, 0, sizeof(struct ap_data));
ap->local = local;
ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
ap->max_inactivity =
GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
spin_lock_init(&ap->sta_table_lock);
INIT_LIST_HEAD(&ap->sta_list);
/* Initialize task queue structure for AP management */
INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue);
ap->tx_callback_idx =
hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
if (ap->tx_callback_idx == 0)
printk(KERN_WARNING "%s: failed to register TX callback for "
"AP\n", local->dev->name);
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue);
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
ap->tx_callback_auth =
hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
ap->tx_callback_assoc =
hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
ap->tx_callback_poll =
hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
ap->tx_callback_poll == 0)
printk(KERN_WARNING "%s: failed to register TX callback for "
"AP\n", local->dev->name);
spin_lock_init(&ap->mac_restrictions.lock);
INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
ap->initialized = 1;
}
void hostap_init_ap_proc(local_info_t *local)
{
struct ap_data *ap = local->ap;
ap->proc = local->proc;
if (ap->proc == NULL)
return;
#ifndef PRISM2_NO_PROCFS_DEBUG
create_proc_read_entry("ap_debug", 0, ap->proc,
ap_debug_proc_read, ap);
#endif /* PRISM2_NO_PROCFS_DEBUG */
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
create_proc_read_entry("ap_control", 0, ap->proc,
ap_control_proc_read, ap);
create_proc_read_entry("ap", 0, ap->proc,
prism2_ap_proc_read, ap);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
}
void hostap_free_data(struct ap_data *ap)
{
if (ap == NULL || !ap->initialized) {
printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
"initialized - skip resource freeing\n");
return;
}
flush_work(&ap->add_sta_proc_queue);
flush_work(&ap->wds_oper_queue);
if (ap->crypt)
ap->crypt->deinit(ap->crypt_priv);
ap->crypt = ap->crypt_priv = NULL;
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
list_for_each_entry_safe(sta, n, &ap->sta_list, list) {
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
ap_sta_hash_del(ap, sta);
list_del(&sta->list);
if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
hostap_event_expired_sta(sta->local->dev, sta);
ap_free_sta(ap, sta);
}
#ifndef PRISM2_NO_PROCFS_DEBUG
if (ap->proc != NULL) {
remove_proc_entry("ap_debug", ap->proc);
}
#endif /* PRISM2_NO_PROCFS_DEBUG */
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if (ap->proc != NULL) {
remove_proc_entry("ap", ap->proc);
remove_proc_entry("ap_control", ap->proc);
}
ap_control_flush_macs(&ap->mac_restrictions);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
ap->initialized = 0;
}
/* caller should have mutex for AP STA list handling */
static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
{
struct sta_info *s;
s = ap->sta_hash[STA_HASH(sta)];
while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0)
s = s->hnext;
return s;
}
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
/* Called from timer handler and from scheduled AP queue handlers */
static void prism2_send_mgmt(struct net_device *dev,
u16 type_subtype, char *body,
int body_len, u8 *addr, u16 tx_cb_idx)
{
struct hostap_interface *iface;
local_info_t *local;
struct ieee80211_hdr *hdr;
u16 fc;
struct sk_buff *skb;
struct hostap_skb_tx_data *meta;
int hdrlen;
iface = netdev_priv(dev);
local = iface->local;
dev = local->dev; /* always use master radio device */
iface = netdev_priv(dev);
if (!(dev->flags & IFF_UP)) {
PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
"cannot send frame\n", dev->name);
return;
}
skb = dev_alloc_skb(sizeof(*hdr) + body_len);
if (skb == NULL) {
PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
"skb\n", dev->name);
return;
}
fc = type_subtype;
hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype));
hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen);
if (body)
memcpy(skb_put(skb, body_len), body, body_len);
memset(hdr, 0, hdrlen);
/* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
* tx_control instead of using local->tx_control */
memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
if (ieee80211_is_data(hdr->frame_control)) {
fc |= IEEE80211_FCTL_FROMDS;
memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
} else if (ieee80211_is_ctl(hdr->frame_control)) {
/* control:ACK does not have addr2 or addr3 */
memset(hdr->addr2, 0, ETH_ALEN);
memset(hdr->addr3, 0, ETH_ALEN);
} else {
memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
}
hdr->frame_control = cpu_to_le16(fc);
meta = (struct hostap_skb_tx_data *) skb->cb;
memset(meta, 0, sizeof(*meta));
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
meta->iface = iface;
meta->tx_cb_idx = tx_cb_idx;
skb->dev = dev;
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
dev_queue_xmit(skb);
}
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
static int prism2_sta_proc_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *p = page;
struct sta_info *sta = (struct sta_info *) data;
int i;
/* FIX: possible race condition.. the STA data could have just expired,
* but proc entry was still here so that the read could have started;
* some locking should be done here.. */
if (off != 0) {
*eof = 1;
return 0;
}