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