Line data Source code
1 : /* $OpenBSD: if_wi_hostap.c,v 1.52 2018/02/19 08:59:52 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 0 : take_hword(caddr_t *ppkt, int *plen)
99 : {
100 0 : u_int16_t s = letoh16(* (u_int16_t *) *ppkt);
101 0 : *ppkt += sizeof(u_int16_t);
102 0 : *plen -= sizeof(u_int16_t);
103 0 : 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 0 : take_tlv(caddr_t *ppkt, int *plen, int id_expect, void *dst, int maxlen)
113 : {
114 : u_int8_t id, len;
115 :
116 0 : if (*plen < 2)
117 0 : return -1;
118 :
119 0 : id = ((u_int8_t *)*ppkt)[0];
120 0 : len = ((u_int8_t *)*ppkt)[1];
121 :
122 0 : if (id != id_expect || *plen < len+2 || maxlen < len)
123 0 : return -1;
124 :
125 0 : bcopy(*ppkt + 2, dst, len);
126 0 : *plen -= 2 + len;
127 0 : *ppkt += 2 + len;
128 :
129 0 : return (len);
130 0 : }
131 :
132 : /* put_hword()
133 : * Put half-word element into management frames.
134 : */
135 : static __inline void
136 0 : put_hword(caddr_t *ppkt, u_int16_t s)
137 : {
138 0 : * (u_int16_t *) *ppkt = htole16(s);
139 0 : *ppkt += sizeof(u_int16_t);
140 0 : }
141 :
142 : /* put_tlv()
143 : * Put TLV elements into management frames.
144 : */
145 : static void
146 0 : put_tlv(caddr_t *ppkt, u_int8_t id, void *src, u_int8_t len)
147 : {
148 0 : (*ppkt)[0] = id;
149 0 : (*ppkt)[1] = len;
150 0 : bcopy(src, (*ppkt) + 2, len);
151 0 : *ppkt += 2 + len;
152 0 : }
153 :
154 : static int
155 0 : put_rates(caddr_t *ppkt, u_int16_t rates)
156 : {
157 0 : u_int8_t ratebuf[8];
158 : int len = 0;
159 :
160 0 : if (rates & WI_SUPPRATES_1M)
161 0 : ratebuf[len++] = 0x82;
162 0 : if (rates & WI_SUPPRATES_2M)
163 0 : ratebuf[len++] = 0x84;
164 0 : if (rates & WI_SUPPRATES_5M)
165 0 : ratebuf[len++] = 0x8b;
166 0 : if (rates & WI_SUPPRATES_11M)
167 0 : ratebuf[len++] = 0x96;
168 :
169 0 : put_tlv(ppkt, IEEE80211_ELEMID_RATES, ratebuf, len);
170 0 : return len;
171 0 : }
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 0 : wihap_init(struct wi_softc *sc)
180 : {
181 : int i;
182 0 : struct wihap_info *whi = &sc->wi_hostap_info;
183 :
184 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
185 0 : printf("wihap_init: sc=%p whi=%p\n", sc, whi);
186 :
187 0 : bzero(whi, sizeof(struct wihap_info));
188 :
189 0 : if (sc->wi_ptype != WI_PORTTYPE_HOSTAP)
190 0 : return;
191 :
192 0 : whi->apflags = WIHAPFL_ACTIVE;
193 :
194 0 : TAILQ_INIT(&whi->sta_list);
195 0 : for (i = 0; i < WI_STA_HASH_SIZE; i++)
196 0 : LIST_INIT(&whi->sta_hash[i]);
197 :
198 0 : whi->inactivity_time = WIHAP_DFLT_INACTIVITY_TIME;
199 0 : timeout_set(&whi->tmo, wihap_timeout, sc);
200 0 : }
201 :
202 : /* wihap_sta_disassoc()
203 : *
204 : * Send a disassociation frame to a specified station.
205 : */
206 : void
207 0 : 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 0 : caddr_t pkt;
211 :
212 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
213 0 : printf("Sending disassoc to sta %s\n", ether_sprintf(sta_addr));
214 :
215 : /* Send disassoc packet. */
216 0 : resp_hdr = (struct wi_80211_hdr *)sc->wi_txbuf;
217 0 : bzero(resp_hdr, sizeof(struct wi_80211_hdr));
218 0 : resp_hdr->frame_ctl = WI_FTYPE_MGMT | WI_STYPE_MGMT_DISAS;
219 0 : pkt = (caddr_t)&sc->wi_txbuf + sizeof(struct wi_80211_hdr);
220 :
221 0 : bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN);
222 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr2, IEEE80211_ADDR_LEN);
223 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr3, IEEE80211_ADDR_LEN);
224 :
225 0 : put_hword(&pkt, reason);
226 :
227 0 : wi_mgmt_xmit(sc, (caddr_t)&sc->wi_txbuf,
228 : 2 + sizeof(struct wi_80211_hdr));
229 0 : }
230 :
231 : /* wihap_sta_deauth()
232 : *
233 : * Send a deauthentication message to a specified station.
234 : */
235 : void
236 0 : 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 0 : caddr_t pkt;
240 :
241 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
242 0 : printf("Sending deauth to sta %s\n", ether_sprintf(sta_addr));
243 :
244 : /* Send deauth packet. */
245 0 : resp_hdr = (struct wi_80211_hdr *)sc->wi_txbuf;
246 0 : bzero(resp_hdr, sizeof(struct wi_80211_hdr));
247 0 : resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_DEAUTH);
248 0 : pkt = (caddr_t)&sc->wi_txbuf + sizeof(struct wi_80211_hdr);
249 :
250 0 : bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN);
251 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr2, IEEE80211_ADDR_LEN);
252 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr3, IEEE80211_ADDR_LEN);
253 :
254 0 : put_hword(&pkt, reason);
255 :
256 0 : wi_mgmt_xmit(sc, (caddr_t)&sc->wi_txbuf,
257 : 2 + sizeof(struct wi_80211_hdr));
258 0 : }
259 :
260 : /* wihap_shutdown()
261 : *
262 : * Disassociate all stations and free up data structures.
263 : */
264 : void
265 0 : wihap_shutdown(struct wi_softc *sc)
266 : {
267 0 : struct wihap_info *whi = &sc->wi_hostap_info;
268 : struct wihap_sta_info *sta, *next;
269 : int i, s;
270 :
271 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
272 0 : printf("wihap_shutdown: sc=%p whi=%p\n", sc, whi);
273 :
274 0 : if (!(whi->apflags & WIHAPFL_ACTIVE))
275 0 : return;
276 0 : whi->apflags = 0;
277 :
278 0 : s = splnet();
279 :
280 : /* Disable wihap inactivity timer. */
281 0 : timeout_del(&whi->tmo);
282 :
283 : /* Delete all stations from the list. */
284 0 : for (sta = TAILQ_FIRST(&whi->sta_list); sta != NULL; sta = next) {
285 0 : timeout_del(&sta->tmo);
286 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
287 0 : printf("wihap_shutdown: free(sta=%p)\n", sta);
288 0 : next = TAILQ_NEXT(sta, list);
289 0 : if (sta->challenge)
290 0 : free(sta->challenge, M_TEMP, 0);
291 0 : free(sta, M_DEVBUF, 0);
292 : }
293 0 : TAILQ_INIT(&whi->sta_list);
294 :
295 : /* Broadcast disassoc and deauth to all the stations. */
296 0 : if (sc->wi_flags & WI_FLAGS_ATTACHED) {
297 0 : for (i = 0; i < 5; i++) {
298 0 : wihap_sta_disassoc(sc, etherbroadcastaddr,
299 : IEEE80211_REASON_ASSOC_LEAVE);
300 0 : wihap_sta_deauth(sc, etherbroadcastaddr,
301 : IEEE80211_REASON_AUTH_LEAVE);
302 0 : DELAY(50);
303 : }
304 : }
305 :
306 0 : splx(s);
307 0 : }
308 :
309 : /* sta_hash_func()
310 : * Hash function for finding stations from ethernet address.
311 : */
312 : static __inline int
313 0 : sta_hash_func(u_int8_t addr[])
314 : {
315 0 : 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 0 : addr_cmp(u_int8_t a[], u_int8_t b[])
321 : {
322 0 : return (*(u_int16_t *)(a + 4) == *(u_int16_t *)(b + 4) &&
323 0 : *(u_int16_t *)(a + 2) == *(u_int16_t *)(b + 2) &&
324 0 : *(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 0 : wihap_sta_movetail(struct wihap_info *whi, struct wihap_sta_info *sta)
330 : {
331 0 : TAILQ_REMOVE(&whi->sta_list, sta, list);
332 0 : sta->flags &= ~WI_SIFLAGS_DEAD;
333 0 : TAILQ_INSERT_TAIL(&whi->sta_list, sta, list);
334 0 : }
335 :
336 : void
337 0 : wihap_timeout(void *v)
338 : {
339 0 : struct wi_softc *sc = v;
340 0 : struct wihap_info *whi = &sc->wi_hostap_info;
341 : struct wihap_sta_info *sta, *next;
342 : int i, s;
343 :
344 0 : s = splnet();
345 :
346 0 : for (i = 10, sta = TAILQ_FIRST(&whi->sta_list);
347 0 : i != 0 && sta != NULL && (sta->flags & WI_SIFLAGS_DEAD);
348 0 : i--, sta = next) {
349 0 : next = TAILQ_NEXT(sta, list);
350 0 : if (timeout_pending(&sta->tmo)) {
351 : /* Became alive again, move to end of list. */
352 0 : wihap_sta_movetail(whi, sta);
353 0 : } else if (sta->flags & WI_SIFLAGS_ASSOC) {
354 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
355 0 : printf("wihap_timeout: disassoc due to inactivity: %s\n",
356 0 : ether_sprintf(sta->addr));
357 :
358 : /* Disassoc station. */
359 0 : wihap_sta_disassoc(sc, sta->addr,
360 : IEEE80211_REASON_ASSOC_EXPIRE);
361 0 : 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 0 : wihap_sta_movetail(whi, sta);
369 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
370 0 : } else if (sta->flags & WI_SIFLAGS_AUTHEN) {
371 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
372 0 : printf("wihap_timeout: deauth due to inactivity: %s\n",
373 0 : ether_sprintf(sta->addr));
374 :
375 : /* Deauthenticate station. */
376 0 : wihap_sta_deauth(sc, sta->addr,
377 : IEEE80211_REASON_AUTH_EXPIRE);
378 0 : sta->flags &= ~WI_SIFLAGS_AUTHEN;
379 :
380 : /* Delete the station if it's not permanent. */
381 0 : if (sta->flags & WI_SIFLAGS_PERM)
382 0 : wihap_sta_movetail(whi, sta);
383 : else
384 0 : wihap_sta_delete(sta);
385 : }
386 : }
387 :
388 : /* Restart the timeout if there are still dead stations left. */
389 0 : sta = TAILQ_FIRST(&whi->sta_list);
390 0 : if (sta != NULL && (sta->flags & WI_SIFLAGS_DEAD))
391 0 : timeout_add(&whi->tmo, 1); /* still work left, requeue */
392 :
393 0 : splx(s);
394 0 : }
395 :
396 : void
397 0 : wihap_sta_timeout(void *v)
398 : {
399 0 : struct wihap_sta_info *sta = v;
400 0 : struct wi_softc *sc = sta->sc;
401 0 : struct wihap_info *whi = &sc->wi_hostap_info;
402 : int s;
403 :
404 0 : s = splnet();
405 :
406 : /* Mark sta as dead and move it to the head of the list. */
407 0 : TAILQ_REMOVE(&whi->sta_list, sta, list);
408 0 : sta->flags |= WI_SIFLAGS_DEAD;
409 0 : TAILQ_INSERT_HEAD(&whi->sta_list, sta, list);
410 :
411 : /* Add wihap timeout if we have not already done so. */
412 0 : if (!timeout_pending(&whi->tmo))
413 0 : timeout_add(&whi->tmo, hz / 10);
414 :
415 0 : splx(s);
416 0 : }
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 0 : wihap_sta_delete(struct wihap_sta_info *sta)
424 : {
425 0 : struct wi_softc *sc = sta->sc;
426 0 : struct wihap_info *whi = &sc->wi_hostap_info;
427 0 : int i = sta->asid - 0xc001;
428 :
429 0 : timeout_del(&sta->tmo);
430 :
431 0 : whi->asid_inuse_mask[i >> 4] &= ~(1UL << (i & 0xf));
432 :
433 0 : TAILQ_REMOVE(&whi->sta_list, sta, list);
434 0 : LIST_REMOVE(sta, hash);
435 0 : if (sta->challenge)
436 0 : free(sta->challenge, M_TEMP, 0);
437 0 : free(sta, M_DEVBUF, 0);
438 0 : whi->n_stations--;
439 0 : }
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 0 : wihap_sta_alloc(struct wi_softc *sc, u_int8_t *addr)
448 : {
449 0 : struct wihap_info *whi = &sc->wi_hostap_info;
450 : struct wihap_sta_info *sta;
451 0 : int i, hash = sta_hash_func(addr);
452 :
453 : /* Allocate structure. */
454 0 : sta = malloc(sizeof(*sta), M_DEVBUF, M_NOWAIT | M_ZERO);
455 0 : if (sta == NULL)
456 0 : return (NULL);
457 :
458 : /* Allocate an ASID. */
459 0 : i=hash<<4;
460 0 : while (whi->asid_inuse_mask[i >> 4] & (1UL << (i & 0xf)))
461 0 : i = (i == (WI_STA_HASH_SIZE << 4) - 1) ? 0 : (i + 1);
462 0 : whi->asid_inuse_mask[i >> 4] |= (1UL << (i & 0xf));
463 0 : sta->asid = 0xc001 + i;
464 :
465 : /* Insert in list and hash list. */
466 0 : TAILQ_INSERT_TAIL(&whi->sta_list, sta, list);
467 0 : LIST_INSERT_HEAD(&whi->sta_hash[hash], sta, hash);
468 :
469 0 : sta->sc = sc;
470 0 : whi->n_stations++;
471 0 : bcopy(addr, &sta->addr, ETHER_ADDR_LEN);
472 0 : timeout_set(&sta->tmo, wihap_sta_timeout, sta);
473 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
474 :
475 0 : return (sta);
476 0 : }
477 :
478 : /* wihap_sta_find()
479 : *
480 : * Find station structure given address.
481 : */
482 : struct wihap_sta_info *
483 0 : wihap_sta_find(struct wihap_info *whi, u_int8_t *addr)
484 : {
485 : int i;
486 : struct wihap_sta_info *sta;
487 :
488 0 : i = sta_hash_func(addr);
489 0 : LIST_FOREACH(sta, &whi->sta_hash[i], hash)
490 0 : if (addr_cmp(addr, sta->addr))
491 0 : return sta;
492 :
493 0 : return (NULL);
494 0 : }
495 :
496 : static __inline int
497 0 : wihap_check_rates(struct wihap_sta_info *sta, u_int8_t rates[], int rates_len)
498 : {
499 0 : struct wi_softc *sc = sta->sc;
500 : int i;
501 :
502 0 : sta->rates = 0;
503 0 : sta->tx_max_rate = 0;
504 0 : for (i = 0; i < rates_len; i++)
505 0 : switch (rates[i] & 0x7f) {
506 : case 0x02:
507 0 : sta->rates |= WI_SUPPRATES_1M;
508 0 : break;
509 : case 0x04:
510 0 : sta->rates |= WI_SUPPRATES_2M;
511 0 : if (sta->tx_max_rate < 1)
512 0 : sta->tx_max_rate = 1;
513 : break;
514 : case 0x0b:
515 0 : sta->rates |= WI_SUPPRATES_5M;
516 0 : if (sta->tx_max_rate < 2)
517 0 : sta->tx_max_rate = 2;
518 : break;
519 : case 0x16:
520 0 : sta->rates |= WI_SUPPRATES_11M;
521 0 : sta->tx_max_rate = 3;
522 0 : break;
523 : }
524 :
525 0 : sta->rates &= sc->wi_supprates;
526 0 : sta->tx_curr_rate = sta->tx_max_rate;
527 :
528 0 : return (sta->rates == 0 ? -1 : 0);
529 : }
530 :
531 :
532 : /* wihap_auth_req()
533 : *
534 : * Handle incoming authentication request.
535 : */
536 : void
537 0 : wihap_auth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
538 : caddr_t pkt, int len)
539 : {
540 0 : 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 0 : u_int32_t challenge[32];
549 :
550 : struct wi_80211_hdr *resp_hdr;
551 :
552 0 : if (len < 6) {
553 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
554 0 : printf("wihap_auth_req: station %s short request\n",
555 0 : ether_sprintf(rxfrm->wi_addr2));
556 0 : return;
557 : }
558 :
559 : /* Break open packet. */
560 0 : algo = take_hword(&pkt, &len);
561 0 : seq = take_hword(&pkt, &len);
562 0 : status = take_hword(&pkt, &len);
563 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
564 0 : printf("wihap_auth_req: station %s algo=0x%x seq=0x%x\n",
565 0 : ether_sprintf(rxfrm->wi_addr2), algo, seq);
566 :
567 : /* Ignore vendor private tlv (if any). */
568 0 : (void)take_tlv(&pkt, &len, IEEE80211_ELEMID_VENDOR, challenge,
569 : sizeof(challenge));
570 :
571 : challenge_len = 0;
572 0 : if (len > 0 && (challenge_len = take_tlv(&pkt, &len,
573 0 : IEEE80211_ELEMID_CHALLENGE, challenge, sizeof(challenge))) < 0) {
574 : status = IEEE80211_STATUS_CHALLENGE;
575 0 : goto fail;
576 : }
577 :
578 : /* Find or create station info. */
579 0 : sta = wihap_sta_find(whi, rxfrm->wi_addr2);
580 0 : if (sta == NULL) {
581 :
582 : /* Are we allowing new stations?
583 : */
584 0 : if (whi->apflags & WIHAPFL_MAC_FILT) {
585 : status = IEEE80211_STATUS_OTHER; /* XXX */
586 0 : goto fail;
587 : }
588 :
589 : /* Check for too many stations.
590 : */
591 0 : if (whi->n_stations >= WIHAP_MAX_STATIONS) {
592 : status = IEEE80211_STATUS_TOOMANY;
593 0 : goto fail;
594 : }
595 :
596 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
597 0 : printf("wihap_auth_req: new station\n");
598 :
599 : /* Create new station. */
600 0 : s = splnet();
601 0 : sta = wihap_sta_alloc(sc, rxfrm->wi_addr2);
602 0 : splx(s);
603 0 : if (sta == NULL) {
604 : /* Out of memory! */
605 : status = IEEE80211_STATUS_TOOMANY;
606 0 : goto fail;
607 : }
608 : }
609 0 : 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 0 : switch (algo) {
615 : case IEEE80211_AUTH_ALG_OPEN:
616 0 : if (sc->wi_authtype != IEEE80211_AUTH_OPEN) {
617 : status = IEEE80211_STATUS_ALG;
618 0 : goto fail;
619 : }
620 0 : if (seq != 1) {
621 : status = IEEE80211_STATUS_SEQUENCE;
622 0 : goto fail;
623 : }
624 : challenge_len = 0;
625 0 : sta->flags |= WI_SIFLAGS_AUTHEN;
626 0 : break;
627 : case IEEE80211_AUTH_ALG_SHARED:
628 0 : if (sc->wi_authtype != IEEE80211_AUTH_SHARED) {
629 : status = IEEE80211_STATUS_ALG;
630 0 : goto fail;
631 : }
632 0 : switch (seq) {
633 : case 1:
634 : /* Create a challenge frame. */
635 0 : if (!sta->challenge) {
636 0 : sta->challenge = malloc(128, M_TEMP, M_NOWAIT);
637 0 : if (!sta->challenge)
638 0 : return;
639 : }
640 0 : for (i = 0; i < 32; i++)
641 0 : challenge[i] = sta->challenge[i] =
642 0 : arc4random();
643 :
644 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
645 0 : printf("\tchallenge: 0x%x 0x%x ...\n",
646 0 : challenge[0], challenge[1]);
647 : challenge_len = 128;
648 0 : break;
649 : case 3:
650 0 : if (challenge_len != 128 || !sta->challenge ||
651 0 : !(letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_WEP)) {
652 : status = IEEE80211_STATUS_CHALLENGE;
653 0 : goto fail;
654 : }
655 :
656 0 : for (i=0; i<32; i++)
657 0 : if (sta->challenge[i] != challenge[i]) {
658 : status = IEEE80211_STATUS_CHALLENGE;
659 0 : goto fail;
660 : }
661 :
662 0 : sta->flags |= WI_SIFLAGS_AUTHEN;
663 0 : free(sta->challenge, M_TEMP, 0);
664 0 : sta->challenge = NULL;
665 : challenge_len = 0;
666 0 : break;
667 : default:
668 : status = IEEE80211_STATUS_SEQUENCE;
669 0 : goto fail;
670 : } /* switch (seq) */
671 : break;
672 : default:
673 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
674 0 : printf("wihap_auth_req: algorithm unsupported: 0x%x\n",
675 : algo);
676 : status = IEEE80211_STATUS_ALG;
677 0 : goto fail;
678 : } /* switch (algo) */
679 :
680 0 : status = IEEE80211_STATUS_SUCCESS;
681 :
682 : fail:
683 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
684 0 : printf("wihap_auth_req: returns status=0x%x\n", status);
685 :
686 : /* Send response. */
687 0 : resp_hdr = (struct wi_80211_hdr *)&sc->wi_txbuf;
688 0 : bzero(resp_hdr, sizeof(struct wi_80211_hdr));
689 0 : resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_AUTH);
690 0 : bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN);
691 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr2, IEEE80211_ADDR_LEN);
692 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr3, IEEE80211_ADDR_LEN);
693 :
694 0 : pkt = (caddr_t)&sc->wi_txbuf + sizeof(struct wi_80211_hdr);
695 0 : put_hword(&pkt, algo);
696 0 : put_hword(&pkt, seq + 1);
697 0 : put_hword(&pkt, status);
698 0 : if (challenge_len > 0)
699 0 : put_tlv(&pkt, IEEE80211_ELEMID_CHALLENGE,
700 0 : challenge, challenge_len);
701 :
702 0 : wi_mgmt_xmit(sc, (caddr_t)&sc->wi_txbuf,
703 0 : 6 + sizeof(struct wi_80211_hdr) +
704 0 : (challenge_len > 0 ? challenge_len + 2 : 0));
705 0 : }
706 :
707 :
708 : /* wihap_assoc_req()
709 : *
710 : * Handle incoming association and reassociation requests.
711 : */
712 : void
713 0 : wihap_assoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
714 : caddr_t pkt, int len)
715 : {
716 0 : 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 0 : u_int8_t rates[12];
722 : int ssid_len, rates_len;
723 0 : struct ieee80211_nwid ssid;
724 : u_int16_t status;
725 : u_int16_t asid = 0;
726 :
727 0 : if (len < 8)
728 0 : return;
729 :
730 : /* Pull out request parameters. */
731 0 : capinfo = take_hword(&pkt, &len);
732 0 : lstintvl = take_hword(&pkt, &len);
733 :
734 0 : if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_STYPE)) ==
735 : htole16(WI_STYPE_MGMT_REASREQ)) {
736 0 : if (len < 6)
737 0 : return;
738 : /* Eat the MAC address of the current AP */
739 0 : take_hword(&pkt, &len);
740 0 : take_hword(&pkt, &len);
741 0 : take_hword(&pkt, &len);
742 0 : }
743 :
744 0 : if ((ssid_len = take_tlv(&pkt, &len, IEEE80211_ELEMID_SSID,
745 0 : ssid.i_nwid, sizeof(ssid))) < 0)
746 0 : return;
747 0 : ssid.i_len = ssid_len;
748 0 : if ((rates_len = take_tlv(&pkt, &len, IEEE80211_ELEMID_RATES,
749 0 : rates, sizeof(rates))) < 0)
750 0 : return;
751 :
752 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
753 0 : printf("wihap_assoc_req: from station %s\n",
754 0 : ether_sprintf(rxfrm->wi_addr2));
755 :
756 : /* If SSID doesn't match, simply drop. */
757 0 : if (sc->wi_net_name.i_len != ssid.i_len ||
758 0 : memcmp(sc->wi_net_name.i_nwid, ssid.i_nwid, ssid.i_len)) {
759 :
760 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
761 0 : printf("wihap_assoc_req: bad ssid: '%.*s' != '%.*s'\n",
762 0 : ssid.i_len, ssid.i_nwid, sc->wi_net_name.i_len,
763 0 : sc->wi_net_name.i_nwid);
764 0 : return;
765 : }
766 :
767 : /* Is this station authenticated yet? */
768 0 : sta = wihap_sta_find(whi, rxfrm->wi_addr2);
769 0 : if (sta == NULL || !(sta->flags & WI_SIFLAGS_AUTHEN)) {
770 0 : wihap_sta_deauth(sc, rxfrm->wi_addr2,
771 : IEEE80211_REASON_NOT_AUTHED);
772 0 : return;
773 : }
774 :
775 : /* Check supported rates against ours. */
776 0 : if (wihap_check_rates(sta, rates, rates_len) < 0) {
777 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
778 0 : printf("wihap_assoc_req: rates mismatch.\n");
779 : status = IEEE80211_STATUS_BASIC_RATE;
780 0 : 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 0 : sta->capinfo = capinfo;
789 : status = IEEE80211_STATUS_CAPINFO;
790 0 : if ((capinfo & (IEEE80211_CAPINFO_ESS | IEEE80211_CAPINFO_IBSS)) !=
791 : IEEE80211_CAPINFO_ESS) {
792 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
793 0 : printf("wihap_assoc_req: capinfo: not ESS: "
794 : "capinfo=0x%x\n", capinfo);
795 : goto fail;
796 :
797 : }
798 0 : if ((sc->wi_use_wep && !(capinfo & IEEE80211_CAPINFO_PRIVACY)) ||
799 0 : (!sc->wi_use_wep && (capinfo & IEEE80211_CAPINFO_PRIVACY))) {
800 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
801 0 : printf("wihap_assoc_req: WEP flag mismatch: "
802 : "capinfo=0x%x\n", capinfo);
803 : goto fail;
804 : }
805 0 : if ((capinfo & (IEEE80211_CAPINFO_CF_POLLABLE |
806 0 : IEEE80211_CAPINFO_CF_POLLREQ)) == IEEE80211_CAPINFO_CF_POLLABLE) {
807 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
808 0 : 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 0 : asid = sta->asid;
815 :
816 0 : if (sta->flags & WI_SIFLAGS_ASSOC) {
817 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
818 0 : printf("wihap_assoc_req: already assoc'ed?\n");
819 : }
820 :
821 0 : sta->flags |= WI_SIFLAGS_ASSOC;
822 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
823 0 : status = IEEE80211_STATUS_SUCCESS;
824 :
825 : fail:
826 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
827 0 : printf("wihap_assoc_req: returns status=0x%x\n", status);
828 :
829 : /* Send response. */
830 0 : resp_hdr = (struct wi_80211_hdr *)&sc->wi_txbuf;
831 0 : bzero(resp_hdr, sizeof(struct wi_80211_hdr));
832 0 : resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_ASRESP);
833 0 : pkt = (caddr_t)&sc->wi_txbuf + sizeof(struct wi_80211_hdr);
834 :
835 0 : bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN);
836 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr2, IEEE80211_ADDR_LEN);
837 0 : bcopy(sc->sc_ic.ic_myaddr, resp_hdr->addr3, IEEE80211_ADDR_LEN);
838 :
839 0 : put_hword(&pkt, capinfo);
840 0 : put_hword(&pkt, status);
841 0 : put_hword(&pkt, asid);
842 0 : rates_len = put_rates(&pkt, sc->wi_supprates);
843 :
844 0 : wi_mgmt_xmit(sc, (caddr_t)&sc->wi_txbuf,
845 0 : 8 + rates_len + sizeof(struct wi_80211_hdr));
846 0 : }
847 :
848 : /* wihap_deauth_req()
849 : *
850 : * Handle deauthentication requests. Delete the station.
851 : */
852 : void
853 0 : wihap_deauth_req(struct wi_softc *sc, struct wi_frame *rxfrm,
854 : caddr_t pkt, int len)
855 : {
856 0 : struct wihap_info *whi = &sc->wi_hostap_info;
857 : struct wihap_sta_info *sta;
858 : u_int16_t reason;
859 :
860 0 : if (len<2)
861 0 : return;
862 :
863 0 : reason = take_hword(&pkt, &len);
864 :
865 0 : sta = wihap_sta_find(whi, rxfrm->wi_addr2);
866 0 : if (sta == NULL) {
867 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
868 0 : printf("wihap_deauth_req: unknown station: %s\n",
869 0 : ether_sprintf(rxfrm->wi_addr2));
870 : }
871 : else
872 0 : wihap_sta_delete(sta);
873 0 : }
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 0 : wihap_disassoc_req(struct wi_softc *sc, struct wi_frame *rxfrm,
883 : caddr_t pkt, int len)
884 : {
885 0 : struct wihap_info *whi = &sc->wi_hostap_info;
886 : struct wihap_sta_info *sta;
887 : u_int16_t reason;
888 :
889 0 : if (len < 2)
890 0 : return;
891 :
892 0 : reason = take_hword(&pkt, &len);
893 :
894 0 : sta = wihap_sta_find(whi, rxfrm->wi_addr2);
895 0 : if (sta == NULL) {
896 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
897 0 : printf("wihap_disassoc_req: unknown station: %s\n",
898 0 : ether_sprintf(rxfrm->wi_addr2));
899 : }
900 0 : else if (!(sta->flags & WI_SIFLAGS_AUTHEN)) {
901 : /*
902 : * If station is not authenticated, send deauthentication
903 : * frame.
904 : */
905 0 : wihap_sta_deauth(sc, rxfrm->wi_addr2,
906 : IEEE80211_REASON_NOT_AUTHED);
907 0 : return;
908 : }
909 : else
910 0 : sta->flags &= ~WI_SIFLAGS_ASSOC;
911 0 : }
912 :
913 : /* wihap_debug_frame_type()
914 : *
915 : * Print out frame type. Used in early debugging.
916 : */
917 : static __inline void
918 0 : wihap_debug_frame_type(struct wi_frame *rxfrm)
919 : {
920 0 : printf("wihap_mgmt_input: len=%d ", letoh16(rxfrm->wi_dat_len));
921 :
922 0 : if ((rxfrm->wi_frame_ctl & htole16(WI_FCTL_FTYPE)) ==
923 : htole16(WI_FTYPE_MGMT)) {
924 :
925 0 : printf("MGMT: ");
926 :
927 0 : switch (letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) {
928 : case WI_STYPE_MGMT_ASREQ:
929 0 : printf("assoc req: \n");
930 0 : break;
931 : case WI_STYPE_MGMT_ASRESP:
932 0 : printf("assoc resp: \n");
933 0 : break;
934 : case WI_STYPE_MGMT_REASREQ:
935 0 : printf("reassoc req: \n");
936 0 : break;
937 : case WI_STYPE_MGMT_REASRESP:
938 0 : printf("reassoc resp: \n");
939 0 : break;
940 : case WI_STYPE_MGMT_PROBEREQ:
941 0 : printf("probe req: \n");
942 0 : break;
943 : case WI_STYPE_MGMT_PROBERESP:
944 0 : printf("probe resp: \n");
945 0 : break;
946 : case WI_STYPE_MGMT_BEACON:
947 0 : printf("beacon: \n");
948 0 : break;
949 : case WI_STYPE_MGMT_ATIM:
950 0 : printf("ann traf ind \n");
951 0 : break;
952 : case WI_STYPE_MGMT_DISAS:
953 0 : printf("disassociation: \n");
954 0 : break;
955 : case WI_STYPE_MGMT_AUTH:
956 0 : printf("auth: \n");
957 0 : break;
958 : case WI_STYPE_MGMT_DEAUTH:
959 0 : printf("deauth: \n");
960 0 : break;
961 : default:
962 0 : printf("unknown (stype=0x%x)\n",
963 : letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE);
964 0 : }
965 :
966 : }
967 : else {
968 0 : 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 0 : }
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 0 : 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 0 : if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
987 0 : wihap_debug_frame_type(rxfrm);
988 :
989 0 : pkt = mtod(m, caddr_t) + WI_802_11_OFFSET_RAW;
990 0 : len = m->m_len - WI_802_11_OFFSET_RAW;
991 :
992 0 : 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 0 : s = splsoftclock();
997 0 : switch (letoh16(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) {
998 : case WI_STYPE_MGMT_ASREQ:
999 0 : wihap_assoc_req(sc, rxfrm, pkt, len);
1000 0 : break;
1001 : case WI_STYPE_MGMT_ASRESP:
1002 : break;
1003 : case WI_STYPE_MGMT_REASREQ:
1004 0 : wihap_assoc_req(sc, rxfrm, pkt, len);
1005 0 : 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 0 : wihap_disassoc_req(sc, rxfrm, pkt, len);
1018 0 : break;
1019 : case WI_STYPE_MGMT_AUTH:
1020 0 : wihap_auth_req(sc, rxfrm, pkt, len);
1021 0 : break;
1022 : case WI_STYPE_MGMT_DEAUTH:
1023 0 : wihap_deauth_req(sc, rxfrm, pkt, len);
1024 0 : break;
1025 : }
1026 0 : splx(s);
1027 0 : }
1028 :
1029 0 : m_freem(m);
1030 0 : }
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 0 : wihap_sta_is_assoc(struct wihap_info *whi, u_int8_t addr[])
1039 : {
1040 : struct wihap_sta_info *sta;
1041 :
1042 0 : sta = wihap_sta_find(whi, addr);
1043 0 : if (sta != NULL && (sta->flags & WI_SIFLAGS_ASSOC)) {
1044 : /* Keep it active. */
1045 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
1046 0 : return (1);
1047 : }
1048 :
1049 0 : return (0);
1050 0 : }
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 0 : 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 0 : if (addr[0] & 0x01) {
1065 0 : *txrate = 0; /* XXX: multicast rate? */
1066 0 : return (1);
1067 : }
1068 :
1069 0 : s = splsoftclock();
1070 0 : sta = wihap_sta_find(whi, addr);
1071 0 : if (sta != NULL && (sta->flags & WI_SIFLAGS_ASSOC)) {
1072 : /* Keep it active. */
1073 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
1074 0 : *txrate = txratetable[sta->tx_curr_rate];
1075 0 : splx(s);
1076 0 : return (1);
1077 : }
1078 0 : splx(s);
1079 :
1080 0 : return (0);
1081 0 : }
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 0 : wihap_data_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m)
1095 : {
1096 0 : struct ifnet *ifp = &sc->sc_ic.ic_if;
1097 0 : 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 0 : fctl = letoh16(rxfrm->wi_frame_ctl);
1109 0 : if (!(fctl & WI_FCTL_TODS) && !(fctl & WI_STYPE_NULLFUNC)) {
1110 0 : if (ifp->if_flags & IFF_DEBUG)
1111 0 : printf("wihap_data_input: no TODS src=%s, fctl=0x%x\n",
1112 0 : ether_sprintf(rxfrm->wi_addr2), fctl);
1113 0 : m_freem(m);
1114 0 : return (1);
1115 : }
1116 :
1117 : /* Check BSSID. (Is this necessary?) */
1118 0 : if (!addr_cmp(rxfrm->wi_addr1, sc->sc_ic.ic_myaddr)) {
1119 0 : if (ifp->if_flags & IFF_DEBUG)
1120 0 : printf("wihap_data_input: incorrect bss: %s\n",
1121 0 : ether_sprintf(rxfrm->wi_addr1));
1122 0 : m_freem(m);
1123 0 : return (1);
1124 : }
1125 :
1126 0 : s = splsoftclock();
1127 :
1128 : /* Find source station. */
1129 0 : sta = wihap_sta_find(whi, rxfrm->wi_addr2);
1130 :
1131 : /* Source station must be associated. */
1132 0 : if (sta == NULL || !(sta->flags & WI_SIFLAGS_ASSOC)) {
1133 0 : if (ifp->if_flags & IFF_DEBUG)
1134 0 : printf("wihap_data_input: dropping unassoc src %s\n",
1135 0 : ether_sprintf(rxfrm->wi_addr2));
1136 0 : wihap_sta_disassoc(sc, rxfrm->wi_addr2,
1137 : IEEE80211_REASON_ASSOC_LEAVE);
1138 0 : splx(s);
1139 0 : m_freem(m);
1140 0 : return (1);
1141 : }
1142 :
1143 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
1144 0 : sta->sig_info = letoh16(rxfrm->wi_q_info);
1145 :
1146 0 : splx(s);
1147 :
1148 : /* Repeat this packet to BSS? */
1149 0 : mcast = (rxfrm->wi_addr3[0] & 0x01) != 0;
1150 0 : if (mcast || wihap_sta_is_assoc(whi, rxfrm->wi_addr3)) {
1151 :
1152 : /* If it's multicast, make a copy.
1153 : */
1154 0 : if (mcast) {
1155 0 : m = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
1156 0 : if (m == NULL)
1157 0 : return (0);
1158 0 : m->m_flags |= M_MCAST; /* XXX */
1159 0 : }
1160 :
1161 : /* Queue up for repeating.
1162 : */
1163 0 : if_enqueue(ifp, m);
1164 0 : return (!mcast);
1165 : }
1166 :
1167 0 : return (0);
1168 0 : }
1169 :
1170 : /* wihap_ioctl()
1171 : *
1172 : * Handle Host AP specific ioctls. Called from wi_ioctl().
1173 : */
1174 : int
1175 0 : wihap_ioctl(struct wi_softc *sc, u_long command, caddr_t data)
1176 : {
1177 0 : struct proc *p = curproc;
1178 0 : struct ifreq *ifr = (struct ifreq *) data;
1179 0 : struct wihap_info *whi = &sc->wi_hostap_info;
1180 : struct wihap_sta_info *sta;
1181 0 : struct hostap_getall reqall;
1182 0 : struct hostap_sta reqsta;
1183 0 : struct hostap_sta stabuf;
1184 0 : int s, error = 0, n, flag;
1185 :
1186 0 : struct ieee80211_nodereq nr;
1187 : struct ieee80211_nodereq_all *na;
1188 :
1189 0 : if (!(sc->sc_ic.ic_if.if_flags & IFF_RUNNING))
1190 0 : return ENODEV;
1191 :
1192 0 : switch (command) {
1193 : case SIOCHOSTAP_DEL:
1194 0 : if ((error = suser(p)))
1195 : break;
1196 0 : if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1197 : break;
1198 0 : s = splnet();
1199 0 : sta = wihap_sta_find(whi, reqsta.addr);
1200 0 : if (sta == NULL)
1201 0 : error = ENOENT;
1202 : else {
1203 : /* Disassociate station. */
1204 0 : if (sta->flags & WI_SIFLAGS_ASSOC)
1205 0 : wihap_sta_disassoc(sc, sta->addr,
1206 : IEEE80211_REASON_ASSOC_LEAVE);
1207 : /* Deauth station. */
1208 0 : if (sta->flags & WI_SIFLAGS_AUTHEN)
1209 0 : wihap_sta_deauth(sc, sta->addr,
1210 : IEEE80211_REASON_AUTH_LEAVE);
1211 :
1212 0 : wihap_sta_delete(sta);
1213 : }
1214 0 : splx(s);
1215 0 : break;
1216 :
1217 : case SIOCHOSTAP_GET:
1218 0 : if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1219 : break;
1220 0 : s = splnet();
1221 0 : sta = wihap_sta_find(whi, reqsta.addr);
1222 0 : if (sta == NULL)
1223 0 : error = ENOENT;
1224 : else {
1225 0 : reqsta.flags = sta->flags;
1226 0 : reqsta.asid = sta->asid;
1227 0 : reqsta.capinfo = sta->capinfo;
1228 0 : reqsta.sig_info = sta->sig_info;
1229 0 : reqsta.rates = sta->rates;
1230 :
1231 0 : error = copyout(&reqsta, ifr->ifr_data,
1232 : sizeof(reqsta));
1233 : }
1234 0 : splx(s);
1235 0 : break;
1236 :
1237 : case SIOCHOSTAP_ADD:
1238 0 : if ((error = suser(p)))
1239 : break;
1240 0 : if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta))))
1241 : break;
1242 0 : s = splnet();
1243 0 : sta = wihap_sta_find(whi, reqsta.addr);
1244 0 : if (sta != NULL) {
1245 : error = EEXIST;
1246 0 : splx(s);
1247 0 : break;
1248 : }
1249 0 : if (whi->n_stations >= WIHAP_MAX_STATIONS) {
1250 : error = ENOSPC;
1251 0 : splx(s);
1252 0 : break;
1253 : }
1254 0 : sta = wihap_sta_alloc(sc, reqsta.addr);
1255 0 : sta->flags = reqsta.flags;
1256 0 : timeout_add_sec(&sta->tmo, whi->inactivity_time);
1257 0 : splx(s);
1258 0 : break;
1259 :
1260 : case SIOCHOSTAP_SFLAGS:
1261 0 : if ((error = suser(p)))
1262 : break;
1263 0 : if ((error = copyin(ifr->ifr_data, &flag, sizeof(int))))
1264 : break;
1265 :
1266 0 : whi->apflags = (whi->apflags & WIHAPFL_CANTCHANGE) |
1267 0 : (flag & ~WIHAPFL_CANTCHANGE);
1268 0 : break;
1269 :
1270 : case SIOCHOSTAP_GFLAGS:
1271 0 : flag = (int) whi->apflags;
1272 0 : error = copyout(&flag, ifr->ifr_data, sizeof(int));
1273 0 : break;
1274 :
1275 : case SIOCHOSTAP_GETALL:
1276 0 : if ((error = copyin(ifr->ifr_data, &reqall, sizeof(reqall))))
1277 : break;
1278 :
1279 0 : reqall.nstations = whi->n_stations;
1280 : n = 0;
1281 0 : s = splnet();
1282 0 : sta = TAILQ_FIRST(&whi->sta_list);
1283 0 : while (sta && reqall.size >= n+sizeof(struct hostap_sta)) {
1284 :
1285 0 : bcopy(sta->addr, stabuf.addr, ETHER_ADDR_LEN);
1286 0 : stabuf.asid = sta->asid;
1287 0 : stabuf.flags = sta->flags;
1288 0 : stabuf.capinfo = sta->capinfo;
1289 0 : stabuf.sig_info = sta->sig_info;
1290 0 : stabuf.rates = sta->rates;
1291 :
1292 0 : error = copyout(&stabuf, (caddr_t) reqall.addr + n,
1293 : sizeof(struct hostap_sta));
1294 0 : if (error)
1295 : break;
1296 :
1297 0 : sta = TAILQ_NEXT(sta, list);
1298 0 : n += sizeof(struct hostap_sta);
1299 : }
1300 0 : splx(s);
1301 :
1302 0 : if (!error)
1303 0 : error = copyout(&reqall, ifr->ifr_data,
1304 : sizeof(reqall));
1305 : break;
1306 :
1307 : case SIOCG80211ALLNODES:
1308 0 : na = (struct ieee80211_nodereq_all *)data;
1309 0 : na->na_nodes = n = 0;
1310 0 : s = splnet();
1311 0 : sta = TAILQ_FIRST(&whi->sta_list);
1312 0 : while (sta && na->na_size >=
1313 0 : n + sizeof(struct ieee80211_nodereq)) {
1314 0 : bzero(&nr, sizeof(nr));
1315 0 : IEEE80211_ADDR_COPY(nr.nr_macaddr, sta->addr);
1316 0 : IEEE80211_ADDR_COPY(nr.nr_bssid,
1317 : &sc->sc_ic.ic_myaddr);
1318 0 : nr.nr_channel = sc->wi_channel;
1319 0 : nr.nr_chan_flags = IEEE80211_CHAN_B;
1320 0 : nr.nr_associd = sta->asid;
1321 0 : nr.nr_rssi = sta->sig_info >> 8;
1322 0 : nr.nr_max_rssi = 0;
1323 0 : nr.nr_capinfo = sta->capinfo;
1324 0 : nr.nr_nrates = 0;
1325 0 : if (sta->rates & WI_SUPPRATES_1M)
1326 0 : nr.nr_rates[nr.nr_nrates++] = 2;
1327 0 : if (sta->rates & WI_SUPPRATES_2M)
1328 0 : nr.nr_rates[nr.nr_nrates++] = 4;
1329 0 : if (sta->rates & WI_SUPPRATES_5M)
1330 0 : nr.nr_rates[nr.nr_nrates++] = 11;
1331 0 : if (sta->rates & WI_SUPPRATES_11M)
1332 0 : nr.nr_rates[nr.nr_nrates++] = 22;
1333 :
1334 0 : error = copyout(&nr, (caddr_t)na->na_node + n,
1335 : sizeof(struct ieee80211_nodereq));
1336 0 : if (error)
1337 : break;
1338 0 : n += sizeof(struct ieee80211_nodereq);
1339 0 : na->na_nodes++;
1340 0 : sta = TAILQ_NEXT(sta, list);
1341 : }
1342 0 : splx(s);
1343 0 : break;
1344 :
1345 : default:
1346 0 : printf("wihap_ioctl: i shouldn't get other ioctls!\n");
1347 : error = EINVAL;
1348 0 : }
1349 :
1350 0 : return (error);
1351 0 : }
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
|