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