diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/wi/if_wavelan_ieee.h | 16 | ||||
-rw-r--r-- | sys/dev/wi/if_wi.c | 448 | ||||
-rw-r--r-- | sys/dev/wi/if_wi_pccard.c | 3 | ||||
-rw-r--r-- | sys/dev/wi/if_wi_pci.c | 3 | ||||
-rw-r--r-- | sys/dev/wi/if_wireg.h | 20 | ||||
-rw-r--r-- | sys/dev/wi/if_wivar.h | 7 | ||||
-rw-r--r-- | sys/dev/wi/wi_hostap.c | 1189 | ||||
-rw-r--r-- | sys/dev/wi/wi_hostap.h | 136 |
8 files changed, 1769 insertions, 53 deletions
diff --git a/sys/dev/wi/if_wavelan_ieee.h b/sys/dev/wi/if_wavelan_ieee.h index 159867e..2ba1dbf 100644 --- a/sys/dev/wi/if_wavelan_ieee.h +++ b/sys/dev/wi/if_wavelan_ieee.h @@ -50,6 +50,7 @@ #ifndef SIOCGWAVELAN #define SIOCGWAVELAN SIOCGIFGENERIC #endif +#define WI_RID_MONITOR_MODE 0x0500 /* * Technically I don't think there's a limit to a record @@ -113,6 +114,13 @@ struct wi_80211_hdr { #define WI_STYPE_MGMT_AUTH 0x00B0 /* authentication */ #define WI_STYPE_MGMT_DEAUTH 0x00C0 /* deauthentication */ +#define WI_STYPE_CTL_PSPOLL 0x00A0 +#define WI_STYPE_CTL_RTS 0x00B0 +#define WI_STYPE_CTL_CTS 0x00C0 +#define WI_STYPE_CTL_ACK 0x00D0 +#define WI_STYPE_CTL_CFEND 0x00E0 +#define WI_STYPE_CTL_CFENDACK 0x00F0 + struct wi_mgmt_hdr { u_int16_t frame_ctl; u_int16_t duration; @@ -209,7 +217,7 @@ struct wi_counters { #define IV_EVERY100_FRAME 0x60 /* every 100 frame IV reuse */ #define HOST_DECRYPT 0x80 #define WI_RID_WEP_MAPTABLE 0xFC29 -#define WI_RID_AUTH_CNTL 0xFC2A +#define WI_RID_CNFAUTHMODE 0xFC2A #define WI_RID_ROAMING_MODE 0xFC2D #define WI_RID_BASIC_RATE 0xFCB3 #define WI_RID_SUPPORT_RATE 0xFCB4 @@ -221,7 +229,11 @@ struct wi_counters { #define WI_RID_CREATE_IBSS 0xFC81 /* create IBSS */ #define WI_RID_FRAG_THRESH 0xFC82 /* frag len, unicast msg xmit */ #define WI_RID_RTS_THRESH 0xFC83 /* frame len for RTS/CTS handshake */ -#define WI_RID_TX_RATE 0xFC84 /* data rate for message xmit */ +#define WI_RID_TX_RATE 0xFC84 /* data rate for message xmit + * 0 == Fixed 1mbps + * 1 == Fixed 2mbps + * 2 == auto fallback + */ #define WI_RID_PROMISC 0xFC85 /* enable promisc mode */ #define WI_RID_FRAG_THRESH0 0xFC90 #define WI_RID_FRAG_THRESH1 0xFC91 diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c index 87ac17b..3adecc1 100644 --- a/sys/dev/wi/if_wi.c +++ b/sys/dev/wi/if_wi.c @@ -95,6 +95,10 @@ #include <net/bpf.h> #include <dev/wi/if_wavelan_ieee.h> +#ifdef WI_HOSTAP +#include <dev/wi/wi_hostap.h> +#include <sys/random.h> +#endif #include <dev/wi/if_wivar.h> #include <dev/wi/if_wireg.h> @@ -124,7 +128,6 @@ static int wi_seek(struct wi_softc *, int, int, int); static int wi_alloc_nicmem(struct wi_softc *, int, int *); static void wi_inquire(void *); static void wi_setdef(struct wi_softc *, struct wi_req *); -static int wi_mgmt_xmit(struct wi_softc *, caddr_t, int); #ifdef WICACHE static @@ -214,6 +217,7 @@ wi_generic_attach(device_t dev) struct ifnet *ifp; int error; + /* XXX maybe we need the splimp stuff here XXX */ sc = device_get_softc(dev); ifp = &sc->arpcom.ac_if; @@ -290,6 +294,7 @@ wi_generic_attach(device_t dev) sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; sc->wi_roaming = WI_DEFAULT_ROAMING; sc->wi_authtype = WI_DEFAULT_AUTHTYPE; + sc->wi_authmode = IEEE80211_AUTH_OPEN; /* * Read the default channel from the NIC. This may vary @@ -311,11 +316,18 @@ wi_generic_attach(device_t dev) sc->wi_has_wep = gen.wi_val; if (bootverbose) { - device_printf(sc->dev, - "%s:wi_has_wep = %d\n", - __func__, sc->wi_has_wep); + device_printf(sc->dev, "%s:wi_has_wep = %d\n", + __func__, sc->wi_has_wep); } + /* + * Find supported rates. + */ + gen.wi_type = WI_RID_TX_RATE; + gen.wi_len = 2; + wi_read_record(sc, &gen); + sc->wi_supprates = gen.wi_val; + bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); wi_init(sc); @@ -339,6 +351,20 @@ wi_generic_attach(device_t dev) ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, IFM_IEEE80211_ADHOC, 0), 0); ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0); +#ifdef IFM_IEEE80211_HOSTAP + if (sc->sc_firmware_type == WI_INTERSIL) { + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, + IFM_IEEE80211_HOSTAP, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, + IFM_IEEE80211_HOSTAP, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, + IFM_IEEE80211_HOSTAP, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, + IFM_IEEE80211_HOSTAP, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, + IFM_IEEE80211_HOSTAP, 0), 0); + } +#endif #undef ADD ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0)); @@ -587,6 +613,38 @@ wi_rxeof(sc) eh = mtod(m, struct ether_header *); m->m_pkthdr.rcvif = ifp; +#ifdef WI_HOSTAP + if (rx_frame.wi_status == WI_STAT_MGMT && + sc->wi_ptype == WI_PORTTYPE_AP) { + if ((WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len + 2) > + MCLBYTES) { + device_printf(sc->dev, "oversized mgmt packet " + "received in hostap mode " + "(wi_dat_len=%d, wi_status=0x%x)\n", + rx_frame.wi_dat_len, rx_frame.wi_status); + m_freem(m); + ifp->if_ierrors++; + return; + } + + /* Put the whole header in there. */ + bcopy(&rx_frame, mtod(m, void *), + sizeof(struct wi_frame)); + if (wi_read_data(sc, id, WI_802_11_OFFSET_RAW, + mtod(m, caddr_t) + WI_802_11_OFFSET_RAW, + rx_frame.wi_dat_len + 2)) { + m_freem(m); + ifp->if_ierrors++; + return; + } + m->m_pkthdr.len = m->m_len = + WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len; + /* XXX: consider giving packet to bhp? */ + wihap_mgmt_input(sc, &rx_frame, m); + return; + } +#endif /* WI_HOSTAP */ + if (rx_frame.wi_status == WI_STAT_1042 || rx_frame.wi_status == WI_STAT_TUNNEL || rx_frame.wi_status == WI_STAT_WMP_MSG) { @@ -653,6 +711,18 @@ wi_rxeof(sc) ifp->if_ipackets++; +#ifdef WI_HOSTAP + if (sc->wi_ptype == WI_PORTTYPE_AP) { + /* + * Give host AP code first crack at data + * packets. If it decides to handle it (or + * drop it), it will return a non-zero. + * Otherwise, it is destined for this host. + */ + if (wihap_data_input(sc, &rx_frame, m)) + return; + } +#endif /* Receive packet. */ m_adj(m, sizeof(struct ether_header)); #ifdef WICACHE @@ -688,6 +758,7 @@ wi_inquire(xsc) { struct wi_softc *sc; struct ifnet *ifp; + int s; sc = xsc; ifp = &sc->arpcom.ac_if; @@ -698,7 +769,9 @@ wi_inquire(xsc) if (ifp->if_flags & IFF_OACTIVE) return; + s = splimp(); wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS, 0, 0); + splx(s); return; } @@ -1002,7 +1075,7 @@ wi_read_record(sc, ltv) oltv->wi_len = 2; oltv->wi_val = ltv->wi_val; break; - case WI_RID_AUTH_CNTL: + case WI_RID_CNFAUTHMODE: oltv->wi_len = 2; if (le16toh(ltv->wi_val) & 0x01) oltv->wi_val = htole16(1); @@ -1047,10 +1120,18 @@ wi_write_record(sc, ltv) case WI_RID_ENCRYPTION: p2ltv.wi_type = WI_RID_P2_ENCRYPTION; p2ltv.wi_len = 2; - if (le16toh(ltv->wi_val)) + if (le16toh(ltv->wi_val)) { p2ltv.wi_val =htole16(PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED); - else +#ifdef WI_HOSTAP + if (sc->wi_ptype == WI_PORTTYPE_AP) + /* + * Disable tx encryption... + * it's broken. + */ + p2ltv.wi_val |= htole16(HOST_ENCRYPT); +#endif + } else p2ltv.wi_val = htole16(HOST_ENCRYPT | HOST_DECRYPT); ltv = &p2ltv; @@ -1084,8 +1165,8 @@ wi_write_record(sc, ltv) } return 0; } - case WI_RID_AUTH_CNTL: - p2ltv.wi_type = WI_RID_AUTH_CNTL; + case WI_RID_CNFAUTHMODE: + p2ltv.wi_type = WI_RID_CNFAUTHMODE; p2ltv.wi_len = 2; if (le16toh(ltv->wi_val) == 1) p2ltv.wi_val = htole16(0x01); @@ -1372,7 +1453,7 @@ wi_setdef(sc, wreq) case WI_RID_MAX_SLEEP: sc->wi_max_sleep = le16toh(wreq->wi_val[0]); break; - case WI_RID_AUTH_CNTL: + case WI_RID_CNFAUTHMODE: sc->wi_authtype = le16toh(wreq->wi_val[0]); break; case WI_RID_ROAMING_MODE: @@ -1632,7 +1713,7 @@ wi_ioctl(ifp, command, data) ireq->i_val = sc->wi_tx_key; break; case IEEE80211_IOC_AUTHMODE: - ireq->i_val = IEEE80211_AUTH_NONE; + ireq->i_val = sc->wi_authmode; break; case IEEE80211_IOC_STATIONNAME: error = copyout(sc->wi_node_name, @@ -1712,7 +1793,7 @@ wi_ioctl(ifp, command, data) sc->wi_tx_key = ireq->i_val; break; case IEEE80211_IOC_AUTHMODE: - error = EINVAL; + sc->wi_authmode = ireq->i_val; break; case IEEE80211_IOC_STATIONNAME: if (ireq->i_len > 32) { @@ -1763,7 +1844,18 @@ wi_ioctl(ifp, command, data) /* Reinitialize WaveLAN. */ wi_init(sc); + break; +#ifdef WI_HOSTAP + case SIOCHOSTAP_ADD: + case SIOCHOSTAP_DEL: + case SIOCHOSTAP_GET: + case SIOCHOSTAP_GETALL: + case SIOCHOSTAP_GFLAGS: + case SIOCHOSTAP_SFLAGS: + /* Send all Host AP specific ioctl's to Host AP code. */ + error = wihap_ioctl(sc, command, data); break; +#endif default: error = EINVAL; break; @@ -1834,6 +1926,9 @@ wi_init(xsc) /* Program the nodename. */ WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); + /* Specify the authentication mode. */ + WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authmode); + /* Set our MAC address. */ mac.wi_len = 4; mac.wi_type = WI_RID_MAC_NODE; @@ -1863,7 +1958,7 @@ wi_init(xsc) /* firm ver < 0.8 variant 2 */ WI_SETVAL(WI_RID_PROMISC, 1); } - WI_SETVAL(WI_RID_AUTH_CNTL, sc->wi_authtype); + WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authtype); } } @@ -1891,6 +1986,15 @@ wi_init(xsc) /* enable interrupts */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); +#ifdef WI_HOSTAP + wihap_init(sc); + + /* + * Initialize ICV to something random. XXX: this doesn't work + * if init happens in early boot-up. Fix later. + */ + read_random(&sc->wi_icv, sizeof(sc->wi_icv)); +#endif ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; @@ -1900,6 +2004,159 @@ wi_init(xsc) return; } +#ifdef WI_HOSTAP + +/* + * Host encryption code: Transmits are broken in Host AP mode when WEP is + * enabled. So, we disable TX encryption and manually encrypt it here. + */ + +static u_int32_t +wi_next_crc32(u_int32_t crc32, u_int8_t byte) +{ + /* Thanks again, RFC 1662. */ + static u_int32_t fcstab_32[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + return (crc32>>8) ^ fcstab_32[(u_int8_t)(byte ^ crc32)]; +} + + +static __inline void +SWAP(u_int8_t *pa, u_int8_t *pb) +{ + u_int8_t c; + c = *pa; + *pa = *pb; + *pb = c; +} + +static void +wi_do_hostencrypt(struct wi_softc *sc, caddr_t buf, int len) +{ + int i; + u_int32_t crc32; + u_int8_t k[16]; + u_int8_t state[256]; + u_int8_t x, y; + int keylen; + + keylen = (sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keylen>5) ? 16:8; + + /* Create ICV/key concatenation. */ + k[0] = sc->wi_icv >> 16; + k[1] = sc->wi_icv >> 8; + k[2] = sc->wi_icv; + for (i=3; i<keylen; i++) + k[i] = sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keydat[i-3]; + + /* Insert ICV in packet. */ + buf[0] = k[0]; + buf[1] = k[1]; + buf[2] = k[2]; + buf[3] = (sc->wi_tx_key<<6); + ++sc->wi_icv; + + /* Initialize key. */ + for (i=0; i<256; i++) + state[i]=i; + x=0; + y=0; + for (i=0; i<256; i++) { + y = (k[x]+state[i]+y); + SWAP(&state[i], &state[y]); + x = (x+1) & (keylen-1); + } + + /* Start encrypting. */ + x=0; + y=0; + crc32=0xffffffff; + for (i=0; i<len; i++) { + x++; + y = state[x]+y; + SWAP(&state[x], &state[y]); + crc32 = wi_next_crc32(crc32, buf[i+4]); + buf[i+4] ^= state[ (u_int8_t)(state[x] + state[y]) ]; + } + + /* Append CRC-32, encrypt it too. */ + crc32 ^= 0xffffffff; + for (i=0; i<4; i++) { + x++; + y = state[x]+y; + SWAP(&state[x], &state[y]); + buf[i+len+4] = crc32; + crc32 >>= 8; + buf[i+len+4] ^= state[ (u_int8_t)(state[x] + state[y]) ]; + } +} +#endif /* WI_HOSTAP */ + static void wi_start(ifp) struct ifnet *ifp; @@ -1923,6 +2180,9 @@ wi_start(ifp) return; } +#ifdef WI_HOSTAP +nextpkt: +#endif IF_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) { WI_UNLOCK(sc); @@ -1930,9 +2190,22 @@ wi_start(ifp) } bzero((char *)&tx_frame, sizeof(tx_frame)); + tx_frame.wi_frame_ctl = htole16(WI_FTYPE_DATA); id = sc->wi_tx_data_id; eh = mtod(m0, struct ether_header *); +#ifdef WI_HOSTAP + if (sc->wi_ptype == WI_PORTTYPE_AP) { + if (!wihap_check_tx(&sc->wi_hostap_info, + eh->ether_dhost, &tx_frame.wi_tx_rate)) { + if (ifp->if_flags & IFF_DEBUG) + printf("wi_start: dropping unassoc " + "dst %6D\n", eh->ether_dhost, ":"); + m_freem(m0); + goto nextpkt; + } + } +#endif /* * Use RFC1042 encoding for IP and ARP datagrams, * 802.3 for anything else. @@ -1940,38 +2213,81 @@ wi_start(ifp) if (ntohs(eh->ether_type) > ETHER_MAX_LEN) { bcopy((char *)&eh->ether_dhost, (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); - bcopy((char *)&eh->ether_shost, - (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); +#ifdef WI_HOSTAP + if (sc->wi_ptype == WI_PORTTYPE_AP) { + tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; /* XXX */ + tx_frame.wi_frame_ctl |= WI_FCTL_FROMDS; + if (sc->wi_use_wep) + tx_frame.wi_frame_ctl |= WI_FCTL_WEP; + bcopy((char *)&sc->arpcom.ac_enaddr, + (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); + bcopy((char *)&eh->ether_shost, + (char *)&tx_frame.wi_addr3, ETHER_ADDR_LEN); + } + else +#endif + bcopy((char *)&eh->ether_shost, + (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); bcopy((char *)&eh->ether_dhost, (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); bcopy((char *)&eh->ether_shost, (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; - tx_frame.wi_frame_ctl = WI_FTYPE_DATA; tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); tx_frame.wi_type = eh->ether_type; - m_copydata(m0, sizeof(struct ether_header), - m0->m_pkthdr.len - sizeof(struct ether_header), - (caddr_t)&sc->wi_txbuf); - - wi_write_data(sc, id, 0, (caddr_t)&tx_frame, - sizeof(struct wi_frame)); - wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, - (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); +#ifdef WI_HOSTAP + if (sc->wi_ptype == WI_PORTTYPE_AP && sc->wi_use_wep) { + /* Do host encryption. */ + bcopy(&tx_frame.wi_dat[0], &sc->wi_txbuf[4], 8); + m_copydata(m0, sizeof(struct ether_header), + m0->m_pkthdr.len - sizeof(struct ether_header), + (caddr_t)&sc->wi_txbuf[12]); + wi_do_hostencrypt(sc, &sc->wi_txbuf[0], + tx_frame.wi_dat_len); + tx_frame.wi_dat_len += 8; + wi_write_data(sc, id, 0, (caddr_t)&tx_frame, + sizeof(struct wi_frame)); + wi_write_data(sc, id, WI_802_11_OFFSET_RAW, + (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len - + sizeof(struct ether_header)) + 18); + } + else +#endif + { + m_copydata(m0, sizeof(struct ether_header), + m0->m_pkthdr.len - sizeof(struct ether_header), + (caddr_t)&sc->wi_txbuf); + wi_write_data(sc, id, 0, (caddr_t)&tx_frame, + sizeof(struct wi_frame)); + wi_write_data(sc, id, WI_802_11_OFFSET, + (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len - + sizeof(struct ether_header)) + 2); + } } else { tx_frame.wi_dat_len = m0->m_pkthdr.len; - eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); - m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); - - wi_write_data(sc, id, 0, (caddr_t)&tx_frame, - sizeof(struct wi_frame)); - wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, - m0->m_pkthdr.len + 2); +#ifdef WI_HOSTAP + if (sc->wi_ptype == WI_PORTTYPE_AP && sc->wi_use_wep) { + /* Do host encryption. */ + printf( "XXX: host encrypt not implemented for 802.3\n" ); + } + else +#endif + { + eh->ether_type = htons(m0->m_pkthdr.len - + WI_SNAPHDR_LEN); + m_copydata(m0, 0, m0->m_pkthdr.len, + (caddr_t)&sc->wi_txbuf); + + wi_write_data(sc, id, 0, (caddr_t)&tx_frame, + sizeof(struct wi_frame)); + wi_write_data(sc, id, WI_802_3_OFFSET, + (caddr_t)&sc->wi_txbuf, m0->m_pkthdr.len + 2); + } } /* @@ -1998,7 +2314,7 @@ wi_start(ifp) return; } -static int +int wi_mgmt_xmit(sc, data, len) struct wi_softc *sc; caddr_t data; @@ -2021,12 +2337,13 @@ wi_mgmt_xmit(sc, data, len) bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, sizeof(struct wi_80211_hdr)); - tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; - tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); + tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; + tx_frame.wi_dat_len = len - sizeof(struct wi_80211_hdr); + tx_frame.wi_len = htons(tx_frame.wi_dat_len); wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, - (len - sizeof(struct wi_80211_hdr)) + 2); + len - sizeof(struct wi_80211_hdr) + 2); if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) { device_printf(sc->dev, "xmit failed\n"); @@ -2049,6 +2366,10 @@ wi_stop(sc) return; } +#ifdef WI_HOSTAP + wihap_shutdown(sc); +#endif + ifp = &sc->arpcom.ac_if; /* @@ -2268,7 +2589,8 @@ wi_cache_store(struct wi_softc *sc, struct ether_header *eh, int sig, noise; int sawip=0; - /* filters: + /* + * filters: * 1. ip only * 2. configurable filter to throw out unicast packets, * keep multicast only. @@ -2278,13 +2600,15 @@ wi_cache_store(struct wi_softc *sc, struct ether_header *eh, sawip = 1; } - /* filter for ip packets only + /* + * filter for ip packets only */ if (wi_cache_iponly && !sawip) { return; } - /* filter for broadcast/multicast only + /* + * filter for broadcast/multicast only */ if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { return; @@ -2295,21 +2619,23 @@ wi_cache_store(struct wi_softc *sc, struct ether_header *eh, rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); #endif - /* find the ip header. we want to store the ip_src + /* + * find the ip header. we want to store the ip_src * address. */ - if (sawip) { + if (sawip) ip = mtod(m, struct ip *); - } - /* do a linear search for a matching MAC address + /* + * do a linear search for a matching MAC address * in the cache table * . MAC address is 6 bytes, * . var w_nextitem holds total number of entries already cached */ for(i = 0; i < sc->wi_nextitem; i++) { if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { - /* Match!, + /* + * Match!, * so we already have this entry, * update the data */ @@ -2317,19 +2643,22 @@ wi_cache_store(struct wi_softc *sc, struct ether_header *eh, } } - /* did we find a matching mac address? + /* + * did we find a matching mac address? * if yes, then overwrite a previously existing cache entry */ if (i < sc->wi_nextitem ) { cache_slot = i; } - /* else, have a new address entry,so + /* + * else, have a new address entry,so * add this new entry, * if table full, then we need to replace LRU entry */ else { - /* check for space in cache table + /* + * check for space in cache table * note: wi_nextitem also holds number of entries * added in the cache table */ @@ -2349,7 +2678,8 @@ wi_cache_store(struct wi_softc *sc, struct ether_header *eh, } } - /* invariant: cache_slot now points at some slot + /* + * invariant: cache_slot now points at some slot * in cache. */ if (cache_slot < 0 || cache_slot >= MAXWICACHE) { @@ -2359,14 +2689,14 @@ wi_cache_store(struct wi_softc *sc, struct ether_header *eh, return; } - /* store items in cache + /* + * store items in cache * .ip source address * .mac src * .signal, etc. */ - if (sawip) { + if (sawip) sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; - } bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); sig = (rx_quality >> 8) & 0xFF; @@ -2390,6 +2720,12 @@ wi_get_cur_ssid(sc, ssid, len) wreq.wi_len = WI_MAX_DATALEN; switch (sc->wi_ptype) { +#ifdef WI_HOSTAP + case WI_PORTTYPE_AP: + *len = IEEE80211_NWID_LEN; + bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN); + break; +#endif case WI_PORTTYPE_ADHOC: wreq.wi_type = WI_RID_CURRENT_SSID; error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); @@ -2442,6 +2778,10 @@ wi_media_change(ifp) if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0) sc->wi_ptype = WI_PORTTYPE_ADHOC; +#if defined(WI_HOSTAP) && defined(IFM_IEEE80211_HOSTAP) + else if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_HOSTAP) != 0) + sc->wi_ptype = WI_PORTTYPE_AP; +#endif else sc->wi_ptype = WI_PORTTYPE_BSS; @@ -2482,6 +2822,10 @@ wi_media_status(ifp, imr) imr->ifm_active = IFM_IEEE80211|IFM_AUTO; if (sc->wi_ptype == WI_PORTTYPE_ADHOC) imr->ifm_active |= IFM_IEEE80211_ADHOC; +#if defined(WI_HOSTAP) && defined(IFM_IEEE80211_HOSTAP) + else if (sc->wi_ptype == WI_PORTTYPE_AP) + imr->ifm_active |= IFM_IEEE80211_HOSTAP; +#endif wreq.wi_type = WI_RID_CUR_TX_RATE; wreq.wi_len = WI_MAX_DATALEN; if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) { @@ -2512,6 +2856,10 @@ wi_media_status(ifp, imr) * created one ourselves. */ imr->ifm_status |= IFM_ACTIVE; +#ifdef WI_HOSTAP + else if (sc->wi_ptype == WI_PORTTYPE_AP) + imr->ifm_status |= IFM_ACTIVE; +#endif else { wreq.wi_type = WI_RID_COMMQUAL; wreq.wi_len = WI_MAX_DATALEN; diff --git a/sys/dev/wi/if_wi_pccard.c b/sys/dev/wi/if_wi_pccard.c index 02a662c..642cdec 100644 --- a/sys/dev/wi/if_wi_pccard.c +++ b/sys/dev/wi/if_wi_pccard.c @@ -60,6 +60,9 @@ #include <dev/pccard/pccarddevs.h> #include <dev/wi/if_wavelan_ieee.h> +#ifdef WI_HOSTAP +#include <dev/wi/wi_hostap.h> +#endif #include <dev/wi/if_wivar.h> #include <dev/wi/if_wireg.h> diff --git a/sys/dev/wi/if_wi_pci.c b/sys/dev/wi/if_wi_pci.c index 9adc8ce..d2f8ef5 100644 --- a/sys/dev/wi/if_wi_pci.c +++ b/sys/dev/wi/if_wi_pci.c @@ -59,6 +59,9 @@ #include <net/if_ieee80211.h> #include <dev/wi/if_wavelan_ieee.h> +#ifdef WI_HOSTAP +#include <dev/wi/wi_hostap.h> +#endif #include <dev/wi/if_wivar.h> #include <dev/wi/if_wireg.h> diff --git a/sys/dev/wi/if_wireg.h b/sys/dev/wi/if_wireg.h index e3c705b..7a02987 100644 --- a/sys/dev/wi/if_wireg.h +++ b/sys/dev/wi/if_wireg.h @@ -497,9 +497,11 @@ struct wi_ltv_pcf { * (Only PRISM2; not 802.11 compliant mode, testing use only) * 6 == HOST AP (Only PRISM2) */ +#define WI_PORTTYPE_IBSS 0x0 #define WI_PORTTYPE_BSS 0x1 #define WI_PORTTYPE_WDS 0x2 #define WI_PORTTYPE_ADHOC 0x3 +#define WI_PORTTYPE_AP 0x6 /* * Mac addresses. (0xFC01, 0xFC08) @@ -539,6 +541,14 @@ struct wi_ltv_mcast { }; /* + * supported rates. (0xFCB4) + */ +#define WI_SUPPRATES_1M 0x0001 +#define WI_SUPPRATES_2M 0x0002 +#define WI_SUPPRATES_5M 0x0004 +#define WI_SUPPRATES_11M 0x0008 + +/* * Information frame types. */ #define WI_INFO_NOTIFY 0xF000 /* Handover address */ @@ -556,7 +566,8 @@ struct wi_frame { u_int16_t wi_rsvd1; /* 0x04 */ u_int16_t wi_q_info; /* 0x06 */ u_int16_t wi_rsvd2; /* 0x08 */ - u_int16_t wi_rsvd3; /* 0x0A */ + u_int8_t wi_tx_rtry; /* 0x0A */ + u_int8_t wi_tx_rate; /* 0x0B */ u_int16_t wi_tx_ctl; /* 0x0C */ u_int16_t wi_frame_ctl; /* 0x0E */ u_int16_t wi_id; /* 0x10 */ @@ -576,6 +587,7 @@ struct wi_frame { #define WI_802_3_OFFSET 0x2E #define WI_802_11_OFFSET 0x44 #define WI_802_11_OFFSET_RAW 0x3C +#define WI_802_11_OFFSET_HDR 0x0E #define WI_STAT_BADCRC 0x0001 #define WI_STAT_UNDECRYPTABLE 0x0002 @@ -584,10 +596,12 @@ struct wi_frame { #define WI_STAT_1042 0x2000 /* RFC1042 encoded */ #define WI_STAT_TUNNEL 0x4000 /* Bridge-tunnel encoded */ #define WI_STAT_WMP_MSG 0x6000 /* WaveLAN-II management protocol */ +#define WI_STAT_MGMT 0x8000 /* 802.11b management frames */ #define WI_RXSTAT_MSG_TYPE 0xE000 #define WI_ENC_TX_802_3 0x00 #define WI_ENC_TX_802_11 0x11 +#define WI_ENC_TX_MGMT 0x08 #define WI_ENC_TX_E_II 0x0E #define WI_ENC_TX_1042 0x00 @@ -595,6 +609,9 @@ struct wi_frame { #define WI_TXCNTL_MACPORT 0x00FF #define WI_TXCNTL_STRUCTTYPE 0xFF00 +#define WI_TXCNTL_TX_EX 0x0004 +#define WI_TXCNTL_TX_OK 0x0002 +#define WI_TXCNTL_NOCRYPT 0x0080 /* * SNAP (sub-network access protocol) constants for transmission @@ -608,3 +625,4 @@ struct wi_frame { #define WI_SNAP_WORD0 (WI_SNAP_K1 | (WI_SNAP_K1 << 8)) #define WI_SNAP_WORD1 (WI_SNAP_K2 | (WI_SNAP_CONTROL << 8)) #define WI_SNAPHDR_LEN 0x6 +#define WI_FCS_LEN 0x4 diff --git a/sys/dev/wi/if_wivar.h b/sys/dev/wi/if_wivar.h index d86f0b7..cc1eaf8 100644 --- a/sys/dev/wi/if_wivar.h +++ b/sys/dev/wi/if_wivar.h @@ -136,6 +136,7 @@ struct wi_softc { u_int16_t wi_pm_enabled; u_int16_t wi_mor_enabled; u_int16_t wi_max_sleep; + u_int16_t wi_supprates; u_int16_t wi_authtype; u_int16_t wi_roaming; @@ -148,6 +149,7 @@ struct wi_softc { struct wi_counters wi_stats; int wi_has_wep; int wi_use_wep; + int wi_authmode; int wi_tx_key; struct wi_ltv_keys wi_keys; #ifdef WICACHE @@ -155,6 +157,10 @@ struct wi_softc { struct wi_sigcache wi_sigcache[MAXWICACHE]; int wi_nextitem; #endif +#ifdef WI_HOSTAP + struct wihap_info wi_hostap_info; + u_int32_t wi_icv; +#endif struct callout_handle wi_stat_ch; struct mtx wi_mtx; int wi_nic_type; @@ -193,3 +199,4 @@ void wi_shutdown(device_t); int wi_alloc(device_t, int); void wi_free(device_t); extern devclass_t wi_devclass; +int wi_mgmt_xmit(struct wi_softc *, caddr_t, int); diff --git a/sys/dev/wi/wi_hostap.c b/sys/dev/wi/wi_hostap.c new file mode 100644 index 0000000..cbd73d0 --- /dev/null +++ b/sys/dev/wi/wi_hostap.c @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 2002 + * Thomas Skibo <skibo@pacbell.net>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Thomas Skibo. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Thomas Skibo AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Thomas Skibo OR HIS DRINKING PALS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* This is experimental Host AP software for Prism 2 802.11b interfaces. + * + * Much of this is based upon the "Linux Host AP driver Host AP driver + * for Intersil Prism2" by Jouni Malinen <jkm@ssh.com> or <jkmaline@cc.hut.fi>. + */ + +#ifdef WI_HOSTAP + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/ucred.h> +#include <sys/socket.h> +#include <sys/module.h> +#include <sys/queue.h> +#include <sys/bus.h> +#include <sys/syslog.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/clock.h> +#include <machine/md_var.h> +#include <machine/bus_pio.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_ieee80211.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> + +#include <dev/wi/if_wavelan_ieee.h> +#ifdef WI_HOSTAP +#include <dev/wi/wi_hostap.h> +#endif +#include <dev/wi/if_wivar.h> +#include <dev/wi/if_wireg.h> + +MALLOC_DEFINE(M_HAP_STA, "hostap_sta", "if_wi host AP mode station entry"); + +/* + * take_hword() + * + * Used for parsing management frames. The pkt pointer and length + * variables are updated after the value is removed. + */ +static __inline u_int16_t +take_hword(caddr_t *ppkt, int *plen) +{ + u_int16_t s = le16toh(* (u_int16_t *) *ppkt); + *ppkt += sizeof(u_int16_t); + *plen -= sizeof(u_int16_t); + return s; +} + +/* + * take_tlv() + * + * Parse out TLV element from a packet, check for underflow of packet + * or overflow of buffer, update pkt/len. + */ +static int +take_tlv(caddr_t *ppkt, int *plen, int id_expect, void *dst, int maxlen) +{ + u_int8_t id, len; + + if (*plen < 2) + return -1; + + id = ((u_int8_t *)*ppkt)[0]; + len = ((u_int8_t *)*ppkt)[1]; + + if (id != id_expect || *plen < len+2 || maxlen < len) + return -1; + + bcopy(*ppkt+2, dst, len); + *plen -= 2+len; + *ppkt += 2+len; + + return len; +} + +/* + * put_hword() + * Put half-word element into management frames. + */ +static __inline void +put_hword(caddr_t *ppkt, u_int16_t s) +{ + * (u_int16_t *) *ppkt = htole16(s); + *ppkt += sizeof(u_int16_t); +} + +/* + * put_tlv() + * Put TLV elements into management frames. + */ +static void +put_tlv(caddr_t *ppkt, u_int8_t id, void *src, u_int8_t len) +{ + (*ppkt)[0] = id; + (*ppkt)[1] = len; + bcopy(src, (*ppkt)+2, len); + *ppkt += 2+len; +} + +static int +put_rates(caddr_t *ppkt, u_int16_t rates) +{ + int len = 0; + u_int8_t ratebuf[8]; + + if (rates & WI_SUPPRATES_1M) + ratebuf[len++] = 0x82; + if (rates & WI_SUPPRATES_2M) + ratebuf[len++] = 0x84; + if (rates & WI_SUPPRATES_5M) + ratebuf[len++] = 0x8b; + if (rates & WI_SUPPRATES_11M) + ratebuf[len++] = 0x96; + + put_tlv(ppkt, IEEE80211_ELEMID_RATES, ratebuf, len); + return len; +} + +/* + * wihap_init() + * + * Initialize host AP data structures. Called even if port type is + * not AP. + */ +void +wihap_init(struct wi_softc *sc) +{ + int i; + struct wihap_info *whi = &sc->wi_hostap_info; + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_init: sc=0x%x whi=0x%x\n", (int)sc, (int)whi); + + bzero(whi, sizeof(struct wihap_info)); + + if (sc->wi_ptype != WI_PORTTYPE_AP) + return; + + whi->apflags = WIHAPFL_ACTIVE; + + LIST_INIT(&whi->sta_list); + for (i=0; i<WI_STA_HASH_SIZE; i++) + LIST_INIT(&whi->sta_hash[i]); + + whi->inactivity_time = WIHAP_DFLT_INACTIVITY_TIME; + whi->hostap_ch = timeout(wihap_timer, sc, hz * WIHAP_INTERVAL); +} + +/* + * wihap_sta_disassoc() + * + * Send a disassociation frame to a specified station. + */ +static void +wihap_sta_disassoc(struct wi_softc *sc, u_int8_t sta_addr[], + u_int16_t reason) +{ + struct wi_80211_hdr *resp_hdr; + caddr_t pkt; + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("Sending disassoc to sta %6D\n", sta_addr, ":"); + + /* Send disassoc packet. */ + resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf; + bzero(resp_hdr, sizeof(struct wi_80211_hdr)); + resp_hdr->frame_ctl = WI_FTYPE_MGMT | WI_STYPE_MGMT_DISAS; + pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr); + + bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN); + + put_hword(&pkt, reason); + + wi_mgmt_xmit(sc, sc->wi_txbuf, 2+sizeof(struct wi_80211_hdr)); +} + +/* + * wihap_sta_deauth() + * + * Send a deauthentication message to a specified station. + */ +static void +wihap_sta_deauth(struct wi_softc *sc, u_int8_t sta_addr[], + u_int16_t reason) +{ + struct wi_80211_hdr *resp_hdr; + caddr_t pkt; + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("Sending deauth to sta %6D\n", sta_addr, ":"); + + /* Send deauth packet. */ + resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf; + bzero(resp_hdr, sizeof(struct wi_80211_hdr)); + resp_hdr->frame_ctl = htole16(WI_FTYPE_MGMT | WI_STYPE_MGMT_DEAUTH); + pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr); + + bcopy(sta_addr, resp_hdr->addr1, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN); + + put_hword(&pkt, reason); + + wi_mgmt_xmit(sc, sc->wi_txbuf, 2+sizeof(struct wi_80211_hdr)); +} + +/* + * wihap_shutdown() + * + * Disassociate all stations and free up data structures. + */ +void +wihap_shutdown(struct wi_softc *sc) +{ + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta, *next; + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_shutdown: sc=0x%x whi=0x%x\n", + (int)sc, (int)whi); + + if (!(whi->apflags & WIHAPFL_ACTIVE)) + return; + + /* + * XXX: I read somewhere you can deauth all the stations with + * a single broadcast. Maybe try that someday. + */ + + sta = LIST_FIRST(&whi->sta_list); + while (sta) { + + if (!sc->wi_gone) { + /* Disassociate station. */ + if (sta->flags & WI_SIFLAGS_ASSOC) + wihap_sta_disassoc(sc, sta->addr, + IEEE80211_REASON_ASSOC_LEAVE); + /* Deauth station. */ + if (sta->flags & WI_SIFLAGS_AUTHEN) + wihap_sta_deauth(sc, sta->addr, + IEEE80211_REASON_AUTH_LEAVE); + } + + /* Delete the structure. */ + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_shutdown: FREE(sta=0x%x)\n", (int) sta); + next = LIST_NEXT(sta, list); + FREE(sta, M_HAP_STA); + sta = next; + } + + untimeout(wihap_timer, sc, whi->hostap_ch); + + whi->apflags = 0; +} + +/* + * sta_hash_func() + * Hash function for finding stations from ethernet address. + */ +static __inline int +sta_hash_func(u_int8_t addr[]) +{ + return ((addr[3] + addr[4] + addr[5]) % WI_STA_HASH_SIZE); +} + +/* + * addr_cmp(): + * Maybe this is a faster way to compare addresses? + */ +static __inline int +addr_cmp(u_int8_t a[], u_int8_t b[]) +{ + return (*(u_int16_t *)(a+4) == *(u_int16_t *)(b+4) && + *(u_int32_t *)a == *(u_int32_t *)b); +} + +/* + * wihap_sta_delete() + * Delete a single station and free up its data structure. + */ +static void +wihap_sta_delete(struct wihap_info *whi, struct wihap_sta_info *sta) +{ + int i = sta->asid - 0xc001; + whi->asid_inuse_mask[i>>4] &= ~(1ul<<(i&0xf)); + + LIST_REMOVE(sta, list); + LIST_REMOVE(sta, hash); + FREE(sta, M_HAP_STA); + whi->n_stations--; +} + +/* + * wihap_sta_alloc() + * + * Create a new station data structure and put it in the list + * and hash table. + */ +static struct wihap_sta_info * +wihap_sta_alloc(struct wihap_info *whi, u_int8_t *addr) +{ + int i; + int hash = sta_hash_func(addr); + struct wihap_sta_info *sta; + + /* Allocate structure. */ + MALLOC(sta, struct wihap_sta_info *, sizeof(struct wihap_sta_info), + M_HAP_STA, M_NOWAIT); + if (sta == NULL) + return(NULL); + + bzero(sta, sizeof(struct wihap_sta_info)); + + /* Allocate an ASID. */ + i=hash<<4; + while (whi->asid_inuse_mask[i>>4] & (1ul<<(i&0xf))) + i = (i==(WI_STA_HASH_SIZE<<4)-1) ? 0 : (i+1); + whi->asid_inuse_mask[i>>4] |= (1ul<<(i&0xf)); + sta->asid = 0xc001+i; + + /* Insert in list and hash list. */ + LIST_INSERT_HEAD(&whi->sta_list, sta, list); + LIST_INSERT_HEAD(&whi->sta_hash[hash], sta, hash); + + whi->n_stations++; + bcopy(addr, &sta->addr, ETHER_ADDR_LEN); + sta->inactivity_timer = whi->inactivity_time; + + return(sta); +} + +/* + * wihap_sta_find() + * + * Find station structure given address. + */ +static struct wihap_sta_info * +wihap_sta_find(struct wihap_info *whi, u_int8_t *addr) +{ + int i; + struct wihap_sta_info *sta; + + i = sta_hash_func(addr); + LIST_FOREACH(sta, &whi->sta_hash[i], hash) + if (addr_cmp(addr,sta->addr)) + return sta; + + return(NULL); +} + +/* + * wihap_timer() + * + * Called every WIHAP_INTERVAL seconds. Look for inactive + * stations and disassociate them. + */ +void +wihap_timer(void *xsc) +{ + int s; + struct wihap_sta_info *sta, *next; + struct wi_softc *sc = (struct wi_softc *) xsc; + struct wihap_info *whi = &sc->wi_hostap_info; + + s = splimp(); + + sta = LIST_FIRST(&whi->sta_list); + while (sta) { + + /* Grab next station now just in case we delete it. */ + next = LIST_NEXT(sta, list); + + if (sta->inactivity_timer >= 0) { + if (--sta->inactivity_timer == 0) { + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_timer: disassoc due to " + "inactivity: %6D\n", + sta->addr, ":"); + + if (sta->flags & WI_SIFLAGS_ASSOC) { + /* Disassoc station. */ + wihap_sta_disassoc(sc, sta->addr, + IEEE80211_REASON_ASSOC_EXPIRE); + + sta->flags &= ~WI_SIFLAGS_ASSOC; + } + } + else if (sta->inactivity_timer == -1) { + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_timer: deauth due to " + "inactivity: %6D\n", + sta->addr, ":"); + + if (sta->flags & WI_SIFLAGS_AUTHEN) { + /* Deauthenticate station. */ + wihap_sta_deauth(sc, sta->addr, + IEEE80211_REASON_AUTH_EXPIRE); + + sta->flags &= ~WI_SIFLAGS_AUTHEN; + } + + /* Delete the station if it's not permanent. */ + if (!(sta->flags & WI_SIFLAGS_PERM)) + wihap_sta_delete(whi, sta); + } + } + + sta = next; + } + + splx(s); + + whi->hostap_ch = timeout(wihap_timer, sc, hz * WIHAP_INTERVAL); +} + +static int +wihap_check_rates(struct wi_softc *sc, struct wihap_sta_info *sta, + u_int8_t rates[], int rates_len) +{ + int i; + + sta->rates = 0; + sta->tx_max_rate = 0; + for (i=0; i<rates_len; i++) + switch (rates[i] & 0x7f) { + case 0x02: + sta->rates |= WI_SUPPRATES_1M; + break; + case 0x04: + sta->rates |= WI_SUPPRATES_2M; + if (sta->tx_max_rate<1) + sta->tx_max_rate = 1; + break; + case 0x0b: + sta->rates |= WI_SUPPRATES_5M; + if (sta->tx_max_rate<2) + sta->tx_max_rate = 2; + break; + case 0x16: + sta->rates |= WI_SUPPRATES_11M; + sta->tx_max_rate = 3; + break; + } + + sta->rates &= sc->wi_supprates; + sta->tx_curr_rate = sta->tx_max_rate; + + return (sta->rates == 0 ? -1 : 0); +} + + +/* + * wihap_auth_req() + * + * Handle incoming authentication request. Only handle OPEN + * requests. + */ +static void +wihap_auth_req(struct wi_softc *sc, struct wi_frame *rxfrm, + caddr_t pkt, int len) +{ + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta; + + u_int16_t algo; + u_int16_t seq; + u_int16_t status; + int challenge_len; + u_int8_t challenge[128]; + + struct wi_80211_hdr *resp_hdr; + + if (len<6) + return; + + /* Break open packet. */ + algo = take_hword(&pkt, &len); + seq = take_hword(&pkt, &len); + status = take_hword(&pkt, &len); + challenge_len=0; + if (len>0 && (challenge_len=take_tlv(&pkt, &len, + IEEE80211_ELEMID_CHALLENGE, + challenge, sizeof(challenge)))<0) + return; + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_auth_req: from station: %6D\n", + rxfrm->wi_addr2, ":"); + + switch (algo) { + case IEEE80211_AUTH_ALG_OPEN: + if (seq != 1) { + status = IEEE80211_STATUS_SEQUENCE; + goto fail; + } + challenge_len=0; + break; + case IEEE80211_AUTH_ALG_SHARED: + /* NOT YET */ + default: + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_auth_req: algorithm unsupported: %d\n", + algo); + status = IEEE80211_STATUS_ALG; + goto fail; + } + + sta = wihap_sta_find(whi, rxfrm->wi_addr2); + if (sta == NULL) { + + /* Are we allowing new stations? + */ + if (whi->apflags & WIHAPFL_MAC_FILT) { + status = IEEE80211_STATUS_OTHER; /* XXX */ + goto fail; + } + + /* Check for too many stations. + */ + if (whi->n_stations >= WIHAP_MAX_STATIONS) { + status = IEEE80211_STATUS_TOO_MANY_STATIONS; + goto fail; + } + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_auth_req: new station\n"); + + /* Create new station. */ + sta = wihap_sta_alloc(whi, rxfrm->wi_addr2); + if (sta == NULL) { + /* Out of memory! */ + status = IEEE80211_STATUS_TOO_MANY_STATIONS; + goto fail; + } + } + + sta->flags |= WI_SIFLAGS_AUTHEN; + sta->inactivity_timer = whi->inactivity_time; + status = IEEE80211_STATUS_SUCCESS; + +fail: + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_auth_req: returns status=0x%x\n", status); + + /* Send response. */ + resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf; + bzero(resp_hdr, sizeof(struct wi_80211_hdr)); + resp_hdr->frame_ctl = WI_FTYPE_MGMT | WI_STYPE_MGMT_AUTH; + pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr); + + bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN); + + put_hword(&pkt, algo); + put_hword(&pkt, 2); + put_hword(&pkt, status); + if (challenge_len>0) + put_tlv(&pkt, IEEE80211_ELEMID_CHALLENGE, + challenge, challenge_len); + + wi_mgmt_xmit(sc, sc->wi_txbuf, + 6 + sizeof(struct wi_80211_hdr) + + (challenge_len>0 ? challenge_len+2 : 0) ); +} + + +/* + * wihap_assoc_req() + * + * Handle incoming association and reassociation requests. + */ +static void +wihap_assoc_req(struct wi_softc *sc, struct wi_frame *rxfrm, + caddr_t pkt, int len) +{ + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta; + + struct wi_80211_hdr *resp_hdr; + + u_int16_t capinfo; + u_int16_t lstintvl; + u_int8_t rates[8]; + int rates_len; + int ssid_len; + char ssid[33]; + + + u_int16_t status; + u_int16_t asid = 0; + + if (len<8) + return; + + /* Pull out request parameters. */ + capinfo = take_hword(&pkt, &len); + lstintvl = take_hword(&pkt, &len); + if ((ssid_len=take_tlv(&pkt, &len, IEEE80211_ELEMID_SSID, + ssid, sizeof(ssid)-1))<0) + return; + ssid[ssid_len] = '\0'; + if ((rates_len=take_tlv(&pkt, &len, IEEE80211_ELEMID_RATES, + rates, sizeof(rates)))<0) + return; + if ((rxfrm->wi_frame_ctl & WI_FCTL_STYPE) == WI_STYPE_MGMT_REASREQ) { + /* Reassociation Request-- + * Current AP. (Ignore?) + */ + if (len<6) + return; + } + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_assoc_req: from station %6D\n", + rxfrm->wi_addr2, ":"); + + /* If SSID doesn't match, simply drop. */ + if (strcmp(sc->wi_net_name, ssid) != 0) { + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_assoc_req: bad ssid: '%s'\n", ssid); + return; + } + + /* Is this station authenticated yet? */ + sta = wihap_sta_find(whi, rxfrm->wi_addr2); + if (sta == NULL || !(sta->flags & WI_SIFLAGS_AUTHEN)) { + wihap_sta_deauth(sc, rxfrm->wi_addr2, + IEEE80211_REASON_NOT_AUTHED); + return; + } + + /* Check capinfo. + * Check for ESS, not IBSS. + * Check WEP/PRIVACY flags match. XXX: WEP doesn't work for host AP. + * Refuse stations requesting to be put on CF-polling list. + */ + if ((capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) != + IEEE80211_CAPINFO_ESS || + (sc->wi_use_wep && !(capinfo & IEEE80211_CAPINFO_PRIVACY)) || + (!sc->wi_use_wep && (capinfo & IEEE80211_CAPINFO_PRIVACY)) || + (capinfo & (IEEE80211_CAPINFO_CF_POLLABLE| + IEEE80211_CAPINFO_CF_POLLREQ)) == + IEEE80211_CAPINFO_CF_POLLABLE) { + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_assoc_req: capinfo mismatch: " + "capinfo=0x%x\n", capinfo); + + status = IEEE80211_STATUS_CAPINFO; + goto fail; + } + sta->capinfo = capinfo; + + + /* Check supported rates against ours. */ + if (wihap_check_rates(sc, sta, rates, rates_len)<0) { + status = IEEE80211_STATUS_RATES; + goto fail; + } + + /* Use ASID is allocated by whi_sta_alloc(). */ + asid = sta->asid; + + if (sta->flags & WI_SIFLAGS_ASSOC) { + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_assoc_req: already assoc'ed?\n"); + } + + sta->flags |= WI_SIFLAGS_ASSOC; + sta->inactivity_timer = whi->inactivity_time; + status = IEEE80211_STATUS_SUCCESS; + +fail: + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_assoc_req: returns status=0x%x\n", status); + + /* Send response. */ + resp_hdr = (struct wi_80211_hdr *) sc->wi_txbuf; + bzero(resp_hdr, sizeof(struct wi_80211_hdr)); + resp_hdr->frame_ctl = WI_FTYPE_MGMT | WI_STYPE_MGMT_ASRESP; + pkt = sc->wi_txbuf + sizeof(struct wi_80211_hdr); + + bcopy(rxfrm->wi_addr2, resp_hdr->addr1, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr2, ETHER_ADDR_LEN); + bcopy(sc->arpcom.ac_enaddr, resp_hdr->addr3, ETHER_ADDR_LEN); + + put_hword(&pkt, capinfo); + put_hword(&pkt, status); + put_hword(&pkt, asid); + rates_len=put_rates(&pkt, sc->wi_supprates); + + wi_mgmt_xmit(sc, sc->wi_txbuf, + 8+rates_len+sizeof(struct wi_80211_hdr)); +} + +/* + * wihap_deauth_req() + * + * Handle deauthentication requests. Delete the station. + */ +static void +wihap_deauth_req(struct wi_softc *sc, struct wi_frame *rxfrm, + caddr_t pkt, int len) +{ + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta; + u_int16_t reason; + + if (len<2) + return; + + reason = take_hword(&pkt, &len); + + sta = wihap_sta_find(whi, rxfrm->wi_addr2); + if (sta == NULL) { + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_deauth_req: unknown station: 6D\n", + rxfrm->wi_addr2, ":"); + } + else + wihap_sta_delete(whi, sta); +} + +/* + * wihap_disassoc_req() + * + * Handle disassociation requests. Just reset the assoc flag. + * We'll free up the station resources when we get a deauth + * request or when it times out. + */ +static void +wihap_disassoc_req(struct wi_softc *sc, struct wi_frame *rxfrm, + caddr_t pkt, int len) +{ + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta; + u_int16_t reason; + + if (len<2) + return; + + reason = take_hword(&pkt, &len); + + sta = wihap_sta_find(whi, rxfrm->wi_addr2); + if (sta == NULL) { + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + printf("wihap_disassoc_req: unknown station: 6D\n", + rxfrm->wi_addr2, ":"); + } + else if (!(sta->flags & WI_SIFLAGS_AUTHEN)) { + /* If station is not authenticated, send deauthentication + * frame. + */ + wihap_sta_deauth(sc, rxfrm->wi_addr2, + IEEE80211_REASON_NOT_AUTHED); + return; + } + else + sta->flags &= ~WI_SIFLAGS_ASSOC; +} + +/* + * wihap_debug_frame_type() + * + * Print out frame type. Used in early debugging. + */ +static void +wihap_debug_frame_type(struct wi_frame *rxfrm) +{ + printf("wihap_mgmt_input: len=%d ", rxfrm->wi_dat_len); + + if ((le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_FTYPE) == WI_FTYPE_MGMT) { + + printf("MGMT: "); + + switch (le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) { + case WI_STYPE_MGMT_ASREQ: + printf("assoc req: \n"); + break; + case WI_STYPE_MGMT_ASRESP: + printf("assoc resp: \n"); + break; + case WI_STYPE_MGMT_REASREQ: + printf("reassoc req: \n"); + break; + case WI_STYPE_MGMT_REASRESP: + printf("reassoc resp: \n"); + break; + case WI_STYPE_MGMT_PROBEREQ: + printf("probe req: \n"); + break; + case WI_STYPE_MGMT_PROBERESP: + printf("probe resp: \n"); + break; + case WI_STYPE_MGMT_BEACON: + printf("beacon: \n"); + break; + case WI_STYPE_MGMT_ATIM: + printf("ann traf ind \n"); + break; + case WI_STYPE_MGMT_DISAS: + printf("disassociation: \n"); + break; + case WI_STYPE_MGMT_AUTH: + printf("auth: \n"); + break; + case WI_STYPE_MGMT_DEAUTH: + printf("deauth: \n"); + break; + default: + printf("unknown (stype=0x%x)\n", + le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE); + } + + } + else { + printf("ftype=0x%x (ctl=0x%x)\n", + le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_FTYPE, + le16toh(rxfrm->wi_frame_ctl)); + } +} + +/* + * wihap_mgmt_input: + * + * Called for each management frame received in host ap mode. + * wihap_mgmt_input() is expected to free the mbuf. + */ +void +wihap_mgmt_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m) +{ + caddr_t pkt; + int len; + + if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) + wihap_debug_frame_type(rxfrm); + + pkt = mtod(m, caddr_t) + WI_802_11_OFFSET_RAW; + len = m->m_len - WI_802_11_OFFSET_RAW; + + if ((rxfrm->wi_frame_ctl & WI_FCTL_FTYPE) == WI_FTYPE_MGMT) { + + switch (le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_STYPE) { + case WI_STYPE_MGMT_ASREQ: + wihap_assoc_req(sc, rxfrm, pkt, len); + break; + case WI_STYPE_MGMT_ASRESP: + break; + case WI_STYPE_MGMT_REASREQ: + wihap_assoc_req(sc, rxfrm, pkt, len); + break; + case WI_STYPE_MGMT_REASRESP: + break; + case WI_STYPE_MGMT_PROBEREQ: + break; + case WI_STYPE_MGMT_PROBERESP: + break; + case WI_STYPE_MGMT_BEACON: + break; + case WI_STYPE_MGMT_ATIM: + break; + case WI_STYPE_MGMT_DISAS: + wihap_disassoc_req(sc, rxfrm, pkt, len); + break; + case WI_STYPE_MGMT_AUTH: + wihap_auth_req(sc, rxfrm, pkt, len); + break; + case WI_STYPE_MGMT_DEAUTH: + wihap_deauth_req(sc, rxfrm, pkt, len); + break; + } + + } + + m_freem(m); +} + +/* + * wihap_sta_is_assoc() + * + * Determine if a station is assoc'ed. Update its activity + * counter as a side-effect. + */ +static int +wihap_sta_is_assoc(struct wihap_info *whi, u_int8_t addr[]) +{ + struct wihap_sta_info *sta; + + sta = wihap_sta_find(whi, addr); + if (sta!=NULL && (sta->flags & WI_SIFLAGS_ASSOC)) { + /* Keep it active. */ + sta->inactivity_timer = whi->inactivity_time; + return(1); + } + else + return(0); +} + +/* + * wihap_check_tx() + * + * Determine if a station is assoc'ed, get its tx rate, and update + * its activity. + */ +int +wihap_check_tx(struct wihap_info *whi, u_int8_t addr[], u_int8_t *txrate) +{ + struct wihap_sta_info *sta; + static u_int8_t txratetable[] = { 10, 20, 55, 110 }; + + if (addr[0] & 0x01) { + *txrate = 0; /* XXX: multicast rate? */ + return(1); + } + sta = wihap_sta_find(whi, addr); + if (sta!=NULL && (sta->flags & WI_SIFLAGS_ASSOC)) { + /* Keep it active. */ + sta->inactivity_timer = whi->inactivity_time; + *txrate = txratetable[ sta->tx_curr_rate ]; + return(1); + } + return(0); +} + +/* + * wihap_data_input() + * + * Handle all data input on interface when in Host AP mode. + * Some packets are destined for this machine, others are + * repeated to other stations. + * + * If wihap_data_input() returns a non-zero, it has processed + * the packet and will free the mbuf. + */ +int +wihap_data_input(struct wi_softc *sc, struct wi_frame *rxfrm, struct mbuf *m) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta; + int mcast; + + /* TODS flag must be set. */ + if (!(le16toh(rxfrm->wi_frame_ctl) & WI_FCTL_TODS)) { + if (ifp->if_flags & IFF_DEBUG) + printf("wihap_data_input: no TODS src=%6D\n", + rxfrm->wi_addr2, ":"); + return(1); + } + + /* Check BSSID. (Is this necessary?) */ + if (!addr_cmp(rxfrm->wi_addr1, sc->arpcom.ac_enaddr)) { + if (ifp->if_flags & IFF_DEBUG) + printf("wihap_data_input: incorrect bss: %6D\n", + rxfrm->wi_addr1, ":"); + return(1); + } + + /* Find source station. */ + sta = wihap_sta_find(whi, rxfrm->wi_addr2); + + /* Source station must be associated. */ + if (sta == NULL || !(sta->flags & WI_SIFLAGS_ASSOC)) { + if (ifp->if_flags & IFF_DEBUG) + printf("wihap_data_input: dropping unassoc src %6D\n", + rxfrm->wi_addr2, ":"); + return(1); + } + + sta->inactivity_timer = whi->inactivity_time; + sta->sig_info = rxfrm->wi_q_info; + + /* Repeat this packet to BSS? */ + mcast = (rxfrm->wi_addr3[0] & 0x01) != 0; + if (mcast || wihap_sta_is_assoc(whi, rxfrm->wi_addr3)) { + + /* If it's multicast, make a copy. + */ + if (mcast) { + m = m_copym(m, 0, M_COPYALL, M_DONTWAIT); + if (m == NULL) + return(0); + m->m_flags |= M_MCAST; /* XXX */ + } + + /* Queue up for repeating. + */ + IF_HANDOFF(&ifp->if_snd, m, ifp); + return (!mcast); + } + + return (0); +} + +/* + * wihap_ioctl() + * + * Handle Host AP specific ioctls. Called from wi_ioctl(). + */ +int +wihap_ioctl(struct wi_softc *sc, u_long command, caddr_t data) +{ + int error = 0; + struct ifreq *ifr = (struct ifreq *) data; + struct wihap_info *whi = &sc->wi_hostap_info; + struct wihap_sta_info *sta; + struct hostap_getall reqall; + struct hostap_sta reqsta; + struct hostap_sta stabuf; + int n; + int flag; + + if (!(sc->arpcom.ac_if.if_flags & IFF_RUNNING)) + return ENODEV; + + switch (command) { + case SIOCHOSTAP_DEL: + if ((error = suser(curthread))) + break; + if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta)))) + break; + sta = wihap_sta_find(whi, reqsta.addr); + if (sta == NULL) + error = ENOENT; + else { + /* Disassociate station. */ + if (sta->flags & WI_SIFLAGS_ASSOC) + wihap_sta_disassoc(sc, sta->addr, + IEEE80211_REASON_ASSOC_LEAVE); + /* Deauth station. */ + if (sta->flags & WI_SIFLAGS_AUTHEN) + wihap_sta_deauth(sc, sta->addr, + IEEE80211_REASON_AUTH_LEAVE); + + wihap_sta_delete(whi, sta); + } + break; + + case SIOCHOSTAP_GET: + if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta)))) + break; + sta = wihap_sta_find(whi, reqsta.addr); + if (sta == NULL) + error = ENOENT; + else { + reqsta.flags = sta->flags; + reqsta.asid = sta->asid; + reqsta.capinfo = sta->capinfo; + reqsta.sig_info = sta->sig_info; + reqsta.rates = sta->rates; + + error = copyout(&reqsta, ifr->ifr_data, + sizeof(reqsta)); + } + break; + + case SIOCHOSTAP_ADD: + if ((error = suser(curthread))) + break; + if ((error = copyin(ifr->ifr_data, &reqsta, sizeof(reqsta)))) + break; + sta = wihap_sta_find(whi, reqsta.addr); + if (sta != NULL) { + error = EEXIST; + break; + } + if (whi->n_stations >= WIHAP_MAX_STATIONS) { + error = ENOSPC; + break; + } + sta = wihap_sta_alloc(whi, reqsta.addr); + sta->flags = reqsta.flags; + sta->inactivity_timer = whi->inactivity_time; + break; + + case SIOCHOSTAP_SFLAGS: + if ((error = suser(curthread))) + break; + if ((error = copyin(ifr->ifr_data, &flag, sizeof(int)))) + break; + + whi->apflags = + (whi->apflags & WIHAPFL_CANTCHANGE) | + (flag & ~WIHAPFL_CANTCHANGE); + break; + + case SIOCHOSTAP_GFLAGS: + flag = (int) whi->apflags; + error = copyout(&flag, ifr->ifr_data, sizeof(int)); + break; + + case SIOCHOSTAP_GETALL: + if ((error = copyin(ifr->ifr_data, &reqall, sizeof(reqall)))) + break; + + reqall.nstations = whi->n_stations; + n = 0; + sta = LIST_FIRST(&whi->sta_list); + while (sta && reqall.size >= n+sizeof(struct hostap_sta)) { + + bcopy(sta->addr, stabuf.addr, ETHER_ADDR_LEN); + stabuf.asid = sta->asid; + stabuf.flags = sta->flags; + stabuf.capinfo = sta->capinfo; + stabuf.sig_info = sta->sig_info; + stabuf.rates = sta->rates; + + error = copyout(&stabuf, (caddr_t) reqall.addr + n, + sizeof(struct hostap_sta)); + if (error) + break; + + sta = LIST_NEXT(sta, list); + n += sizeof(struct hostap_sta); + } + + if (!error) + error = copyout(&reqall, ifr->ifr_data, + sizeof(reqall)); + break; + default: + printf("wihap_ioctl: i shouldn't get other ioctls!\n"); + error = EINVAL; + } + + return(error); +} + +#endif /* WI_HOSTAP */ diff --git a/sys/dev/wi/wi_hostap.h b/sys/dev/wi/wi_hostap.h new file mode 100644 index 0000000..493b5ad --- /dev/null +++ b/sys/dev/wi/wi_hostap.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2002 + * Thomas Skibo <skibo@pacbell.net>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Thomas Skibo. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Thomas Skibo AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Thomas Skibo OR HIS DRINKING PALS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __WI_HOSTAP_H__ +#define __WI_HOSTAP_H__ + +#define WIHAP_MAX_STATIONS 1800 + +struct hostap_sta { + u_int8_t addr[6]; + u_int16_t asid; + u_int16_t flags; + u_int16_t sig_info; /* 15:8 signal, 7:0 noise */ + u_int16_t capinfo; + u_int8_t rates; +}; + +#define HOSTAP_FLAGS_AUTHEN 0x0001 +#define HOSTAP_FLAGS_ASSOC 0x0002 +#define HOSTAP_FLAGS_PERM 0x0004 + +#define SIOCHOSTAP_GET _IOWR('i', 210, struct ifreq) +#define SIOCHOSTAP_ADD _IOWR('i', 211, struct ifreq) +#define SIOCHOSTAP_DEL _IOWR('i', 212, struct ifreq) +#define SIOCHOSTAP_GETALL _IOWR('i', 213, struct ifreq) +#define SIOCHOSTAP_GFLAGS _IOWR('i', 214, struct ifreq) +#define SIOCHOSTAP_SFLAGS _IOWR('i', 215, struct ifreq) + +/* Flags for SIOCHOSTAP_GFLAGS/SFLAGS */ +#define WIHAPFL_ACTIVE 0x0001 +#define WIHAPFL_MAC_FILT 0x0002 + +/* Flags set inernally only: */ +#define WIHAPFL_CANTCHANGE (WIHAPFL_ACTIVE) + +struct hostap_getall { + int nstations; + struct hostap_sta *addr; + int size; +}; + + + +#ifdef _KERNEL +struct wihap_sta_info { + LIST_ENTRY(wihap_sta_info) list; + LIST_ENTRY(wihap_sta_info) hash; + + u_int8_t addr[6]; + u_short flags; + int inactivity_timer; + + u_int16_t asid; + u_int16_t capinfo; + u_int16_t sig_info; /* 15:8 signal, 7:0 noise */ + u_int8_t rates; + u_int8_t tx_curr_rate; + u_int8_t tx_max_rate; +}; + +#define WI_SIFLAGS_ASSOC HOSTAP_FLAGS_ASSOC +#define WI_SIFLAGS_AUTHEN HOSTAP_FLAGS_AUTHEN +#define WI_SIFLAGS_PERM HOSTAP_FLAGS_PERM + +#define WI_STA_HASH_SIZE 113 + +#if WI_STA_HASH_SIZE*16 >= 2007 /* will generate ASID's too large. */ +#error "WI_STA_HASH_SIZE too big" +#endif +#if WI_STA_HASH_SIZE*16 < WIHAP_MAX_STATIONS +#error "WI_STA_HASH_SIZE too small" +#endif + +struct wihap_info { + LIST_HEAD(sta_list, wihap_sta_info) sta_list; + LIST_HEAD(sta_hash, wihap_sta_info) sta_hash[WI_STA_HASH_SIZE]; + + u_int16_t apflags; + + int n_stations; + u_int16_t asid_inuse_mask[WI_STA_HASH_SIZE]; + + int inactivity_time; + struct callout_handle hostap_ch; +}; + +#define WIHAP_INTERVAL 5 +#define WIHAP_DFLT_INACTIVITY_TIME (120/WIHAP_INTERVAL) /* 2 minutes */ + +struct wi_softc; +struct wi_frame; + +void wihap_timer __P((void *)); +void wihap_mgmt_input __P((struct wi_softc *, struct wi_frame *, + struct mbuf *)); +int wihap_data_input __P((struct wi_softc *, struct wi_frame *, + struct mbuf *)); +int wihap_check_tx __P((struct wihap_info *, u_int8_t [], + u_int8_t *)); +void wihap_init __P((struct wi_softc *)); +void wihap_shutdown __P((struct wi_softc *)); +int wihap_ioctl __P((struct wi_softc *, u_long, caddr_t)); + +#endif /* _KERNEL */ +#endif /* __WI_HOSTAP_H__ */ |