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