Use netproto/802_11 includes instead of net/if_ieee80211.h.
[dragonfly.git] / sys / dev / netif / wi / wi_hostap.c
1 /*
2  * Copyright (c) 2002
3  *      Thomas Skibo <skibo@pacbell.net>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Thomas Skibo.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Thomas Skibo AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Thomas Skibo OR HIS DRINKING PALS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/dev/wi/wi_hostap.c,v 1.7.2.4 2002/08/02 07:11:34 imp Exp $
33  * $DragonFly: src/sys/dev/netif/wi/Attic/wi_hostap.c,v 1.9 2004/07/27 14:30:10 joerg Exp $
34  */
35
36 /* This is experimental Host AP software for Prism 2 802.11b interfaces.
37  *
38  * Much of this is based upon the "Linux Host AP driver Host AP driver
39  * for Intersil Prism2" by Jouni Malinen <jkm@ssh.com> or <jkmaline@cc.hut.fi>.
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #if defined(__FreeBSD__) && __FreeBSD_version >= 500033
45 #include <sys/endian.h>
46 #endif
47 #include <sys/sockio.h>
48 #include <sys/mbuf.h>
49 #include <sys/malloc.h>
50 #include <sys/kernel.h>
51 #include <sys/proc.h>
52 #include <sys/ucred.h>
53 #include <sys/socket.h>
54 #include <sys/module.h>
55 #include <sys/queue.h>
56 #include <sys/bus.h>
57 #include <sys/syslog.h>
58 #include <sys/sysctl.h>
59
60 #include <machine/bus.h>
61 #include <machine/resource.h>
62 #include <machine/clock.h>
63 #include <machine/md_var.h>
64 #include <machine/bus_pio.h>
65 #include <sys/rman.h>
66
67 #include <net/if.h>
68 #include <net/if_arp.h>
69 #include <net/ethernet.h>
70 #include <net/if_dl.h>
71 #include <net/if_media.h>
72 #include <net/if_types.h>
73 #include <netproto/802_11/ieee80211.h>
74 #include <netproto/802_11/ieee80211_ioctl.h>
75 #include <netproto/802_11/if_wavelan_ieee.h>
76
77 #include <netinet/in.h>
78 #include <netinet/in_systm.h>
79 #include <netinet/in_var.h>
80 #include <netinet/ip.h>
81 #include <netinet/if_ether.h>
82
83 #include "wi_hostap.h"
84 #include "if_wivar.h"
85 #include "if_wireg.h"
86
87 MALLOC_DEFINE(M_HAP_STA, "hostap_sta", "if_wi host AP mode station entry");
88
89 static void wihap_sta_timeout(void *v);
90 static struct wihap_sta_info *wihap_sta_alloc(struct wi_softc *sc,
91     u_int8_t *addr);
92 static void wihap_sta_delete(struct wihap_sta_info *sta);
93 static struct wihap_sta_info *wihap_sta_find(struct wihap_info *whi,
94     u_int8_t *addr);
95 static int wihap_sta_is_assoc(struct wihap_info *whi, u_int8_t addr[]);
96 static void wihap_auth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
97     caddr_t pkt, int len);
98 static void wihap_sta_deauth(struct wi_softc *sc, u_int8_t sta_addr[],
99     u_int16_t reason);
100 static void wihap_deauth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
101     caddr_t pkt, int len);
102 static void wihap_assoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
103     caddr_t pkt, int len);
104 static void wihap_sta_disassoc(struct wi_softc *sc, u_int8_t sta_addr[],
105     u_int16_t reason);
106 static void wihap_disassoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
107     caddr_t pkt, int len);
108
109 /*
110  * Spl use in this driver.
111  *
112  * splnet is used everywhere here to block timeouts when we need to do
113  * so.
114  */
115
116 /*
117  * take_hword()
118  *
119  *      Used for parsing management frames.  The pkt pointer and length
120  *      variables are updated after the value is removed.
121  */
122 static __inline u_int16_t
123 take_hword(caddr_t *ppkt, int *plen)
124 {
125         u_int16_t s = le16toh(* (u_int16_t *) *ppkt);
126         *ppkt += sizeof(u_int16_t);
127         *plen -= sizeof(u_int16_t);
128         return s;
129 }
130
131 /* take_tlv()
132  *
133  *      Parse out TLV element from a packet, check for underflow of packet
134  *      or overflow of buffer, update pkt/len.
135  */
136 static int
137 take_tlv(caddr_t *ppkt, int *plen, int id_expect, void *dst, int maxlen)
138 {
139         u_int8_t id, len;
140
141         if (*plen < 2)
142                 return -1;
143
144         id = ((u_int8_t *)*ppkt)[0];
145         len = ((u_int8_t *)*ppkt)[1];
146
147         if (id != id_expect || *plen < len+2 || maxlen < len)
148                 return -1;
149
150         bcopy(*ppkt + 2, dst, len);
151         *plen -= 2 + len;
152         *ppkt += 2 + len;
153
154         return (len);
155 }
156
157 /* put_hword()
158  *      Put half-word element into management frames.
159  */
160 static __inline void
161 put_hword(caddr_t *ppkt, u_int16_t s)
162 {
163         * (u_int16_t *) *ppkt = htole16(s);
164         *ppkt += sizeof(u_int16_t);
165 }
166
167 /* put_tlv()
168  *      Put TLV elements into management frames.
169  */
170 static void
171 put_tlv(caddr_t *ppkt, u_int8_t id, void *src, u_int8_t len)
172 {
173         (*ppkt)[0] = id;
174         (*ppkt)[1] = len;
175         bcopy(src, (*ppkt) + 2, len);
176         *ppkt += 2 + len;
177 }
178
179 static int
180 put_rates(caddr_t *ppkt, u_int16_t rates)
181 {
182         u_int8_t ratebuf[8];
183         int len = 0;
184
185         if (rates & WI_SUPPRATES_1M)
186                 ratebuf[len++] = 0x82;
187         if (rates & WI_SUPPRATES_2M)
188                 ratebuf[len++] = 0x84;
189         if (rates & WI_SUPPRATES_5M)
190                 ratebuf[len++] = 0x8b;
191         if (rates & WI_SUPPRATES_11M)
192                 ratebuf[len++] = 0x96;
193
194         put_tlv(ppkt, IEEE80211_ELEMID_RATES, ratebuf, len);
195         return len;
196 }
197
198 /* wihap_init()
199  *
200  *      Initialize host AP data structures.  Called even if port type is
201  *      not AP.
202  */
203 void
204 wihap_init(struct wi_softc *sc)
205 {
206         int i;
207         struct wihap_info *whi = &sc->wi_hostap_info;
208
209         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
210                 printf("wihap_init: sc=0x%x whi=0x%x\n", (int)sc, (int)whi);
211
212         bzero(whi, sizeof(struct wihap_info));
213
214         if (sc->wi_ptype != WI_PORTTYPE_AP)
215                 return;
216
217         whi->apflags = WIHAPFL_ACTIVE;
218
219         LIST_INIT(&whi->sta_list);
220         for (i = 0; i < WI_STA_HASH_SIZE; i++)
221                 LIST_INIT(&whi->sta_hash[i]);
222
223         whi->inactivity_time = WIHAP_DFLT_INACTIVITY_TIME;
224 }
225
226 /* wihap_sta_disassoc()
227  *
228  *      Send a disassociation frame to a specified station.
229  */
230 static void
231 wihap_sta_disassoc(struct wi_softc *sc, u_int8_t sta_addr[], u_int16_t reason)
232 {
233         struct wi_80211_hdr     *resp_hdr;
234         caddr_t                 pkt;
235
236         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
237                 printf("Sending disassoc to sta %6D\n", sta_addr, ":");
238
239         /* Send disassoc packet. */
240         resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
241         bzero(resp_hdr, sizeof(struct wi_80211_hdr));
242         resp_hdr->frame_ctl = WI_FTYPE_MGMT | WI_STYPE_MGMT_DISAS;
243         pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr);
244
245         bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN);
246         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
247         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
248
249         put_hword(&pkt, reason);
250
251         wi_mgmt_xmit(sc, sc->wi_txbuf, 2 + sizeof(struct wi_80211_hdr));
252 }
253
254 /* wihap_sta_deauth()
255  *
256  *      Send a deauthentication message to a specified station.
257  */
258 static void
259 wihap_sta_deauth(struct wi_softc *sc, u_int8_t sta_addr[], u_int16_t reason)
260 {
261         struct wi_80211_hdr     *resp_hdr;
262         caddr_t                 pkt;
263
264         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
265                 printf("Sending deauth to sta %6D\n", sta_addr, ":");
266
267         /* Send deauth packet. */
268         resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
269         bzero(resp_hdr, sizeof(struct wi_80211_hdr));
270         resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_DEAUTH);
271         pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr);
272
273         bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN);
274         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
275         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
276
277         put_hword(&pkt, reason);
278
279         wi_mgmt_xmit(sc, sc->wi_txbuf, 2 + sizeof(struct wi_80211_hdr));
280 }
281
282 /* wihap_shutdown()
283  *
284  *      Disassociate all stations and free up data structures.
285  */
286 void
287 wihap_shutdown(struct wi_softc *sc)
288 {
289         struct wihap_info       *whi = &sc->wi_hostap_info;
290         struct wihap_sta_info   *sta, *next;
291         int s;
292
293         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
294                 printf("wihap_shutdown: sc=0x%x whi=0x%x\n",
295                     (int)sc, (int)whi);
296
297         if (!(whi->apflags & WIHAPFL_ACTIVE))
298                 return;
299
300         /* XXX: I read somewhere you can deauth all the stations with
301          * a single broadcast.  Maybe try that someday.
302          */
303
304         s = splnet();
305         sta = LIST_FIRST(&whi->sta_list);
306         while (sta) {
307                 untimeout(wihap_sta_timeout, sta, sta->tmo);
308                 if (!sc->wi_gone) {
309                         /* Disassociate station. */
310                         if (sta->flags & WI_SIFLAGS_ASSOC)
311                                 wihap_sta_disassoc(sc, sta->addr,
312                                     IEEE80211_REASON_ASSOC_LEAVE);
313                         /* Deauth station. */
314                         if (sta->flags & WI_SIFLAGS_AUTHEN)
315                                 wihap_sta_deauth(sc, sta->addr,
316                                     IEEE80211_REASON_AUTH_LEAVE);
317                 }
318
319                 /* Delete the structure. */
320                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
321                         printf("wihap_shutdown: FREE(sta=0x%x)\n", (int)sta);
322                 next = LIST_NEXT(sta, list);
323                 FREE(sta, M_HAP_STA);
324                 sta = next;
325         }
326
327         whi->apflags = 0;
328         splx(s);
329 }
330
331 /* sta_hash_func()
332  * Hash function for finding stations from ethernet address.
333  */
334 static __inline int
335 sta_hash_func(u_int8_t addr[])
336 {
337         return ((addr[3] + addr[4] + addr[5]) % WI_STA_HASH_SIZE);
338 }
339
340 /* addr_cmp():  Maybe this is a faster way to compare addresses? */
341 static __inline int
342 addr_cmp(u_int8_t a[], u_int8_t b[])
343 {
344         return (*(u_int16_t *)(a + 4) == *(u_int16_t *)(b + 4) &&
345                 *(u_int32_t *)(a    ) == *(u_int32_t *)(b));
346 }
347
348 void
349 wihap_sta_timeout(void *v)
350 {
351         struct wihap_sta_info   *sta = v;
352         struct wi_softc         *sc = sta->sc;
353         struct wihap_info       *whi = &sc->wi_hostap_info;
354         int     s;
355
356         s = splnet();
357         if (sta->flags & WI_SIFLAGS_ASSOC) {
358                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
359                         device_printf(sc->dev, "inactivity disassoc: %6D\n",
360                             sta->addr, ":");
361
362                 /* Disassoc station. */
363                 wihap_sta_disassoc(sc, sta->addr,
364                     IEEE80211_REASON_ASSOC_EXPIRE);
365                 sta->flags &= ~WI_SIFLAGS_ASSOC;
366
367                 sta->tmo = timeout(wihap_sta_timeout, sta,
368                     hz * whi->inactivity_time);
369
370         } else if (sta->flags & WI_SIFLAGS_AUTHEN) {
371
372                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
373                         device_printf(sc->dev, "inactivity disassoc: %6D\n",
374                             sta->addr, ":");
375
376                 /* Deauthenticate station. */
377                 wihap_sta_deauth(sc, sta->addr, IEEE80211_REASON_AUTH_EXPIRE);
378                 sta->flags &= ~WI_SIFLAGS_AUTHEN;
379
380                 /* Delete the station if it's not permanent. */
381                 if (!(sta->flags & WI_SIFLAGS_PERM))
382                         wihap_sta_delete(sta);
383         }
384         splx(s);
385 }
386
387 /* wihap_sta_delete()
388  * Delete a single station and free up its data structure.
389  */
390 static void
391 wihap_sta_delete(struct wihap_sta_info *sta)
392 {
393         struct wi_softc         *sc = sta->sc;
394         struct wihap_info       *whi = &sc->wi_hostap_info;
395         int i = sta->asid - 0xc001;
396
397         untimeout(wihap_sta_timeout, sta, sta->tmo);
398
399         whi->asid_inuse_mask[i >> 4] &= ~(1UL << (i & 0xf));
400
401         LIST_REMOVE(sta, list);
402         LIST_REMOVE(sta, hash);
403         if (sta->challenge)
404                 FREE(sta->challenge, M_TEMP);
405         FREE(sta, M_HAP_STA);
406         whi->n_stations--;
407 }
408
409 /* wihap_sta_alloc()
410  *
411  *      Create a new station data structure and put it in the list
412  *      and hash table.
413  */
414 static struct wihap_sta_info *
415 wihap_sta_alloc(struct wi_softc *sc, u_int8_t *addr)
416 {
417         struct wihap_info       *whi = &sc->wi_hostap_info;
418         struct wihap_sta_info   *sta;
419         int i, hash = sta_hash_func(addr);
420
421         /* Allocate structure. */
422         MALLOC(sta, struct wihap_sta_info *, sizeof(struct wihap_sta_info),
423             M_HAP_STA, M_INTWAIT);
424         if (sta == NULL)
425                 return(NULL);
426
427         bzero(sta, sizeof(struct wihap_sta_info));
428
429         /* Allocate an ASID. */
430         i=hash<<4;
431         while (whi->asid_inuse_mask[i >> 4] & (1UL << (i & 0xf)))
432                 i = (i == (WI_STA_HASH_SIZE << 4) - 1) ? 0 : (i + 1);
433         whi->asid_inuse_mask[i >> 4] |= (1UL << (i & 0xf));
434         sta->asid = 0xc001 + i;
435
436         /* Insert in list and hash list. */
437         LIST_INSERT_HEAD(&whi->sta_list, sta, list);
438         LIST_INSERT_HEAD(&whi->sta_hash[hash], sta, hash);
439
440         sta->sc = sc;
441         whi->n_stations++;
442         bcopy(addr, &sta->addr, ETHER_ADDR_LEN);
443
444         return(sta);
445 }
446
447 /* wihap_sta_find()
448  *
449  *      Find station structure given address.
450  */
451 static struct wihap_sta_info *
452 wihap_sta_find(struct wihap_info *whi, u_int8_t *addr)
453 {
454         int i;
455         struct wihap_sta_info *sta;
456
457         i = sta_hash_func(addr);
458         LIST_FOREACH(sta, &whi->sta_hash[i], hash)
459                 if (addr_cmp(addr,sta->addr))
460                         return sta;
461
462         return (NULL);
463 }
464
465 static int
466 wihap_check_rates(struct wihap_sta_info *sta, u_int8_t rates[], int rates_len)
467 {
468         struct wi_softc *sc = sta->sc;
469         int     i;
470
471         sta->rates = 0;
472         sta->tx_max_rate = 0;
473         for (i=0; i<rates_len; i++)
474                 switch (rates[i] & 0x7f) {
475                 case 0x02:
476                         sta->rates |= WI_SUPPRATES_1M;
477                         break;
478                 case 0x04:
479                         sta->rates |= WI_SUPPRATES_2M;
480                         if (sta->tx_max_rate<1)
481                                 sta->tx_max_rate = 1;
482                         break;
483                 case 0x0b:
484                         sta->rates |= WI_SUPPRATES_5M;
485                         if (sta->tx_max_rate<2)
486                                 sta->tx_max_rate = 2;
487                         break;
488                 case 0x16:
489                         sta->rates |= WI_SUPPRATES_11M;
490                         sta->tx_max_rate = 3;
491                         break;
492                 }
493
494         sta->rates &= sc->wi_supprates;
495         sta->tx_curr_rate = sta->tx_max_rate;
496
497         return (sta->rates == 0 ? -1 : 0);
498 }
499
500
501 /* wihap_auth_req()
502  *
503  *      Handle incoming authentication request.  Only handle OPEN
504  *      requests.
505  */
506 static void
507 wihap_auth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
508     caddr_t pkt, int len)
509 {
510         struct wihap_info       *whi = &sc->wi_hostap_info;
511         struct wihap_sta_info   *sta;
512
513         u_int16_t               algo;
514         u_int16_t               seq;
515         u_int16_t               status;
516         int                     i, challenge_len;
517         u_int32_t               challenge[32];
518
519         struct wi_80211_hdr     *resp_hdr;
520
521         if (len < 6)
522                 return;
523
524         /* Break open packet. */
525         algo = take_hword(&pkt, &len);
526         seq = take_hword(&pkt, &len);
527         status = take_hword(&pkt, &len);
528         challenge_len = 0;
529         if (len > 0 && (challenge_len = take_tlv(&pkt, &len,
530             IEEE80211_ELEMID_CHALLENGE, challenge, sizeof(challenge))) < 0)
531                 return;
532
533         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
534                 printf("wihap_auth_req: station %6D algo=0x%x seq=0x%x\n",
535                        rxfrm->wi_addr2, ":", algo, seq);
536
537         /* Find or create station info. */
538         sta = wihap_sta_find(whi, rxfrm->wi_addr2);
539         if (sta == NULL) {
540
541                 /* Are we allowing new stations?
542                  */
543                 if (whi->apflags & WIHAPFL_MAC_FILT) {
544                         status = IEEE80211_STATUS_OTHER; /* XXX */
545                         goto fail;
546                 }
547
548                 /* Check for too many stations.
549                  */
550                 if (whi->n_stations >= WIHAP_MAX_STATIONS) {
551                         status = IEEE80211_STATUS_TOO_MANY_STATIONS;
552                         goto fail;
553                 }
554
555                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
556                         printf("wihap_auth_req: new station\n");
557
558                 /* Create new station. */
559                 sta = wihap_sta_alloc(sc, rxfrm->wi_addr2);
560                 if (sta == NULL) {
561                         /* Out of memory! */
562                         status = IEEE80211_STATUS_TOO_MANY_STATIONS;
563                         goto fail;
564                 }
565         }
566
567         /* Note: it's okay to leave the station info structure around
568          * if the authen fails.  It'll be timed out eventually.
569          */
570         switch (algo) {
571         case IEEE80211_AUTH_ALG_OPEN:
572                 if (sc->wi_authmode != IEEE80211_AUTH_OPEN) {
573                         seq = 2;
574                         status = IEEE80211_STATUS_ALG;
575                         goto fail;
576                 }
577                 if (seq != 1) {
578                         seq = 2;
579                         status = IEEE80211_STATUS_SEQUENCE;
580                         goto fail;
581                 }
582                 challenge_len = 0;
583                 seq = 2;
584                 sta->flags |= WI_SIFLAGS_AUTHEN;
585                 break;
586         case IEEE80211_AUTH_ALG_SHARED:
587                 if (sc->wi_authmode != IEEE80211_AUTH_SHARED) {
588                         seq = 2;
589                         status = IEEE80211_STATUS_ALG;
590                         goto fail;
591                 }
592                 switch (seq) {
593                 case 1:
594                         /* Create a challenge frame. */
595                         if (!sta->challenge) {
596                                 MALLOC(sta->challenge, u_int32_t *, 128,
597                                        M_TEMP, M_INTWAIT);
598                                 if (!sta->challenge)
599                                         return;
600                         }
601                         for (i = 0; i < 32; i++)
602                                 challenge[i] = sta->challenge[i] =
603                                         arc4random();
604                         challenge_len = 128;
605                         seq = 2;
606                         break;
607                 case 3:
608                         if (challenge_len != 128 || !sta->challenge ||
609                             !(le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_WEP)) {
610                                 status = IEEE80211_STATUS_CHALLENGE;
611                                 goto fail;
612                         }
613                         challenge_len = 0;
614                         seq = 4;
615
616                         /* Check the challenge text.  (Was decrypted by
617                          * the adapter.)
618                          */
619                         for (i=0; i<32; i++)
620                                 if (sta->challenge[i] != challenge[i]) {
621                                         status = IEEE80211_STATUS_CHALLENGE;
622                                         FREE(sta->challenge, M_TEMP);
623                                         sta->challenge = NULL;
624                                         goto fail;
625                                 }
626
627                         sta->flags |= WI_SIFLAGS_AUTHEN;
628                         FREE(sta->challenge, M_TEMP);
629                         sta->challenge = NULL;
630                         break;
631                 default:
632                         seq = 2;
633                         status = IEEE80211_STATUS_SEQUENCE;
634                         goto fail;
635                 } /* switch (seq) */
636                 break;
637         default:
638                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
639                         printf("wihap_auth_req: algorithm unsupported: 0x%x\n",
640                                algo);
641                 status = IEEE80211_STATUS_ALG;
642                 goto fail;
643         } /* switch (algo) */
644
645         status = IEEE80211_STATUS_SUCCESS;
646
647 fail:
648         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
649                 printf("wihap_auth_req: returns status=0x%x\n", status);
650
651         /* Send response. */
652         resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
653         bzero(resp_hdr, sizeof(struct wi_80211_hdr));
654         resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_AUTH);
655         bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN);
656         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
657         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
658         pkt = &sc->wi_txbuf[sizeof(struct wi_80211_hdr)];
659         put_hword(&pkt, algo);
660         put_hword(&pkt, seq);
661         put_hword(&pkt, status);
662         if (challenge_len > 0)
663                 put_tlv(&pkt, IEEE80211_ELEMID_CHALLENGE,
664                     challenge, challenge_len);
665         wi_mgmt_xmit(sc, sc->wi_txbuf, (char *) pkt - (char *) sc->wi_txbuf);
666 }
667
668 /* wihap_assoc_req()
669  *
670  *      Handle incoming association and reassociation requests.
671  */
672 static void
673 wihap_assoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
674                 caddr_t pkt, int len)
675 {
676         struct wihap_info       *whi = &sc->wi_hostap_info;
677         struct wihap_sta_info   *sta;
678         struct wi_80211_hdr     *resp_hdr;
679         u_int16_t               capinfo;
680         u_int16_t               lstintvl;
681         u_int8_t                rates[8];
682         int                     ssid_len, rates_len;
683         char                    ssid[33];
684         u_int16_t               status;
685         u_int16_t               asid = 0;
686
687         if (len < 8)
688                 return;
689
690         /* Pull out request parameters. */
691         capinfo = take_hword(&pkt, &len);
692         lstintvl = take_hword(&pkt, &len);
693
694         if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_STYPE)) ==
695             htole16(WI_STYPE_MGMT_REASREQ)) {
696                 if (len < 6)
697                         return;
698                 /* Eat the MAC address of the current AP */
699                 take_hword(&pkt, &len);
700                 take_hword(&pkt, &len);
701                 take_hword(&pkt, &len);
702         }
703
704         if ((ssid_len = take_tlv(&pkt, &len, IEEE80211_ELEMID_SSID,
705             ssid, sizeof(ssid) - 1))<0)
706                 return;
707         ssid[ssid_len] = '\0';
708         if ((rates_len = take_tlv(&pkt, &len, IEEE80211_ELEMID_RATES,
709             rates, sizeof(rates)))<0)
710                 return;
711
712         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
713                 printf("wihap_assoc_req: from station %6D\n",
714                     rxfrm->wi_addr2, ":");
715
716         /* If SSID doesn't match, simply drop. */
717         if (strcmp(sc->wi_net_name, ssid) != 0) {
718                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
719                         printf("wihap_assoc_req: bad ssid: '%s' != '%s'\n",
720                             ssid, sc->wi_net_name);
721                 return;
722         }
723
724         /* Is this station authenticated yet? */
725         sta = wihap_sta_find(whi, rxfrm->wi_addr2);
726         if (sta == NULL || !(sta->flags & WI_SIFLAGS_AUTHEN)) {
727                 wihap_sta_deauth(sc, rxfrm->wi_addr2,
728                     IEEE80211_REASON_NOT_AUTHED);
729                 return;
730         }
731
732         /* Check supported rates against ours. */
733         if (wihap_check_rates(sta, rates, rates_len) < 0) {
734                 status = IEEE80211_STATUS_RATES;
735                 goto fail;
736         }
737
738         /* Check capinfo.
739          * Check for ESS, not IBSS.
740          * Check WEP/PRIVACY flags match.
741          * Refuse stations requesting to be put on CF-polling list.
742          */
743         sta->capinfo = capinfo;
744         status = IEEE80211_STATUS_CAPINFO;
745         if ((capinfo & (IEEE80211_CAPINFO_ESS | IEEE80211_CAPINFO_IBSS)) !=
746             IEEE80211_CAPINFO_ESS) {
747                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
748                         printf("wihap_assoc_req: capinfo mismatch: "
749                             "client using IBSS mode\n");
750                 goto fail;
751
752         }
753         if ((sc->wi_use_wep && !(capinfo & IEEE80211_CAPINFO_PRIVACY)) ||
754             (!sc->wi_use_wep && (capinfo & IEEE80211_CAPINFO_PRIVACY))) {
755                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
756                         printf("wihap_assoc_req: capinfo mismatch: client "
757                             "%susing WEP\n", sc->wi_use_wep ? "not " : "");
758                 goto fail;
759         }
760         if ((capinfo & (IEEE80211_CAPINFO_CF_POLLABLE |
761             IEEE80211_CAPINFO_CF_POLLREQ)) == IEEE80211_CAPINFO_CF_POLLABLE) {
762                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
763                         printf("wihap_assoc_req: capinfo mismatch: "
764                             "client requested CF polling\n");
765                 goto fail;
766         }
767
768         /* Use ASID is allocated by whi_sta_alloc(). */
769         asid = sta->asid;
770
771         if (sta->flags & WI_SIFLAGS_ASSOC) {
772                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
773                         printf("wihap_assoc_req: already assoc'ed?\n");
774         }
775
776         sta->flags |= WI_SIFLAGS_ASSOC;
777         sta->inactivity_timer = whi->inactivity_time;
778         status = IEEE80211_STATUS_SUCCESS;
779
780 fail:
781         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
782                 printf("wihap_assoc_req: returns status=0x%x\n", status);
783
784         /* Send response. */
785         resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
786         bzero(resp_hdr, sizeof(struct wi_80211_hdr));
787         resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_ASRESP);
788         pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr);
789
790         bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN);
791         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
792         bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
793
794         put_hword(&pkt, capinfo);
795         put_hword(&pkt, status);
796         put_hword(&pkt, asid);
797         rates_len = put_rates(&pkt, sc->wi_supprates);
798
799         wi_mgmt_xmit(sc, sc->wi_txbuf,
800             8 + rates_len + sizeof(struct wi_80211_hdr));
801 }
802
803 /* wihap_deauth_req()
804  *
805  *      Handle deauthentication requests.  Delete the station.
806  */
807 static void
808 wihap_deauth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
809                  caddr_t pkt, int len)
810 {
811         struct wihap_info       *whi = &sc->wi_hostap_info;
812         struct wihap_sta_info   *sta;
813         u_int16_t               reason;
814
815         if (len<2)
816                 return;
817
818         reason = take_hword(&pkt, &len);
819
820         sta = wihap_sta_find(whi, rxfrm->wi_addr2);
821         if (sta == NULL) {
822                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
823                         printf("wihap_deauth_req: unknown station: %6D\n",
824                             rxfrm->wi_addr2, ":");
825         }
826         else
827                 wihap_sta_delete(sta);
828 }
829
830 /* wihap_disassoc_req()
831  *
832  *      Handle disassociation requests.  Just reset the assoc flag.
833  *      We'll free up the station resources when we get a deauth
834  *      request or when it times out.
835  */
836 static void
837 wihap_disassoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
838     caddr_t pkt, int len)
839 {
840         struct wihap_info       *whi = &sc->wi_hostap_info;
841         struct wihap_sta_info   *sta;
842         u_int16_t               reason;
843
844         if (len < 2)
845                 return;
846
847         reason = take_hword(&pkt, &len);
848
849         sta = wihap_sta_find(whi, rxfrm->wi_addr2);
850         if (sta == NULL) {
851                 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
852                         printf("wihap_disassoc_req: unknown station: %6D\n",
853                             rxfrm->wi_addr2, ":");
854         }
855         else if (!(sta->flags & WI_SIFLAGS_AUTHEN)) {
856                 /*
857                  * If station is not authenticated, send deauthentication
858                  * frame.
859                  */
860                 wihap_sta_deauth(sc, rxfrm->wi_addr2,
861                     IEEE80211_REASON_NOT_AUTHED);
862                 return;
863         }
864         else
865                 sta->flags &= ~WI_SIFLAGS_ASSOC;
866 }
867
868 /* wihap_debug_frame_type()
869  *
870  * Print out frame type.  Used in early debugging.
871  */
872 static __inline void
873 wihap_debug_frame_type(struct wi_frame *rxfrm)
874 {
875         printf("wihap_mgmt_input: len=%d ", le16toh(rxfrm->wi_dat_len));
876
877         if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_FTYPE)) ==
878             htole16(WI_FTYPE_MGMT)) {
879
880                 printf("MGMT: ");
881
882                 switch (le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) {
883                 case WI_STYPE_MGMT_ASREQ:
884                         printf("assoc req: \n");
885                         break;
886                 case WI_STYPE_MGMT_ASRESP:
887                         printf("assoc resp: \n");
888                         break;
889                 case WI_STYPE_MGMT_REASREQ:
890                         printf("reassoc req: \n");
891                         break;
892                 case WI_STYPE_MGMT_REASRESP:
893                         printf("reassoc resp: \n");
894                         break;
895                 case WI_STYPE_MGMT_PROBEREQ:
896                         printf("probe req: \n");
897                         break;
898                 case WI_STYPE_MGMT_PROBERESP:
899                         printf("probe resp: \n");
900                         break;
901                 case WI_STYPE_MGMT_BEACON:
902                         printf("beacon: \n");
903                         break;
904                 case WI_STYPE_MGMT_ATIM:
905                         printf("ann traf ind \n");
906                         break;
907                 case WI_STYPE_MGMT_DISAS:
908                         printf("disassociation: \n");
909                         break;
910                 case WI_STYPE_MGMT_AUTH:
911                         printf("auth: \n");
912                         break;
913                 case WI_STYPE_MGMT_DEAUTH:
914                         printf("deauth: \n");
915                         break;
916                 default:
917                         printf("unknown (stype=0x%x)\n",
918                             le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE);
919                 }
920
921         }
922         else {
923                 printf("ftype=0x%x (ctl=0x%x)\n",
924                     le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_FTYPE,
925                     le16toh(rxfrm->wi_frame_ctl));
926         }
927 }
928
929 /* wihap_mgmt_input:
930  *
931  *      Called for each management frame received in host ap mode.
932  *      wihap_mgmt_input() is expected to free the mbuf.
933  */
934 void
935 wihap_mgmt_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m)
936 {
937         caddr_t pkt;
938         int     s, len;
939
940         if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
941                 wihap_debug_frame_type(rxfrm);
942
943         pkt = mtod(m, caddr_t) + WI_802_11_OFFSET_RAW;
944         len = m->m_len - WI_802_11_OFFSET_RAW;
945
946         if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_FTYPE)) ==
947             htole16(WI_FTYPE_MGMT)) {
948
949                 /* any of the following will mess w/ the station list */
950                 s = splnet();
951                 switch (le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) {
952                 case WI_STYPE_MGMT_ASREQ:
953                         wihap_assoc_req(sc, rxfrm, pkt, len);
954                         break;
955                 case WI_STYPE_MGMT_ASRESP:
956                         break;
957                 case WI_STYPE_MGMT_REASREQ:
958                         wihap_assoc_req(sc, rxfrm, pkt, len);
959                         break;
960                 case WI_STYPE_MGMT_REASRESP:
961                         break;
962                 case WI_STYPE_MGMT_PROBEREQ:
963                         break;
964                 case WI_STYPE_MGMT_PROBERESP:
965                         break;
966                 case WI_STYPE_MGMT_BEACON:
967                         break;
968                 case WI_STYPE_MGMT_ATIM:
969                         break;
970                 case WI_STYPE_MGMT_DISAS:
971                         wihap_disassoc_req(sc, rxfrm, pkt, len);
972                         break;
973                 case WI_STYPE_MGMT_AUTH:
974                         wihap_auth_req(sc, rxfrm, pkt, len);
975                         break;
976                 case WI_STYPE_MGMT_DEAUTH:
977                         wihap_deauth_req(sc, rxfrm, pkt, len);
978                         break;
979                 }
980                 splx(s);
981         }
982
983         m_freem(m);
984 }
985
986 /* wihap_sta_is_assoc()
987  *
988  *      Determine if a station is assoc'ed.  Update its activity
989  *      counter as a side-effect.
990  */
991 static int
992 wihap_sta_is_assoc(struct wihap_info *whi, u_int8_t addr[])
993 {
994         struct wihap_sta_info *sta;
995         int retval, s;
996
997         s = splnet();
998         retval = 0;
999         sta = wihap_sta_find(whi, addr);
1000         if (sta != NULL && (sta->flags & WI_SIFLAGS_ASSOC)) {
1001                 /* Keep it active. */
1002                 untimeout(wihap_sta_timeout, sta, sta->tmo);
1003                 sta->tmo = timeout(wihap_sta_timeout, sta,
1004                     hz * whi->inactivity_time);
1005                 retval = 1;
1006         }
1007         splx(s);
1008         return (retval);
1009 }
1010
1011 /* wihap_check_tx()
1012  *
1013  *      Determine if a station is assoc'ed, get its tx rate, and update
1014  *      its activity.
1015  */
1016 int
1017 wihap_check_tx(struct wihap_info *whi, u_int8_t addr[], u_int8_t *txrate)
1018 {
1019         struct wihap_sta_info *sta;
1020         static u_int8_t txratetable[] = { 10, 20, 55, 110 };
1021         int s;
1022
1023         if (addr[0] & 0x01) {
1024                 *txrate = 0; /* XXX: multicast rate? */
1025                 return(1);
1026         }
1027         s = splnet();
1028         sta = wihap_sta_find(whi, addr);
1029         if (sta != NULL && (sta->flags & WI_SIFLAGS_ASSOC)) {
1030                 /* Keep it active. */
1031                 untimeout(wihap_sta_timeout, sta, sta->tmo);
1032                 sta->tmo = timeout(wihap_sta_timeout, sta,
1033                     hz * whi->inactivity_time);
1034                 *txrate = txratetable[ sta->tx_curr_rate ];
1035                 splx(s);
1036                 return(1);
1037         }
1038         splx(s);
1039
1040         return(0);
1041 }
1042
1043 /*
1044  * wihap_data_input()
1045  *
1046  *      Handle all data input on interface when in Host AP mode.
1047  *      Some packets are destined for this machine, others are
1048  *      repeated to other stations.
1049  *
1050  *      If wihap_data_input() returns a non-zero, it has processed
1051  *      the packet and will free the mbuf.
1052  */
1053 int
1054 wihap_data_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m)
1055 {
1056         struct ifnet            *ifp = &sc->arpcom.ac_if;
1057         struct wihap_info       *whi = &sc->wi_hostap_info;
1058         struct wihap_sta_info   *sta;
1059         int                     mcast, s;
1060
1061         /* TODS flag must be set. */
1062         if (!(rxfrm->wi_frame_ctl & htole16(WI_FCTL_TODS))) {
1063                 if (ifp->if_flags & IFF_DEBUG)
1064                         printf("wihap_data_input: no TODS src=%6D\n",
1065                             rxfrm->wi_addr2, ":");
1066                 m_freem(m);
1067                 return(1);
1068         }
1069
1070         /* Check BSSID. (Is this necessary?) */
1071         if (!addr_cmp(rxfrm->wi_addr1, sc->arpcom.ac_enaddr)) {
1072                 if (ifp->if_flags & IFF_DEBUG)
1073                         printf("wihap_data_input: incorrect bss: %6D\n",
1074                                 rxfrm->wi_addr1, ":");
1075                 m_freem(m);
1076                 return (1);
1077         }
1078
1079         s = splnet();
1080
1081         /* Find source station. */
1082         sta = wihap_sta_find(whi, rxfrm->wi_addr2);
1083
1084         /* Source station must be associated. */
1085         if (sta == NULL || !(sta->flags & WI_SIFLAGS_ASSOC)) {
1086                 if (ifp->if_flags & IFF_DEBUG)
1087                         printf("wihap_data_input: dropping unassoc src %6D\n",
1088                             rxfrm->wi_addr2, ":");
1089                 wihap_sta_disassoc(sc, rxfrm->wi_addr2,
1090                     IEEE80211_REASON_ASSOC_LEAVE);
1091                 splx(s);
1092                 m_freem(m);
1093                 return(1);
1094         }
1095
1096         untimeout(wihap_sta_timeout, sta, sta->tmo);
1097         sta->tmo = timeout(wihap_sta_timeout, sta,
1098             hz * whi->inactivity_time);
1099         sta->sig_info = le16toh(rxfrm->wi_q_info);
1100
1101         splx(s);
1102
1103         /* Repeat this packet to BSS? */
1104         mcast = (rxfrm->wi_addr3[0] & 0x01) != 0;
1105         if (mcast || wihap_sta_is_assoc(whi, rxfrm->wi_addr3)) {
1106
1107                 /* If it's multicast, make a copy.
1108                  */
1109                 if (mcast) {
1110                         m = m_copym(m, 0, M_COPYALL, MB_DONTWAIT);
1111                         if (m == NULL)
1112                                 return(0);
1113                         m->m_flags |= M_MCAST; /* XXX */
1114                 }
1115
1116                 /* Queue up for repeating.
1117                  */
1118                 IF_HANDOFF(&ifp->if_snd, m, ifp);
1119                 return (!mcast);
1120         }
1121
1122         return(0);
1123 }
1124
1125 /* wihap_ioctl()
1126  *
1127  *      Handle Host AP specific ioctls.  Called from wi_ioctl().
1128  */
1129 int
1130 wihap_ioctl(struct wi_softc *sc, u_long command, caddr_t data)
1131 {
1132         struct ifreq            *ifr = (struct ifreq *) data;
1133         struct wihap_info       *whi = &sc->wi_hostap_info;
1134         struct wihap_sta_info   *sta;
1135         struct hostap_getall    reqall;
1136         struct hostap_sta       reqsta;
1137         struct hostap_sta       stabuf;
1138         int                     s, error = 0, n, flag;
1139         struct thread           *td = curthread;
1140
1141         if (!(sc->arpcom.ac_if.if_flags & IFF_RUNNING))
1142                 return ENODEV;
1143
1144         switch (command) {
1145         case SIOCHOSTAP_DEL:
1146                 if ((error = suser(td)))
1147                         break;
1148                 if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1149                         break;
1150                 s = splnet();
1151                 sta = wihap_sta_find(whi, reqsta.addr);
1152                 if (sta == NULL)
1153                         error = ENOENT;
1154                 else {
1155                         /* Disassociate station. */
1156                         if (sta->flags & WI_SIFLAGS_ASSOC)
1157                                 wihap_sta_disassoc(sc, sta->addr,
1158                                     IEEE80211_REASON_ASSOC_LEAVE);
1159                         /* Deauth station. */
1160                         if (sta->flags & WI_SIFLAGS_AUTHEN)
1161                                 wihap_sta_deauth(sc, sta->addr,
1162                                     IEEE80211_REASON_AUTH_LEAVE);
1163
1164                         wihap_sta_delete(sta);
1165                 }
1166                 splx(s);
1167                 break;
1168
1169         case SIOCHOSTAP_GET:
1170                 if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1171                         break;
1172                 s = splnet();
1173                 sta = wihap_sta_find(whi, reqsta.addr);
1174                 if (sta == NULL) {
1175                         error = ENOENT;
1176                         splx(s);
1177                 } else {
1178                         reqsta.flags = sta->flags;
1179                         reqsta.asid = sta->asid;
1180                         reqsta.capinfo = sta->capinfo;
1181                         reqsta.sig_info = sta->sig_info;
1182                         reqsta.rates = sta->rates;
1183                         splx(s);
1184                         error = copyout(&reqsta, ifr->ifr_data,
1185                             sizeof(reqsta));
1186                 }
1187                 break;
1188
1189         case SIOCHOSTAP_ADD:
1190                 if ((error = suser(td)))
1191                         break;
1192                 if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1193                         break;
1194                 s = splnet();
1195                 sta = wihap_sta_find(whi, reqsta.addr);
1196                 if (sta != NULL) {
1197                         error = EEXIST;
1198                         splx(s);
1199                         break;
1200                 }
1201                 if (whi->n_stations >= WIHAP_MAX_STATIONS) {
1202                         error = ENOSPC;
1203                         splx(s);
1204                         break;
1205                 }
1206                 sta = wihap_sta_alloc(sc, reqsta.addr);
1207                 sta->flags = reqsta.flags;
1208                 sta->tmo = timeout(wihap_sta_timeout, sta,
1209                     hz * whi->inactivity_time);
1210                 splx(s);
1211                 break;
1212
1213         case SIOCHOSTAP_SFLAGS:
1214                 if ((error = suser(td)))
1215                         break;
1216                 if ((error = copyin(ifr->ifr_data, &flag, sizeof(int))))
1217                         break;
1218
1219                 whi->apflags = (whi->apflags & WIHAPFL_CANTCHANGE) |
1220                     (flag & ~WIHAPFL_CANTCHANGE);
1221                 break;
1222
1223         case SIOCHOSTAP_GFLAGS:
1224                 flag = (int) whi->apflags;
1225                 error = copyout(&flag, ifr->ifr_data, sizeof(int));
1226                 break;
1227
1228         case SIOCHOSTAP_GETALL:
1229                 if ((error = copyin(ifr->ifr_data, &reqall, sizeof(reqall))))
1230                         break;
1231
1232                 reqall.nstations = whi->n_stations;
1233                 n = 0;
1234                 s = splnet();
1235                 sta = LIST_FIRST(&whi->sta_list);
1236                 while (sta && reqall.size >= n+sizeof(struct hostap_sta)) {
1237
1238                         bcopy(sta->addr, stabuf.addr, ETHER_ADDR_LEN);
1239                         stabuf.asid = sta->asid;
1240                         stabuf.flags = sta->flags;
1241                         stabuf.capinfo = sta->capinfo;
1242                         stabuf.sig_info = sta->sig_info;
1243                         stabuf.rates = sta->rates;
1244
1245                         error = copyout(&stabuf, (caddr_t) reqall.addr + n,
1246                             sizeof(struct hostap_sta));
1247                         if (error)
1248                                 break;
1249
1250                         sta = LIST_NEXT(sta, list);
1251                         n += sizeof(struct hostap_sta);
1252                 }
1253                 splx(s);
1254
1255                 if (!error)
1256                         error = copyout(&reqall, ifr->ifr_data,
1257                             sizeof(reqall));
1258                 break;
1259         default:
1260                 printf("wihap_ioctl: i shouldn't get other ioctls!\n");
1261                 error = EINVAL;
1262         }
1263
1264         return(error);
1265 }