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