summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/wi/if_wavelan_ieee.h16
-rw-r--r--sys/dev/wi/if_wi.c448
-rw-r--r--sys/dev/wi/if_wi_pccard.c3
-rw-r--r--sys/dev/wi/if_wi_pci.c3
-rw-r--r--sys/dev/wi/if_wireg.h20
-rw-r--r--sys/dev/wi/if_wivar.h7
-rw-r--r--sys/dev/wi/wi_hostap.c1189
-rw-r--r--sys/dev/wi/wi_hostap.h136
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__ */
OpenPOWER on IntegriCloud