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