diff options
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/bwfm.c | 732 | ||||
-rw-r--r-- | sys/dev/ic/bwfmreg.h | 30 | ||||
-rw-r--r-- | sys/dev/ic/bwfmvar.h | 32 |
3 files changed, 477 insertions, 317 deletions
diff --git a/sys/dev/ic/bwfm.c b/sys/dev/ic/bwfm.c index 0a8caa0bff1..cf6ada71ea3 100644 --- a/sys/dev/ic/bwfm.c +++ b/sys/dev/ic/bwfm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bwfm.c,v 1.13 2017/10/19 11:16:17 patrick Exp $ */ +/* $OpenBSD: bwfm.c,v 1.14 2017/10/21 20:43:03 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se> @@ -61,7 +61,6 @@ void bwfm_stop(struct ifnet *); void bwfm_watchdog(struct ifnet *); int bwfm_ioctl(struct ifnet *, u_long, caddr_t); int bwfm_media_change(struct ifnet *); -void bwfm_media_status(struct ifnet *, struct ifmediareq *); int bwfm_chip_attach(struct bwfm_softc *); int bwfm_chip_detach(struct bwfm_softc *, int); @@ -93,8 +92,24 @@ int bwfm_fwvar_var_set_data(struct bwfm_softc *, char *, void *, size_t); int bwfm_fwvar_var_get_int(struct bwfm_softc *, char *, uint32_t *); int bwfm_fwvar_var_set_int(struct bwfm_softc *, char *, uint32_t); +void bwfm_connect(struct bwfm_softc *); void bwfm_scan(struct bwfm_softc *); -void bwfm_scan_timeout(void *); + +void bwfm_task(void *); +void bwfm_do_async(struct bwfm_softc *, + void (*)(struct bwfm_softc *, void *), void *, int); + +int bwfm_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void bwfm_delete_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +int bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *, + int, int, int); +int bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int); + +void bwfm_set_key_cb(struct bwfm_softc *, void *); +void bwfm_delete_key_cb(struct bwfm_softc *, void *); +void bwfm_newstate_cb(struct bwfm_softc *, void *); void bwfm_rx(struct bwfm_softc *, char *, size_t); void bwfm_rx_event(struct bwfm_softc *, char *, size_t); @@ -142,18 +157,19 @@ bwfm_attach(struct bwfm_softc *sc) } printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr)); - /* - * This hardware is supposed to be run with wpa_supplicant, but since - * we want to handle basic authentication in our stack, we _need_ to - * rely on the firmware's handshake algorithm. If it doesn't exist we - * will have to think about something new. - */ - if (bwfm_fwvar_var_get_int(sc, "sup_wpa", &tmp)) { - printf("%s: no supplicant in firmware, bailing\n", DEVNAME(sc)); - return; - } + /* Init host async commands ring. */ + sc->sc_cmdq.cur = sc->sc_cmdq.next = sc->sc_cmdq.queued = 0; + sc->sc_taskq = taskq_create(DEVNAME(sc), 1, IPL_SOFTNET, 0); + task_set(&sc->sc_task, bwfm_task, sc); + + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; - ic->ic_caps = IEEE80211_C_RSN; /* WPA/RSN */ + ic->ic_caps = + IEEE80211_C_RSN | /* WPA/RSN */ + IEEE80211_C_SCANALL | /* device scans all channels at once */ + IEEE80211_C_SCANALLBAND; /* device scans all bands at once */ if (bwfm_fwvar_var_get_int(sc, "nmode", &nmode)) nmode = 0; @@ -214,9 +230,6 @@ bwfm_attach(struct bwfm_softc *sc) /* IBSS channel undefined for now. */ ic->ic_ibss_chan = &ic->ic_channels[0]; - /* Init some net80211 stuff we abuse. */ - ieee80211_node_attach(ifp); - ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = bwfm_ioctl; @@ -224,13 +237,16 @@ bwfm_attach(struct bwfm_softc *sc) ifp->if_watchdog = bwfm_watchdog; memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); - ifmedia_init(&sc->sc_media, 0, bwfm_media_change, bwfm_media_status); - ifmedia_set(&sc->sc_media, - IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0)); if_attach(ifp); - memcpy(((struct arpcom *)ifp)->ac_enaddr, ic->ic_myaddr, - ETHER_ADDR_LEN); - ether_ifattach(ifp); + ieee80211_ifattach(ifp); + + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = bwfm_newstate; + ic->ic_send_mgmt = bwfm_send_mgmt; + ic->ic_set_key = bwfm_set_key; + ic->ic_delete_key = bwfm_delete_key; + + ieee80211_media_init(ifp, bwfm_media_change, ieee80211_media_status); } int @@ -238,7 +254,9 @@ bwfm_detach(struct bwfm_softc *sc, int flags) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; - ether_ifdetach(ifp); + task_del(sc->sc_taskq, &sc->sc_task); + taskq_destroy(sc->sc_taskq); + ieee80211_ifdetach(ifp); if_detach(ifp); return 0; } @@ -290,9 +308,7 @@ void bwfm_init(struct ifnet *ifp) { struct bwfm_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; uint8_t evmask[BWFM_EVENT_MASK_LEN]; - struct bwfm_ext_join_params *params; struct bwfm_join_pref_params join_pref[2]; if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) { @@ -320,8 +336,10 @@ bwfm_init(struct ifnet *ifp) return; } evmask[BWFM_E_IF / 8] |= 1 << (BWFM_E_IF % 8); - evmask[BWFM_E_ESCAN_RESULT / 8] |= 1 << (BWFM_E_ESCAN_RESULT % 8); + evmask[BWFM_E_LINK / 8] |= 1 << (BWFM_E_LINK % 8); + evmask[BWFM_E_ASSOC / 8] |= 1 << (BWFM_E_ASSOC % 8); evmask[BWFM_E_SET_SSID / 8] |= 1 << (BWFM_E_SET_SSID % 8); + evmask[BWFM_E_ESCAN_RESULT / 8] |= 1 << (BWFM_E_ESCAN_RESULT % 8); if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) { printf("%s: could not set event mask\n", DEVNAME(sc)); return; @@ -360,84 +378,11 @@ bwfm_init(struct ifnet *ifp) bwfm_fwvar_var_set_int(sc, "toe", 0); /* - * Use the firmware supplicant to handle the WPA handshake. - * As long as we're still figuring things out this is ok, but - * it would be better to handle the handshake in our stack. + * The firmware supplicant can handle the WPA handshake for + * us, but we honestly want to do this ourselves, so disable + * the firmware supplicant and let our stack handle it. */ - bwfm_fwvar_var_set_int(sc, "sup_wpa", 1); - - /* - * OPEN: Open or WPA/WPA2 on newer Chips/Firmware. - * SHARED KEY: WEP. - * AUTO: Automatic, probably for older Chips/Firmware. - */ - if (ic->ic_flags & IEEE80211_F_PSK) { - struct bwfm_wsec_pmk pmk; - uint32_t wsec = 0; - uint32_t wpa = 0; - int i; - - pmk.key_len = htole16(sizeof(ic->ic_psk) << 1); - pmk.flags = htole16(BWFM_WSEC_PASSPHRASE); - for (i = 0; i < 32; i++) - snprintf(&pmk.key[2 * i], 3, "%02x", - ic->ic_psk[i]); - bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_WSEC_PMK, &pmk, - sizeof(pmk)); - - if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) { - if (ic->ic_rsnakms & IEEE80211_AKM_PSK) - wpa |= BWFM_WPA_AUTH_WPA_PSK; - if (ic->ic_rsnakms & IEEE80211_AKM_8021X) - wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED; - } - if (ic->ic_rsnprotos & IEEE80211_PROTO_RSN) { - if (ic->ic_rsnakms & IEEE80211_AKM_PSK) - wpa |= BWFM_WPA_AUTH_WPA2_PSK; - if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_PSK) - wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256; - if (ic->ic_rsnakms & IEEE80211_AKM_8021X) - wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED; - if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_8021X) - wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256; - } - if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_TKIP || - ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP) - wsec |= BWFM_WSEC_TKIP; - if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_CCMP || - ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP) - wsec |= BWFM_WSEC_AES; - - bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa); - bwfm_fwvar_var_set_int(sc, "wsec", wsec); - } else { - bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED); - bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE); - } - bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN); - bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE); - - if (ic->ic_des_esslen && ic->ic_des_esslen < BWFM_MAX_SSID_LEN) { - params = malloc(sizeof(*params), M_TEMP, M_WAITOK | M_ZERO); - memcpy(params->ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen); - params->ssid.len = htole32(ic->ic_des_esslen); - memset(params->assoc.bssid, 0xff, sizeof(params->assoc.bssid)); - params->scan.scan_type = -1; - params->scan.nprobes = htole32(-1); - params->scan.active_time = htole32(-1); - params->scan.passive_time = htole32(-1); - params->scan.home_time = htole32(-1); - if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) { - struct bwfm_join_params join; - memset(&join, 0, sizeof(join)); - memcpy(join.ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen); - join.ssid.len = htole32(ic->ic_des_esslen); - memset(join.assoc.bssid, 0xff, sizeof(join.assoc.bssid)); - bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, - sizeof(join)); - } - free(params, M_TEMP, sizeof(*params)); - } + bwfm_fwvar_var_set_int(sc, "sup_wpa", 0); #if 0 /* TODO: set these on proper ioctl */ @@ -447,6 +392,8 @@ bwfm_init(struct ifnet *ifp) ifp->if_flags |= IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); + + ieee80211_begin_scan(ifp); } void @@ -463,6 +410,8 @@ bwfm_stop(struct ifnet *ifp) /* In case we were scanning, release the scan "lock". */ ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED; + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1); bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 0); } @@ -488,23 +437,13 @@ bwfm_watchdog(struct ifnet *ifp) int bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { - struct bwfm_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *)data; - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_nwid nwid; - struct ieee80211_wpapsk *psk; - struct ieee80211_nodereq *nr, nrbuf; - struct ieee80211_nodereq_all *na; - struct ieee80211_node *ni; - int s, i, error = 0; - uint32_t flags; + int s, error = 0; s = splnet(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; - bwfm_init(ifp); - break; + /* FALLTHROUGH */ case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) @@ -514,167 +453,8 @@ bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) bwfm_stop(ifp); } break; - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); - break; - case SIOCG80211ALLNODES: - na = (struct ieee80211_nodereq_all *)data; - na->na_nodes = i = 0; - ni = RBT_MIN(ieee80211_tree, &ic->ic_tree); - while (ni && na->na_size >= - i + sizeof(struct ieee80211_nodereq)) { - ieee80211_node2req(ic, ni, &nrbuf); - error = copyout(&nrbuf, (caddr_t)na->na_node + i, - sizeof(struct ieee80211_nodereq)); - if (error) - break; - i += sizeof(struct ieee80211_nodereq); - na->na_nodes++; - ni = RBT_NEXT(ieee80211_tree, ni); - } - break; - case SIOCG80211FLAGS: - flags = ic->ic_flags; -#ifndef IEEE80211_STA_ONLY - if (ic->ic_opmode != IEEE80211_M_HOSTAP) -#endif - flags &= ~IEEE80211_F_HOSTAPMASK; - ifr->ifr_flags = flags >> IEEE80211_F_USERSHIFT; - break; - case SIOCS80211FLAGS: - if ((error = suser(curproc, 0)) != 0) - break; - flags = (u_int32_t)ifr->ifr_flags << IEEE80211_F_USERSHIFT; - if ( -#ifndef IEEE80211_STA_ONLY - ic->ic_opmode != IEEE80211_M_HOSTAP && -#endif - (flags & IEEE80211_F_HOSTAPMASK)) { - error = EINVAL; - break; - } - ic->ic_flags = (ic->ic_flags & ~IEEE80211_F_USERMASK) | flags; - error = ENETRESET; - break; - case SIOCG80211NODE: - nr = (struct ieee80211_nodereq *)data; - ni = ieee80211_find_node(ic, nr->nr_macaddr); - if (ni == NULL) { - error = ENOENT; - break; - } - ieee80211_node2req(ic, ni, nr); - break; - case SIOCS80211NODE: - if ((error = suser(curproc, 0)) != 0) - break; -#ifndef IEEE80211_STA_ONLY - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - error = EINVAL; - break; - } -#endif - nr = (struct ieee80211_nodereq *)data; - - ni = ieee80211_find_node(ic, nr->nr_macaddr); - if (ni == NULL) - ni = ieee80211_alloc_node(ic, nr->nr_macaddr); - if (ni == NULL) { - error = ENOENT; - break; - } - - if (nr->nr_flags & IEEE80211_NODEREQ_COPY) - ieee80211_req2node(ic, nr, ni); - break; - case SIOCS80211NWID: - if ((error = suser(curproc, 0)) != 0) - break; - if ((error = copyin(ifr->ifr_data, &nwid, sizeof(nwid))) != 0) - break; - if (nwid.i_len > IEEE80211_NWID_LEN) { - error = EINVAL; - break; - } - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = nwid.i_len; - memcpy(ic->ic_des_essid, nwid.i_nwid, nwid.i_len); - error = ENETRESET; - break; - case SIOCG80211NWID: - memset(&nwid, 0, sizeof(nwid)); - nwid.i_len = ic->ic_des_esslen; - memcpy(nwid.i_nwid, ic->ic_des_essid, nwid.i_len); - error = copyout(&nwid, ifr->ifr_data, sizeof(nwid)); - break; - case SIOCS80211SCAN: - if ((error = suser(curproc, 0)) != 0) - break; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - if (ic->ic_scan_lock == IEEE80211_SCAN_UNLOCKED) { - ieee80211_clean_cached(ic); - bwfm_scan(sc); - timeout_set(&sc->sc_scan_timeout, - bwfm_scan_timeout, sc); - timeout_add_msec(&sc->sc_scan_timeout, - 10000); - } - /* Let the userspace process wait for completion */ - error = tsleep(&ic->ic_scan_lock, PCATCH, "80211scan", - hz * IEEE80211_SCAN_TIMEOUT); - break; - case SIOCS80211WPAPSK: - if ((error = suser(curproc, 0)) != 0) - break; - psk = (struct ieee80211_wpapsk *)data; - if (psk->i_enabled) { - ic->ic_flags |= IEEE80211_F_PSK; - memcpy(ic->ic_psk, psk->i_psk, sizeof(ic->ic_psk)); - } else { - ic->ic_flags &= ~IEEE80211_F_PSK; - memset(ic->ic_psk, 0, sizeof(ic->ic_psk)); - } - error = ENETRESET; - break; - case SIOCG80211WPAPSK: - psk = (struct ieee80211_wpapsk *)data; - if (ic->ic_flags & IEEE80211_F_PSK) { - psk->i_enabled = 1; - /* do not show any keys to non-root user */ - if (suser(curproc, 0) != 0) { - psk->i_enabled = 2; - memset(psk->i_psk, 0, sizeof(psk->i_psk)); - break; /* return ok but w/o key */ - } - memcpy(psk->i_psk, ic->ic_psk, sizeof(psk->i_psk)); - } else - psk->i_enabled = 0; - break; - case SIOCG80211ALLCHANS: - case SIOCG80211STATS: - case SIOCS80211WPAPARMS: - case SIOCG80211WPAPARMS: - error = ieee80211_ioctl(ifp, cmd, data); - break; - case SIOCS80211NWKEY: - case SIOCG80211NWKEY: - case SIOCS80211WMMPARMS: - case SIOCG80211WMMPARMS: - case SIOCS80211KEYAVAIL: - case SIOCS80211KEYRUN: - case SIOCS80211POWER: - case SIOCG80211POWER: - case SIOCS80211BSSID: - case SIOCG80211BSSID: - case SIOCS80211CHANNEL: - case SIOCG80211CHANNEL: - case SIOCS80211TXPOWER: - case SIOCG80211TXPOWER: - case SIOCS80211DELNODE: default: - error = ether_ioctl(ifp, &sc->sc_ic.ic_ac, cmd, data); + error = ieee80211_ioctl(ifp, cmd, data); } if (error == ENETRESET) { if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == @@ -691,12 +471,18 @@ bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) int bwfm_media_change(struct ifnet *ifp) { - return 0; -} + int error; -void -bwfm_media_status(struct ifnet *ifp, struct ifmediareq *imr) -{ + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + bwfm_stop(ifp); + bwfm_init(ifp); + } + return 0; } /* Chip initialization (SDIO, PCIe) */ @@ -1273,15 +1059,91 @@ bwfm_fwvar_var_set_int(struct bwfm_softc *sc, char *name, uint32_t data) /* 802.11 code */ void -bwfm_scan(struct bwfm_softc *sc) +bwfm_connect(struct bwfm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; + struct bwfm_ext_join_params *params; + uint8_t buf[64]; /* XXX max WPA/RSN/WMM IE length */ + uint8_t *frm; + + /* + * OPEN: Open or WPA/WPA2 on newer Chips/Firmware. + * SHARED KEY: WEP. + * AUTO: Automatic, probably for older Chips/Firmware. + */ + if (ic->ic_flags & IEEE80211_F_RSNON) { + uint32_t wsec = 0; + uint32_t wpa = 0; + + /* tell firmware to add WPA/RSN IE to (re)assoc request */ + if (ic->ic_bss->ni_rsnprotos == IEEE80211_PROTO_RSN) + frm = ieee80211_add_rsn(buf, ic, ic->ic_bss); + else + frm = ieee80211_add_wpa(buf, ic, ic->ic_bss); + bwfm_fwvar_var_set_data(sc, "wpaie", buf, frm - buf); + + if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) { + if (ic->ic_rsnakms & IEEE80211_AKM_PSK) + wpa |= BWFM_WPA_AUTH_WPA_PSK; + if (ic->ic_rsnakms & IEEE80211_AKM_8021X) + wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED; + } + if (ic->ic_rsnprotos & IEEE80211_PROTO_RSN) { + if (ic->ic_rsnakms & IEEE80211_AKM_PSK) + wpa |= BWFM_WPA_AUTH_WPA2_PSK; + if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_PSK) + wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256; + if (ic->ic_rsnakms & IEEE80211_AKM_8021X) + wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED; + if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_8021X) + wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256; + } + if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_TKIP || + ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP) + wsec |= BWFM_WSEC_TKIP; + if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_CCMP || + ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP) + wsec |= BWFM_WSEC_AES; + + bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa); + bwfm_fwvar_var_set_int(sc, "wsec", wsec); + } else { + bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED); + bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE); + } + bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN); + bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE); + + if (ic->ic_des_esslen && ic->ic_des_esslen < BWFM_MAX_SSID_LEN) { + params = malloc(sizeof(*params), M_TEMP, M_WAITOK | M_ZERO); + memcpy(params->ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen); + params->ssid.len = htole32(ic->ic_des_esslen); + memset(params->assoc.bssid, 0xff, sizeof(params->assoc.bssid)); + params->scan.scan_type = -1; + params->scan.nprobes = htole32(-1); + params->scan.active_time = htole32(-1); + params->scan.passive_time = htole32(-1); + params->scan.home_time = htole32(-1); + if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) { + struct bwfm_join_params join; + memset(&join, 0, sizeof(join)); + memcpy(join.ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen); + join.ssid.len = htole32(ic->ic_des_esslen); + memset(join.assoc.bssid, 0xff, sizeof(join.assoc.bssid)); + bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, + sizeof(join)); + } + free(params, M_TEMP, sizeof(*params)); + } +} + +void +bwfm_scan(struct bwfm_softc *sc) +{ struct bwfm_escan_params *params; uint32_t nssid = 0, nchannel = 0; size_t params_size; - ic->ic_scan_lock = IEEE80211_SCAN_LOCKED; - #if 0 /* Active scan is used for scanning for an SSID */ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 0); @@ -1322,16 +1184,6 @@ bwfm_scan(struct bwfm_softc *sc) } void -bwfm_scan_timeout(void *arg) -{ - struct bwfm_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - DPRINTF(("%s: scan timeout\n", DEVNAME(sc))); - ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED; - wakeup(&ic->ic_scan_lock); -} - -void bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len) { struct ieee80211com *ic = &sc->sc_ic; @@ -1341,8 +1193,6 @@ bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len) struct mbuf *m; char *mb; - printf("%s: buf %p len %lu\n", __func__, buf, len); - if (len >= sizeof(e->ehdr) && ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL && memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 && @@ -1365,15 +1215,26 @@ bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len) memcpy(mb, buf, len); m->m_pkthdr.len = m->m_len = len; - ml_enqueue(&ml, m); - if_input(ifp, &ml); + if ((ic->ic_flags & IEEE80211_F_RSNON) && + len >= sizeof(e->ehdr) && + ntohs(e->ehdr.ether_type) == ETHERTYPE_PAE) { + ifp->if_ipackets++; +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif + ieee80211_eapol_key_input(ic, m, ic->ic_bss); + } else { + ml_enqueue(&ml, m); + if_input(ifp, &ml); + } } void bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len) { struct ieee80211com *ic = &sc->sc_ic; - //struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = &ic->ic_if; struct bwfm_event *e = (void *)buf; if (ntohl(e->msg.event_type) >= BWFM_E_LAST) @@ -1385,9 +1246,7 @@ bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len) struct bwfm_bss_info *bss; int i; if (ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) { - timeout_del(&sc->sc_scan_timeout); - ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED; - wakeup(&ic->ic_scan_lock); + ieee80211_end_scan(ifp); break; } len -= sizeof(*e); @@ -1410,6 +1269,25 @@ bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len) } break; } + case BWFM_E_SET_SSID: + if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case BWFM_E_ASSOC: + if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) + ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); + else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case BWFM_E_LINK: + if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS && + ntohl(e->msg.reason) == 0) + break; + /* Link status has changed */ + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; default: printf("%s: buf %p len %lu datalen %u code %u status %u" " reason %u\n", __func__, buf, len, ntohl(e->msg.datalen), @@ -1423,17 +1301,243 @@ void bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len) { struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_rxinfo rxi; + struct ieee80211_frame *wh; struct ieee80211_node *ni; - int chan; - - ni = ieee80211_alloc_node(ic, bss->bssid); - IEEE80211_ADDR_COPY(ni->ni_macaddr, bss->bssid); - IEEE80211_ADDR_COPY(ni->ni_bssid, bss->bssid); - ni->ni_esslen = min(bss->ssid_len, sizeof(bss->ssid)); - ni->ni_esslen = min(ni->ni_esslen, IEEE80211_NWID_LEN); - memcpy(ni->ni_essid, bss->ssid, ni->ni_esslen); - chan = (bss->chanspec >> BWFM_CHANSPEC_CHAN_SHIFT) - & BWFM_CHANSPEC_CHAN_MASK; - ni->ni_chan = &ic->ic_channels[chan]; - ni->ni_rssi = letoh32(bss->rssi); + struct mbuf *m; + uint32_t pktlen, ieslen; + uint16_t iesoff; + + iesoff = letoh16(bss->ie_offset); + ieslen = letoh32(bss->ie_length); + if (ieslen > len - iesoff) + return; + + /* Build a fake beacon frame to let net80211 do all the parsing. */ + pktlen = sizeof(*wh) + ieslen + 12; + if (__predict_false(pktlen > MCLBYTES)) + return; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (__predict_false(m == NULL)) + return; + if (pktlen > MHLEN) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_free(m); + return; + } + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(uint16_t *)wh->i_dur = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, bss->bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, bss->bssid); + *(uint16_t *)wh->i_seq = 0; + memset(&wh[1], 0, 12); + ((uint16_t *)(&wh[1]))[4] = bss->beacon_period; + ((uint16_t *)(&wh[1]))[5] = bss->capability; + memcpy(((uint8_t *)&wh[1]) + 12, ((uint8_t *)bss) + iesoff, ieslen); + + /* Finalize mbuf. */ + m->m_pkthdr.len = m->m_len = pktlen; + ni = ieee80211_find_rxnode(ic, wh); + rxi.rxi_flags = 0; + rxi.rxi_rssi = letoh32(bss->rssi); + rxi.rxi_tstamp = 0; + ieee80211_input(ifp, m, ni, &rxi); + /* Node is no longer needed. */ + ieee80211_release_node(ic, ni); +} + +void +bwfm_task(void *arg) +{ + struct bwfm_softc *sc = arg; + struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq; + struct bwfm_host_cmd *cmd; + int s; + + s = splsoftnet(); + while (ring->next != ring->cur) { + cmd = &ring->cmd[ring->next]; + splx(s); + cmd->cb(sc, cmd->data); + s = splsoftnet(); + ring->queued--; + ring->next = (ring->next + 1) % BWFM_HOST_CMD_RING_COUNT; + } + splx(s); +} + +void +bwfm_do_async(struct bwfm_softc *sc, + void (*cb)(struct bwfm_softc *, void *), void *arg, int len) +{ + struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq; + struct bwfm_host_cmd *cmd; + int s; + + s = splsoftnet(); + cmd = &ring->cmd[ring->cur]; + cmd->cb = cb; + KASSERT(len <= sizeof(cmd->data)); + memcpy(cmd->data, arg, len); + ring->cur = (ring->cur + 1) % BWFM_HOST_CMD_RING_COUNT; + task_add(sc->sc_taskq, &sc->sc_task); + splx(s); +} + +int +bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + int type, int arg1, int arg2) +{ +#ifdef BWFM_DEBUG + struct bwfm_softc *sc = ic->ic_softc; + DPRINTF(("%s: %s\n", DEVNAME(sc), __func__)); +#endif + return 0; +} + +int +bwfm_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct bwfm_softc *sc = ic->ic_softc; + struct bwfm_cmd_key cmd; + + cmd.ni = ni; + cmd.k = k; + bwfm_do_async(sc, bwfm_set_key_cb, &cmd, sizeof(cmd)); + return 0; +} + +void +bwfm_set_key_cb(struct bwfm_softc *sc, void *arg) +{ + struct bwfm_cmd_key *cmd = arg; + struct ieee80211_key *k = cmd->k; + struct ieee80211_node *ni = cmd->ni; + struct bwfm_wsec_key key; + uint32_t wsec, wsec_enable; + int ext_key = 0; + + if ((k->k_flags & IEEE80211_KEY_GROUP) == 0 && + k->k_cipher != IEEE80211_CIPHER_WEP40 && + k->k_cipher != IEEE80211_CIPHER_WEP104) + ext_key = 1; + + memset(&key, 0, sizeof(key)); + if (ext_key && !IEEE80211_IS_MULTICAST(ni->ni_bssid)) + memcpy(key.ea, ni->ni_bssid, sizeof(key.ea)); + key.index = htole32(k->k_id); + key.len = htole32(k->k_len); + memcpy(key.data, k->k_key, sizeof(key.data)); + if (!ext_key) + key.flags = htole32(BWFM_WSEC_PRIMARY_KEY); + + switch (k->k_cipher) { + case IEEE80211_CIPHER_WEP40: + key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1); + wsec_enable = BWFM_WSEC_WEP; + break; + case IEEE80211_CIPHER_WEP104: + key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128); + wsec_enable = BWFM_WSEC_WEP; + break; + case IEEE80211_CIPHER_TKIP: + key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP); + wsec_enable = BWFM_WSEC_TKIP; + break; + case IEEE80211_CIPHER_CCMP: + key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM); + wsec_enable = BWFM_WSEC_AES; + break; + default: + printf("%s: cipher %x not supported\n", DEVNAME(sc), + k->k_cipher); + return; + } + + bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key)); + bwfm_fwvar_var_get_int(sc, "wsec", &wsec); + wsec |= wsec_enable; + bwfm_fwvar_var_set_int(sc, "wsec", wsec); +} + +void +bwfm_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct bwfm_softc *sc = ic->ic_softc; + struct bwfm_cmd_key cmd; + + cmd.ni = ni; + cmd.k = k; + bwfm_do_async(sc, bwfm_delete_key_cb, &cmd, sizeof(cmd)); +} + +void +bwfm_delete_key_cb(struct bwfm_softc *sc, void *arg) +{ + struct bwfm_cmd_key *cmd = arg; + struct ieee80211_key *k = cmd->k; + struct bwfm_wsec_key key; + + memset(&key, 0, sizeof(key)); + key.index = htole32(k->k_id); + key.flags = htole32(BWFM_WSEC_PRIMARY_KEY); + bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key)); +} + +int +bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct bwfm_softc *sc = ic->ic_softc; + struct bwfm_cmd_newstate cmd; + + cmd.state = nstate; + cmd.arg = arg; + bwfm_do_async(sc, bwfm_newstate_cb, &cmd, sizeof(cmd)); + return 0; +} + +void +bwfm_newstate_cb(struct bwfm_softc *sc, void *arg) +{ + struct bwfm_cmd_newstate *cmd = arg; + struct ieee80211com *ic = &sc->sc_ic; + enum ieee80211_state ostate, nstate; + int s; + + s = splnet(); + ostate = ic->ic_state; + nstate = cmd->state; + DPRINTF(("newstate %s -> %s\n", + ieee80211_state_name[ostate], + ieee80211_state_name[nstate])); + + switch (nstate) { + case IEEE80211_S_SCAN: + bwfm_scan(sc); + ic->ic_state = nstate; + splx(s); + return; + case IEEE80211_S_AUTH: + ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE; + bwfm_connect(sc); + ic->ic_state = cmd->state; + if (ic->ic_flags & IEEE80211_F_RSNON) + ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_PTKSTART; + splx(s); + return; + default: + break; + } + sc->sc_newstate(ic, nstate, cmd->arg); + splx(s); + return; } diff --git a/sys/dev/ic/bwfmreg.h b/sys/dev/ic/bwfmreg.h index f6de250e374..428214aa6d4 100644 --- a/sys/dev/ic/bwfmreg.h +++ b/sys/dev/ic/bwfmreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bwfmreg.h,v 1.8 2017/10/18 19:18:07 patrick Exp $ */ +/* $OpenBSD: bwfmreg.h,v 1.9 2017/10/21 20:43:20 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se> @@ -139,6 +139,13 @@ #define BWFM_AUTH_OPEN 0 #define BWFM_AUTH_SHARED_KEY 1 #define BWFM_AUTH_AUTO 2 +#define BWFM_CRYPTO_ALGO_OFF 0 +#define BWFM_CRYPTO_ALGO_WEP1 1 +#define BWFM_CRYPTO_ALGO_TKIP 2 +#define BWFM_CRYPTO_ALGO_WEP128 3 +#define BWFM_CRYPTO_ALGO_AES_CCM 4 +#define BWFM_CRYPTO_ALGO_AES_RESERVED1 5 +#define BWFM_CRYPTO_ALGO_AES_RESERVED2 6 #define BWFM_MFP_NONE 0 #define BWFM_MFP_CAPABLE 1 #define BWFM_MFP_REQUIRED 2 @@ -383,6 +390,27 @@ struct bwfm_ext_join_params { struct bwfm_assoc_params assoc; }; +struct bwfm_wsec_key { + uint32_t index; + uint32_t len; + uint8_t data[32]; + uint32_t pad_1[18]; + uint32_t algo; + uint32_t flags; +#define BWFM_WSEC_PRIMARY_KEY (1 << 1) + uint32_t pad_2[3]; + uint32_t iv_initialized; + uint32_t pad_3; + /* Rx IV */ + struct { + uint32_t hi; + uint16_t lo; + uint16_t pad_4; + } rxiv; + uint32_t pad_5[2]; + uint8_t ea[ETHER_ADDR_LEN]; +}; + struct bwfm_wsec_pmk { uint16_t key_len; #define BWFM_WSEC_MAX_PSK_LEN 32 diff --git a/sys/dev/ic/bwfmvar.h b/sys/dev/ic/bwfmvar.h index 00970ff9978..19a62e2a38c 100644 --- a/sys/dev/ic/bwfmvar.h +++ b/sys/dev/ic/bwfmvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bwfmvar.h,v 1.2 2017/10/18 19:59:37 patrick Exp $ */ +/* $OpenBSD: bwfmvar.h,v 1.3 2017/10/21 20:43:20 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se> @@ -106,6 +106,29 @@ struct bwfm_proto_ops { }; extern struct bwfm_proto_ops bwfm_proto_bcdc_ops; +struct bwfm_host_cmd { + void (*cb)(struct bwfm_softc *, void *); + uint8_t data[256]; +}; + +struct bwfm_cmd_newstate { + enum ieee80211_state state; + int arg; +}; + +struct bwfm_cmd_key { + struct ieee80211_node *ni; + struct ieee80211_key *k; +}; + +struct bwfm_host_cmd_ring { +#define BWFM_HOST_CMD_RING_COUNT 32 + struct bwfm_host_cmd cmd[BWFM_HOST_CMD_RING_COUNT]; + int cur; + int next; + int queued; +}; + struct bwfm_softc { struct device sc_dev; struct ieee80211com sc_ic; @@ -119,7 +142,12 @@ struct bwfm_softc { #define BWFM_IO_TYPE_D11AC 2 int sc_tx_timer; - struct timeout sc_scan_timeout; + + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + struct bwfm_host_cmd_ring sc_cmdq; + struct taskq *sc_taskq; + struct task sc_task; }; void bwfm_attach(struct bwfm_softc *); |