if_wi_hostap.c revision 1.11
1/*	$OpenBSD: if_wi_hostap.c,v 1.11 2002/04/08 18:44:42 mickey Exp $	*/
2
3/*
4 * Copyright (c) 2002
5 *	Thomas Skibo <skibo@pacbell.net>.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Thomas Skibo.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY Thomas Skibo AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL Thomas Skibo OR HIS DRINKING PALS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 *
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#include <sys/sockio.h>
45#include <sys/mbuf.h>
46#include <sys/malloc.h>
47#include <sys/kernel.h>
48#include <sys/timeout.h>
49#include <sys/proc.h>
50#include <sys/ucred.h>
51#include <sys/socket.h>
52#include <sys/queue.h>
53#include <sys/syslog.h>
54#include <sys/sysctl.h>
55#include <sys/device.h>
56
57#include <machine/bus.h>
58
59#include <net/if.h>
60#include <net/if_arp.h>
61#include <net/if_dl.h>
62#include <net/if_media.h>
63#include <net/if_types.h>
64
65#include <netinet/in.h>
66#include <netinet/in_systm.h>
67#include <netinet/in_var.h>
68#include <netinet/ip.h>
69#include <netinet/if_ether.h>
70
71#include <net/if_ieee80211.h>
72
73#include <dev/ic/if_wireg.h>
74#include <dev/ic/if_wi_ieee.h>
75#include <dev/ic/if_wivar.h>
76
77void wihap_sta_timeout(void *v);
78struct wihap_sta_info *wihap_sta_alloc(struct wi_softc *sc, u_int8_t *addr);
79void wihap_sta_delete(struct wihap_sta_info *sta);
80struct wihap_sta_info *wihap_sta_find(struct wihap_info *whi, u_int8_t *addr);
81int wihap_sta_is_assoc(struct wihap_info *whi, u_int8_t addr[]);
82void wihap_auth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
83    caddr_t pkt, int len);
84void wihap_sta_deauth(struct wi_softc *sc, u_int8_t sta_addr[],
85    u_int16_t reason);
86void wihap_deauth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
87    caddr_t pkt, int len);
88void wihap_assoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
89    caddr_t pkt, int len);
90void wihap_sta_disassoc(struct wi_softc *sc,
91    struct wihap_sta_info *sta, u_int16_t reason);
92void wihap_disassoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
93    caddr_t pkt, int len);
94
95/*
96 * take_hword()
97 *
98 *	Used for parsing management frames.  The pkt pointer and length
99 *	variables are updated after the value is removed.
100 */
101static __inline u_int16_t
102take_hword(caddr_t *ppkt, int *plen)
103{
104	u_int16_t s = letoh16(* (u_int16_t *) *ppkt);
105	*ppkt += sizeof(u_int16_t);
106	*plen -= sizeof(u_int16_t);
107	return s;
108}
109
110/* take_tlv()
111 *
112 *	Parse out TLV element from a packet, check for underflow of packet
113 *	or overflow of buffer, update pkt/len.
114 */
115static int
116take_tlv(caddr_t *ppkt, int *plen, int id_expect, void *dst, int maxlen)
117{
118	u_int8_t id, len;
119
120	if (*plen < 2)
121		return -1;
122
123	id = ((u_int8_t *)*ppkt)[0];
124	len = ((u_int8_t *)*ppkt)[1];
125
126	if (id != id_expect || *plen < len+2 || maxlen < len)
127		return -1;
128
129	bcopy(*ppkt + 2, dst, len);
130	*plen -= 2 + len;
131	*ppkt += 2 + len;
132
133	return (len);
134}
135
136/* put_hword()
137 *	Put half-word element into management frames.
138 */
139static __inline void
140put_hword(caddr_t *ppkt, u_int16_t s)
141{
142	* (u_int16_t *) *ppkt = htole16(s);
143	*ppkt += sizeof(u_int16_t);
144}
145
146/* put_tlv()
147 *	Put TLV elements into management frames.
148 */
149static void
150put_tlv(caddr_t *ppkt, u_int8_t id, void *src, u_int8_t len)
151{
152	(*ppkt)[0] = id;
153	(*ppkt)[1] = len;
154	bcopy(src, (*ppkt) + 2, len);
155	*ppkt += 2 + len;
156}
157
158static int
159put_rates(caddr_t *ppkt, u_int16_t rates)
160{
161	u_int8_t ratebuf[8];
162	int len = 0;
163
164	if (rates & WI_SUPPRATES_1M)
165		ratebuf[len++] = 0x82;
166	if (rates & WI_SUPPRATES_2M)
167		ratebuf[len++] = 0x84;
168	if (rates & WI_SUPPRATES_5M)
169		ratebuf[len++] = 0x8b;
170	if (rates & WI_SUPPRATES_11M)
171		ratebuf[len++] = 0x96;
172
173	put_tlv(ppkt, IEEE80211_ELEMID_RATES, ratebuf, len);
174	return len;
175}
176
177/* wihap_init()
178 *
179 *	Initialize host AP data structures.  Called even if port type is
180 *	not AP.
181 */
182void
183wihap_init(struct wi_softc *sc)
184{
185	int i;
186	struct wihap_info *whi = &sc->wi_hostap_info;
187
188	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
189		printf("wihap_init: sc=0x%x whi=0x%x\n", (int)sc, (int)whi);
190
191	bzero(whi, sizeof(struct wihap_info));
192
193	if (sc->wi_ptype != WI_PORTTYPE_AP)
194		return;
195
196	whi->apflags = WIHAPFL_ACTIVE;
197
198	LIST_INIT(&whi->sta_list);
199	for (i = 0; i < WI_STA_HASH_SIZE; i++)
200		LIST_INIT(&whi->sta_hash[i]);
201
202	whi->inactivity_time = WIHAP_DFLT_INACTIVITY_TIME;
203}
204
205/* wihap_sta_disassoc()
206 *
207 *	Send a disassociation frame to a specified station.
208 */
209void
210wihap_sta_disassoc(struct wi_softc *sc,
211    struct wihap_sta_info *sta, u_int16_t reason)
212{
213	struct wi_80211_hdr	*resp_hdr;
214	caddr_t			pkt;
215
216	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
217		printf("Sending disassoc to sta %s\n", ether_sprintf(sta->addr));
218
219	/* Send disassoc packet. */
220	resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
221	bzero(resp_hdr, sizeof(struct wi_80211_hdr));
222	resp_hdr->frame_ctl = WI_FTYPE_MGMT | WI_STYPE_MGMT_DISAS;
223	pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr);
224
225	bcopy(sta->addr, resp_hdr->addr1, ETHER_ADDR_LEN);
226	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
227	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
228
229	put_hword(&pkt, reason);
230
231	wi_mgmt_xmit(sc, sc->wi_txbuf, 2 + sizeof(struct wi_80211_hdr));
232}
233
234/* wihap_sta_deauth()
235 *
236 *	Send a deauthentication message to a specified station.
237 */
238void
239wihap_sta_deauth(struct wi_softc *sc, u_int8_t sta_addr[],
240    u_int16_t reason)
241{
242	struct wi_80211_hdr	*resp_hdr;
243	caddr_t			pkt;
244
245	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
246		printf("Sending deauth to sta %s\n", ether_sprintf(sta_addr));
247
248	/* Send deauth packet. */
249	resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
250	bzero(resp_hdr, sizeof(struct wi_80211_hdr));
251	resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_DEAUTH);
252	pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr);
253
254	bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN);
255	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
256	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
257
258	put_hword(&pkt, reason);
259
260	wi_mgmt_xmit(sc, sc->wi_txbuf, 2 + sizeof(struct wi_80211_hdr));
261}
262
263/* wihap_shutdown()
264 *
265 *	Disassociate all stations and free up data structures.
266 */
267void
268wihap_shutdown(struct wi_softc *sc)
269{
270	struct wihap_info	*whi = &sc->wi_hostap_info;
271	struct wihap_sta_info	*sta, *next;
272	int s;
273
274	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
275		printf("wihap_shutdown: sc=0x%x whi=0x%x\n",
276		    (int)sc, (int)whi);
277
278	if (!(whi->apflags & WIHAPFL_ACTIVE))
279		return;
280
281	/* XXX: I read somewhere you can deauth all the stations with
282	 * a single broadcast.  Maybe try that someday.
283	 */
284
285	s = splimp();
286	sta = LIST_FIRST(&whi->sta_list);
287	while (sta) {
288
289		timeout_del(&sta->tmo);
290
291		if (sc->wi_flags & WI_FLAGS_ATTACHED) {
292			/* Disassociate station. */
293			if (sta->flags & WI_SIFLAGS_ASSOC)
294				wihap_sta_disassoc(sc, sta,
295				    IEEE80211_REASON_ASSOC_LEAVE);
296			/* Deauth station. */
297			if (sta->flags & WI_SIFLAGS_AUTHEN)
298				wihap_sta_deauth(sc, sta->addr,
299				    IEEE80211_REASON_AUTH_LEAVE);
300		}
301
302		/* Delete the structure. */
303		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
304			printf("wihap_shutdown: FREE(sta=0x%x)\n", (int)sta);
305		next = LIST_NEXT(sta, list);
306		FREE(sta, M_DEVBUF);
307		sta = next;
308	}
309	splx(s);
310
311	whi->apflags = 0;
312}
313
314/* sta_hash_func()
315 * Hash function for finding stations from ethernet address.
316 */
317static __inline int
318sta_hash_func(u_int8_t addr[])
319{
320	return ((addr[3] + addr[4] + addr[5]) % WI_STA_HASH_SIZE);
321}
322
323/* addr_cmp():  Maybe this is a faster way to compare addresses? */
324static __inline int
325addr_cmp(u_int8_t a[], u_int8_t b[])
326{
327	return (*(u_int16_t *)(a + 4) == *(u_int16_t *)(b + 4) &&
328		*(u_int32_t *)(a    ) == *(u_int32_t *)(b));
329}
330
331void
332wihap_sta_timeout(void *v)
333{
334	struct wihap_sta_info	*sta = v;
335	struct wi_softc		*sc = sta->sc;
336	struct wihap_info	*whi = &sc->wi_hostap_info;
337	int	s;
338
339	s = splsoftnet();
340
341	if (sta->flags & WI_SIFLAGS_ASSOC) {
342		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
343			printf("wihap_timer: disassoc due to inactivity: %s\n",
344			    ether_sprintf(sta->addr));
345
346		/* Disassoc station. */
347		wihap_sta_disassoc(sc, sta, IEEE80211_REASON_ASSOC_EXPIRE);
348		sta->flags &= ~WI_SIFLAGS_ASSOC;
349
350		timeout_add(&sta->tmo, hz * whi->inactivity_time);
351
352	} else if (sta->flags & WI_SIFLAGS_AUTHEN) {
353
354		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
355			printf("wihap_timer: deauth due to inactivity: %s\n",
356			    ether_sprintf(sta->addr));
357
358		/* Deauthenticate station. */
359		wihap_sta_deauth(sc, sta->addr, IEEE80211_REASON_AUTH_EXPIRE);
360		sta->flags &= ~WI_SIFLAGS_AUTHEN;
361
362		/* Delete the station if it's not permanent. */
363		if (!(sta->flags & WI_SIFLAGS_PERM))
364			wihap_sta_delete(sta);
365	}
366	splx(s);
367}
368
369/* wihap_sta_delete()
370 * Delete a single station and free up its data structure.
371 */
372void
373wihap_sta_delete(struct wihap_sta_info *sta)
374{
375	struct wi_softc		*sc = sta->sc;
376	struct wihap_info	*whi = &sc->wi_hostap_info;
377	int i = sta->asid - 0xc001;
378
379	timeout_del(&sta->tmo);
380
381	whi->asid_inuse_mask[i >> 4] &= ~(1UL << (i & 0xf));
382
383	LIST_REMOVE(sta, list);
384	LIST_REMOVE(sta, hash);
385	FREE(sta, M_DEVBUF);
386	whi->n_stations--;
387}
388
389/* wihap_sta_alloc()
390 *
391 *	Create a new station data structure and put it in the list
392 *	and hash table.
393 */
394struct wihap_sta_info *
395wihap_sta_alloc(struct wi_softc *sc, u_int8_t *addr)
396{
397	struct wihap_info	*whi = &sc->wi_hostap_info;
398	struct wihap_sta_info	*sta;
399	int i, hash = sta_hash_func(addr);
400
401	/* Allocate structure. */
402	MALLOC(sta, struct wihap_sta_info *, sizeof(struct wihap_sta_info),
403	    M_DEVBUF, M_NOWAIT);
404	if (sta == NULL)
405		return(NULL);
406
407	bzero(sta, sizeof(struct wihap_sta_info));
408
409	/* Allocate an ASID. */
410	i=hash<<4;
411	while (whi->asid_inuse_mask[i >> 4] & (1UL << (i & 0xf)))
412		i = (i == (WI_STA_HASH_SIZE << 4) - 1) ? 0 : (i + 1);
413	whi->asid_inuse_mask[i >> 4] |= (1UL << (i & 0xf));
414	sta->asid = 0xc001 + i;
415
416	/* Insert in list and hash list. */
417	LIST_INSERT_HEAD(&whi->sta_list, sta, list);
418	LIST_INSERT_HEAD(&whi->sta_hash[hash], sta, hash);
419
420	sta->sc = sc;
421	whi->n_stations++;
422	bcopy(addr, &sta->addr, ETHER_ADDR_LEN);
423	timeout_set(&sta->tmo, wihap_sta_timeout, sta);
424	timeout_add(&sta->tmo, hz * whi->inactivity_time);
425
426	return(sta);
427}
428
429/* wihap_sta_find()
430 *
431 *	Find station structure given address.
432 */
433struct wihap_sta_info *
434wihap_sta_find(struct wihap_info *whi, u_int8_t *addr)
435{
436	int i;
437	struct wihap_sta_info *sta;
438
439	i = sta_hash_func(addr);
440	LIST_FOREACH(sta, &whi->sta_hash[i], hash)
441		if (addr_cmp(addr, sta->addr))
442			return sta;
443
444	return(NULL);
445}
446
447static __inline int
448wihap_check_rates(struct wihap_sta_info *sta, u_int8_t rates[], int rates_len)
449{
450	struct wi_softc *sc = sta->sc;
451	int	i;
452
453	sta->rates = 0;
454	sta->tx_max_rate = 0;
455	for (i = 0; i < rates_len; i++)
456		switch (rates[i] & 0x7f) {
457		case 0x02:
458			sta->rates |= WI_SUPPRATES_1M;
459			break;
460		case 0x04:
461			sta->rates |= WI_SUPPRATES_2M;
462			if (sta->tx_max_rate < 1)
463				sta->tx_max_rate = 1;
464			break;
465		case 0x0b:
466			sta->rates |= WI_SUPPRATES_5M;
467			if (sta->tx_max_rate < 2)
468				sta->tx_max_rate = 2;
469			break;
470		case 0x16:
471			sta->rates |= WI_SUPPRATES_11M;
472			sta->tx_max_rate = 3;
473			break;
474		}
475
476	sta->rates &= sc->wi_supprates;
477	sta->tx_curr_rate = sta->tx_max_rate;
478
479	return (sta->rates == 0 ? -1 : 0);
480}
481
482
483/* wihap_auth_req()
484 *
485 *	Handle incoming authentication request.  Only handle OPEN
486 *	requests.
487 */
488void
489wihap_auth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
490    caddr_t pkt, int len)
491{
492	struct wihap_info	*whi = &sc->wi_hostap_info;
493	struct wihap_sta_info	*sta;
494
495	u_int16_t		algo;
496	u_int16_t		seq;
497	u_int16_t		status;
498	int			challenge_len;
499	u_int8_t		challenge[128];
500
501	struct wi_80211_hdr	*resp_hdr;
502
503	if (len<6)
504		return;
505
506	/* Break open packet. */
507	algo = take_hword(&pkt, &len);
508	seq = take_hword(&pkt, &len);
509	status = take_hword(&pkt, &len);
510	challenge_len=0;
511	if (len > 0 && (challenge_len = take_tlv(&pkt, &len,
512	    IEEE80211_ELEMID_CHALLENGE, challenge, sizeof(challenge)))<0)
513		return;
514
515	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
516		printf("wihap_auth_req: from station: %s\n",
517		    ether_sprintf(rxfrm->wi_addr2));
518
519	switch (algo) {
520	case IEEE80211_AUTH_ALG_OPEN:
521		if (seq != 1) {
522			status = IEEE80211_STATUS_SEQUENCE;
523			goto fail;
524		}
525		challenge_len=0;
526		break;
527	case IEEE80211_AUTH_ALG_SHARED:
528		/* NOT YET */
529	default:
530		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
531			printf("wihap_auth_req: algorithm unsupported: %d\n",
532			    algo);
533		status = IEEE80211_STATUS_ALG;
534		goto fail;
535	}
536
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	sta->flags |= WI_SIFLAGS_AUTHEN;
567	timeout_add(&sta->tmo, hz * whi->inactivity_time);
568	status = IEEE80211_STATUS_SUCCESS;
569
570fail:
571	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
572		printf("wihap_auth_req: returns status=0x%x\n", status);
573
574	/* Send response. */
575	resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
576	bzero(resp_hdr, sizeof(struct wi_80211_hdr));
577	resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_AUTH);
578	bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN);
579	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
580	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
581
582	pkt = &sc->wi_txbuf[sizeof(struct wi_80211_hdr)];
583	put_hword(&pkt, algo);
584	put_hword(&pkt, 2);
585	put_hword(&pkt, status);
586	if (challenge_len > 0)
587		put_tlv(&pkt, IEEE80211_ELEMID_CHALLENGE,
588			challenge, challenge_len);
589
590	wi_mgmt_xmit(sc, sc->wi_txbuf,
591		     6 + sizeof(struct wi_80211_hdr) +
592		     (challenge_len>0 ? challenge_len + 2 : 0));
593}
594
595
596/* wihap_assoc_req()
597 *
598 *	Handle incoming association and reassociation requests.
599 */
600void
601wihap_assoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
602		caddr_t pkt, int len)
603{
604	struct wihap_info	*whi = &sc->wi_hostap_info;
605	struct wihap_sta_info	*sta;
606	struct wi_80211_hdr	*resp_hdr;
607	u_int16_t		capinfo;
608	u_int16_t		lstintvl;
609	u_int8_t		rates[8];
610	int			ssid_len, rates_len;
611	struct ieee80211_nwid	ssid;
612	u_int16_t		status;
613	u_int16_t		asid = 0;
614
615	if (len < 8)
616		return;
617
618	/* Pull out request parameters. */
619	capinfo = take_hword(&pkt, &len);
620	lstintvl = take_hword(&pkt, &len);
621	if ((ssid_len = take_tlv(&pkt, &len, IEEE80211_ELEMID_SSID,
622	    ssid.i_nwid, sizeof(ssid)))<0)
623		return;
624	ssid.i_len = ssid_len;
625	if ((rates_len = take_tlv(&pkt, &len, IEEE80211_ELEMID_RATES,
626	    rates, sizeof(rates)))<0)
627		return;
628
629	if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_STYPE)) ==
630	    htole16(WI_STYPE_MGMT_REASREQ)) {
631		/* Reassociation Request-- * Current AP.  (Ignore?) */
632		if (len < 6)
633			return;
634	}
635
636	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
637		printf("wihap_assoc_req: from station %s\n",
638		    ether_sprintf(rxfrm->wi_addr2));
639
640	/* If SSID doesn't match, simply drop. */
641	if (sc->wi_net_name.i_len != ssid.i_len ||
642	    memcmp(sc->wi_net_name.i_nwid, ssid.i_nwid, ssid.i_len)) {
643
644		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
645			printf("wihap_assoc_req: bad ssid: '%.*s' != '%.*s'\n",
646			    ssid.i_len, ssid.i_nwid, sc->wi_net_name.i_len,
647			    sc->wi_net_name.i_nwid);
648		return;
649	}
650
651	/* Is this station authenticated yet? */
652	sta = wihap_sta_find(whi, rxfrm->wi_addr2);
653	if (sta == NULL || !(sta->flags & WI_SIFLAGS_AUTHEN)) {
654		wihap_sta_deauth(sc, rxfrm->wi_addr2,
655		    IEEE80211_REASON_NOT_AUTHED);
656		return;
657	}
658
659	/* Check capinfo.
660	 * Check for ESS, not IBSS.
661	 * Check WEP/PRIVACY flags match.  XXX: WEP doesn't work for host AP.
662	 * Refuse stations requesting to be put on CF-polling list.
663	 */
664	if ((capinfo & (IEEE80211_CAPINFO_ESS | IEEE80211_CAPINFO_IBSS)) !=
665	    IEEE80211_CAPINFO_ESS ||
666	    (sc->wi_use_wep && !(capinfo & IEEE80211_CAPINFO_PRIVACY)) ||
667	    (!sc->wi_use_wep && (capinfo & IEEE80211_CAPINFO_PRIVACY)) ||
668	    (capinfo & (IEEE80211_CAPINFO_CF_POLLABLE |
669		IEEE80211_CAPINFO_CF_POLLREQ)) ==
670	    IEEE80211_CAPINFO_CF_POLLABLE) {
671
672		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
673			printf("wihap_assoc_req: capinfo mismatch: "
674			    "capinfo=0x%x\n", capinfo);
675
676		status = IEEE80211_STATUS_CAPINFO;
677		goto fail;
678	}
679	sta->capinfo = capinfo;
680
681	/* Check supported rates against ours. */
682	if (wihap_check_rates(sta, rates, rates_len)<0) {
683		status = IEEE80211_STATUS_RATES;
684		goto fail;
685	}
686
687	/* Use ASID is allocated by whi_sta_alloc(). */
688	asid = sta->asid;
689
690	if (sta->flags & WI_SIFLAGS_ASSOC) {
691		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
692			printf("wihap_assoc_req: already assoc'ed?\n");
693	}
694
695	sta->flags |= WI_SIFLAGS_ASSOC;
696	timeout_add(&sta->tmo, hz * whi->inactivity_time);
697	status = IEEE80211_STATUS_SUCCESS;
698
699fail:
700	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
701		printf("wihap_assoc_req: returns status=0x%x\n", status);
702
703	/* Send response. */
704	resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf;
705	bzero(resp_hdr, sizeof(struct wi_80211_hdr));
706	resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_ASRESP);
707	pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr);
708
709	bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN);
710	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN);
711	bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN);
712
713	put_hword(&pkt, capinfo);
714	put_hword(&pkt, status);
715	put_hword(&pkt, asid);
716	rates_len = put_rates(&pkt, sc->wi_supprates);
717
718	wi_mgmt_xmit(sc, sc->wi_txbuf,
719	    8 + rates_len + sizeof(struct wi_80211_hdr));
720}
721
722/* wihap_deauth_req()
723 *
724 *	Handle deauthentication requests.  Delete the station.
725 */
726void
727wihap_deauth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
728		 caddr_t pkt, int len)
729{
730	struct wihap_info	*whi = &sc->wi_hostap_info;
731	struct wihap_sta_info	*sta;
732	u_int16_t		reason;
733
734	if (len<2)
735		return;
736
737	reason = take_hword(&pkt, &len);
738
739	sta = wihap_sta_find(whi, rxfrm->wi_addr2);
740	if (sta == NULL) {
741		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
742			printf("wihap_deauth_req: unknown station: 6D\n",
743			    rxfrm->wi_addr2, ":");
744	}
745	else
746		wihap_sta_delete(sta);
747}
748
749/* wihap_disassoc_req()
750 *
751 *	Handle disassociation requests.  Just reset the assoc flag.
752 *	We'll free up the station resources when we get a deauth
753 *	request or when it times out.
754 */
755void
756wihap_disassoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
757    caddr_t pkt, int len)
758{
759	struct wihap_info	*whi = &sc->wi_hostap_info;
760	struct wihap_sta_info	*sta;
761	u_int16_t		reason;
762
763	if (len < 2)
764		return;
765
766	reason = take_hword(&pkt, &len);
767
768	sta = wihap_sta_find(whi, rxfrm->wi_addr2);
769	if (sta == NULL) {
770		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
771			printf("wihap_disassoc_req: unknown station: 6D\n",
772			    rxfrm->wi_addr2, ":");
773	}
774	else if (!(sta->flags & WI_SIFLAGS_AUTHEN)) {
775		/*
776		 * If station is not authenticated, send deauthentication
777		 * frame.
778		 */
779		wihap_sta_deauth(sc, rxfrm->wi_addr2,
780		    IEEE80211_REASON_NOT_AUTHED);
781		return;
782	}
783	else
784		sta->flags &= ~WI_SIFLAGS_ASSOC;
785}
786
787/* wihap_debug_frame_type()
788 *
789 * Print out frame type.  Used in early debugging.
790 */
791static __inline void
792wihap_debug_frame_type(struct wi_frame *rxfrm)
793{
794	printf("wihap_mgmt_input: len=%d ", letoh16(rxfrm->wi_dat_len));
795
796	if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_FTYPE)) ==
797	    htole16(WI_FTYPE_MGMT)) {
798
799		printf("MGMT: ");
800
801		switch (letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) {
802		case WI_STYPE_MGMT_ASREQ:
803			printf("assoc req: \n");
804			break;
805		case WI_STYPE_MGMT_ASRESP:
806			printf("assoc resp: \n");
807			break;
808		case WI_STYPE_MGMT_REASREQ:
809			printf("reassoc req: \n");
810			break;
811		case WI_STYPE_MGMT_REASRESP:
812			printf("reassoc resp: \n");
813			break;
814		case WI_STYPE_MGMT_PROBEREQ:
815			printf("probe req: \n");
816			break;
817		case WI_STYPE_MGMT_PROBERESP:
818			printf("probe resp: \n");
819			break;
820		case WI_STYPE_MGMT_BEACON:
821			printf("beacon: \n");
822			break;
823		case WI_STYPE_MGMT_ATIM:
824			printf("ann traf ind \n");
825			break;
826		case WI_STYPE_MGMT_DISAS:
827			printf("disassociation: \n");
828			break;
829		case WI_STYPE_MGMT_AUTH:
830			printf("auth: \n");
831			break;
832		case WI_STYPE_MGMT_DEAUTH:
833			printf("deauth: \n");
834			break;
835		default:
836			printf("unknown (stype=0x%x)\n",
837			    letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE);
838		}
839
840	}
841	else {
842		printf("ftype=0x%x (ctl=0x%x)\n",
843		    letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_FTYPE,
844		    letoh16(rxfrm->wi_frame_ctl));
845	}
846}
847
848/*
849 * wihap_mgmt_input:
850 *
851 *	Called for each management frame received in host ap mode.
852 *	wihap_mgmt_input() is expected to free the mbuf.
853 */
854void
855wihap_mgmt_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m)
856{
857	caddr_t	pkt;
858	int	s, len;
859
860	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
861		wihap_debug_frame_type(rxfrm);
862
863	pkt = mtod(m, caddr_t) + WI_802_11_OFFSET_RAW;
864	len = m->m_len - WI_802_11_OFFSET_RAW;
865
866	if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_FTYPE)) ==
867	    htole16(WI_FTYPE_MGMT)) {
868
869		/* any of the following will mess w/ the station list */
870		s = splsoftclock();
871		switch (letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) {
872		case WI_STYPE_MGMT_ASREQ:
873			wihap_assoc_req(sc, rxfrm, pkt, len);
874			break;
875		case WI_STYPE_MGMT_ASRESP:
876			break;
877		case WI_STYPE_MGMT_REASREQ:
878			wihap_assoc_req(sc, rxfrm, pkt, len);
879			break;
880		case WI_STYPE_MGMT_REASRESP:
881			break;
882		case WI_STYPE_MGMT_PROBEREQ:
883			break;
884		case WI_STYPE_MGMT_PROBERESP:
885			break;
886		case WI_STYPE_MGMT_BEACON:
887			break;
888		case WI_STYPE_MGMT_ATIM:
889			break;
890		case WI_STYPE_MGMT_DISAS:
891			wihap_disassoc_req(sc, rxfrm, pkt, len);
892			break;
893		case WI_STYPE_MGMT_AUTH:
894			wihap_auth_req(sc, rxfrm, pkt, len);
895			break;
896		case WI_STYPE_MGMT_DEAUTH:
897			wihap_deauth_req(sc, rxfrm, pkt, len);
898			break;
899		}
900		splx(s);
901	}
902
903	m_freem(m);
904}
905
906/* wihap_sta_is_assoc()
907 *
908 *	Determine if a station is assoc'ed.  Update its activity
909 *	counter as a side-effect.
910 */
911int
912wihap_sta_is_assoc(struct wihap_info *whi, u_int8_t addr[])
913{
914	struct wihap_sta_info *sta;
915
916	sta = wihap_sta_find(whi, addr);
917	if (sta != NULL && (sta->flags & WI_SIFLAGS_ASSOC)) {
918		/* Keep it active. */
919		timeout_add(&sta->tmo, hz * whi->inactivity_time);
920		return(1);
921	}
922
923	return(0);
924}
925
926/* wihap_check_tx()
927 *
928 *	Determine if a station is assoc'ed, get its tx rate, and update
929 *	its activity.
930 */
931int
932wihap_check_tx(struct wihap_info *whi, u_int8_t addr[], u_int8_t *txrate)
933{
934	struct wihap_sta_info *sta;
935	static u_int8_t txratetable[] = { 10, 20, 55, 110 };
936	int s;
937
938	if (addr[0] & 0x01) {
939		*txrate = 0; /* XXX: multicast rate? */
940		return(1);
941	}
942
943	s = splsoftclock();
944	sta = wihap_sta_find(whi, addr);
945	if (sta != NULL && (sta->flags & WI_SIFLAGS_ASSOC)) {
946		/* Keep it active. */
947		timeout_add(&sta->tmo, hz * whi->inactivity_time);
948		*txrate = txratetable[sta->tx_curr_rate];
949		splx(s);
950		return(1);
951	}
952	splx(s);
953
954	return(0);
955}
956
957/*
958 * wihap_data_input()
959 *
960 *	Handle all data input on interface when in Host AP mode.
961 *	Some packets are destined for this machine, others are
962 *	repeated to other stations.
963 *
964 *	If wihap_data_input() returns a non-zero, it has processed
965 *	the packet and will free the mbuf.
966 */
967int
968wihap_data_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m)
969{
970	struct ifnet		*ifp = &sc->arpcom.ac_if;
971	struct wihap_info	*whi = &sc->wi_hostap_info;
972	struct wihap_sta_info	*sta;
973	int			mcast, s;
974
975	/* TODS flag must be set. */
976	if (!(rxfrm->wi_frame_ctl & htole16(WI_FCTL_TODS))) {
977		if (ifp->if_flags & IFF_DEBUG)
978			printf("wihap_data_input: no TODS src=%s\n",
979			    ether_sprintf(rxfrm->wi_addr2));
980		m_freem(m);
981		return(1);
982	}
983
984	/* Check BSSID. (Is this necessary?) */
985	if (!addr_cmp(rxfrm->wi_addr1, sc->arpcom.ac_enaddr)) {
986		if (ifp->if_flags & IFF_DEBUG)
987			printf("wihap_data_input: incorrect bss: %s\n",
988			    ether_sprintf(rxfrm->wi_addr1));
989		m_freem(m);
990		return(1);
991	}
992
993	s = splsoftclock();
994
995	/* Find source station. */
996	sta = wihap_sta_find(whi, rxfrm->wi_addr2);
997
998	/* Source station must be associated. */
999	if (sta == NULL || !(sta->flags & WI_SIFLAGS_ASSOC)) {
1000		if (ifp->if_flags & IFF_DEBUG)
1001			printf("wihap_data_input: dropping unassoc src %s\n",
1002			    ether_sprintf(rxfrm->wi_addr2));
1003		splx(s);
1004		m_freem(m);
1005		return(1);
1006	}
1007
1008	timeout_add(&sta->tmo, hz * whi->inactivity_time);
1009	sta->sig_info = letoh16(rxfrm->wi_q_info);
1010
1011	splx(s);
1012
1013	/* Repeat this packet to BSS? */
1014	mcast = (rxfrm->wi_addr3[0] & 0x01) != 0;
1015	if (mcast || wihap_sta_is_assoc(whi, rxfrm->wi_addr3)) {
1016
1017		/* If it's multicast, make a copy.
1018		 */
1019		if (mcast) {
1020			m = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
1021			if (m == NULL)
1022				return(0);
1023			m->m_flags |= M_MCAST; /* XXX */
1024		}
1025
1026		/* Queue up for repeating.
1027		 */
1028		if (IF_QFULL(&ifp->if_snd)) {
1029			IF_DROP(&ifp->if_snd);
1030			m_freem(m);
1031		}
1032		else {
1033			ifp->if_obytes += m->m_pkthdr.len;
1034			if (m->m_flags & M_MCAST)
1035				ifp->if_omcasts++;
1036			IF_ENQUEUE(&ifp->if_snd, m);
1037			if ((ifp->if_flags & IFF_OACTIVE) == 0)
1038				(*ifp->if_start)(ifp);
1039		}
1040		return (!mcast);
1041	}
1042
1043	return (0);
1044}
1045
1046/* wihap_ioctl()
1047 *
1048 *	Handle Host AP specific ioctls.  Called from wi_ioctl().
1049 */
1050int
1051wihap_ioctl(struct wi_softc *sc, u_long command, caddr_t data)
1052{
1053	struct proc		*p = curproc;
1054	struct ifreq		*ifr = (struct ifreq *) data;
1055	struct wihap_info	*whi = &sc->wi_hostap_info;
1056	struct wihap_sta_info	*sta;
1057	struct hostap_getall	reqall;
1058	struct hostap_sta	reqsta;
1059	struct hostap_sta	stabuf;
1060	int			s, error = 0, n, flag;
1061
1062	if (!(sc->arpcom.ac_if.if_flags & IFF_RUNNING))
1063		return ENODEV;
1064
1065	switch (command) {
1066	case SIOCHOSTAP_DEL:
1067		if ((error = suser(p->p_ucred, &p->p_acflag)))
1068			break;
1069		if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1070			break;
1071		s = splimp();
1072		sta = wihap_sta_find(whi, reqsta.addr);
1073		if (sta == NULL)
1074			error = ENOENT;
1075		else {
1076			/* Disassociate station. */
1077			if (sta->flags & WI_SIFLAGS_ASSOC)
1078				wihap_sta_disassoc(sc, sta,
1079				    IEEE80211_REASON_ASSOC_LEAVE);
1080			/* Deauth station. */
1081			if (sta->flags & WI_SIFLAGS_AUTHEN)
1082				wihap_sta_deauth(sc, sta->addr,
1083				    IEEE80211_REASON_AUTH_LEAVE);
1084
1085			wihap_sta_delete(sta);
1086		}
1087		splx(s);
1088		break;
1089
1090	case SIOCHOSTAP_GET:
1091		if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1092			break;
1093		s = splimp();
1094		sta = wihap_sta_find(whi, reqsta.addr);
1095		if (sta == NULL)
1096			error = ENOENT;
1097		else {
1098			reqsta.flags = sta->flags;
1099			reqsta.asid = sta->asid;
1100			reqsta.capinfo = sta->capinfo;
1101			reqsta.sig_info = sta->sig_info;
1102			reqsta.rates = sta->rates;
1103
1104			error = copyout(&reqsta, ifr->ifr_data,
1105			    sizeof(reqsta));
1106		}
1107		splx(s);
1108		break;
1109
1110	case SIOCHOSTAP_ADD:
1111		if ((error = suser(p->p_ucred, &p->p_acflag)))
1112			break;
1113		if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1114			break;
1115		s = splimp();
1116		sta = wihap_sta_find(whi, reqsta.addr);
1117		if (sta != NULL) {
1118			error = EEXIST;
1119			splx(s);
1120			break;
1121		}
1122		if (whi->n_stations >= WIHAP_MAX_STATIONS) {
1123			error = ENOSPC;
1124			splx(s);
1125			break;
1126		}
1127		sta = wihap_sta_alloc(sc, reqsta.addr);
1128		sta->flags = reqsta.flags;
1129		timeout_add(&sta->tmo, hz * whi->inactivity_time);
1130		splx(s);
1131		break;
1132
1133	case SIOCHOSTAP_SFLAGS:
1134		if ((error = suser(p->p_ucred, &p->p_acflag)))
1135			break;
1136		if ((error = copyin(ifr->ifr_data, &flag, sizeof(int))))
1137			break;
1138
1139		whi->apflags = (whi->apflags & WIHAPFL_CANTCHANGE) |
1140		    (flag & ~WIHAPFL_CANTCHANGE);
1141		break;
1142
1143	case SIOCHOSTAP_GFLAGS:
1144		flag = (int) whi->apflags;
1145		error = copyout(&flag, ifr->ifr_data, sizeof(int));
1146		break;
1147
1148	case SIOCHOSTAP_GETALL:
1149		if ((error = copyin(ifr->ifr_data, &reqall, sizeof(reqall))))
1150			break;
1151
1152		reqall.nstations = whi->n_stations;
1153		n = 0;
1154		s = splimp();
1155		sta = LIST_FIRST(&whi->sta_list);
1156		while (sta && reqall.size >= n+sizeof(struct hostap_sta)) {
1157
1158			bcopy(sta->addr, stabuf.addr, ETHER_ADDR_LEN);
1159			stabuf.asid = sta->asid;
1160			stabuf.flags = sta->flags;
1161			stabuf.capinfo = sta->capinfo;
1162			stabuf.sig_info = sta->sig_info;
1163			stabuf.rates = sta->rates;
1164
1165			error = copyout(&stabuf, (caddr_t) reqall.addr + n,
1166			    sizeof(struct hostap_sta));
1167			if (error)
1168				break;
1169
1170			sta = LIST_NEXT(sta, list);
1171			n += sizeof(struct hostap_sta);
1172		}
1173		splx(s);
1174
1175		if (!error)
1176			error = copyout(&reqall, ifr->ifr_data,
1177			    sizeof(reqall));
1178		break;
1179	default:
1180		printf("wihap_ioctl: i shouldn't get other ioctls!\n");
1181		error = EINVAL;
1182	}
1183
1184	return(error);
1185}
1186