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