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