From owner-svn-src-head@FreeBSD.ORG Sun Mar 15 20:54:41 2015 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 5BE8368A; Sun, 15 Mar 2015 20:54:41 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 3B865316; Sun, 15 Mar 2015 20:54:41 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t2FKsf5c074664; Sun, 15 Mar 2015 20:54:41 GMT (envelope-from adrian@FreeBSD.org) Received: (from adrian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t2FKse7h074661; Sun, 15 Mar 2015 20:54:40 GMT (envelope-from adrian@FreeBSD.org) Message-Id: <201503152054.t2FKse7h074661@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: adrian set sender to adrian@FreeBSD.org using -f From: Adrian Chadd Date: Sun, 15 Mar 2015 20:54:40 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r280088 - head/sys/dev/wpi X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 15 Mar 2015 20:54:41 -0000 Author: adrian Date: Sun Mar 15 20:54:40 2015 New Revision: 280088 URL: https://svnweb.freebsd.org/changeset/base/280088 Log: Add support for AES-CCMP group keys. PR: kern/197143 Submitted by: Andriy Voskoboinyk Modified: head/sys/dev/wpi/if_wpi.c head/sys/dev/wpi/if_wpivar.h Modified: head/sys/dev/wpi/if_wpi.c ============================================================================== --- head/sys/dev/wpi/if_wpi.c Sun Mar 15 20:54:10 2015 (r280087) +++ head/sys/dev/wpi/if_wpi.c Sun Mar 15 20:54:40 2015 (r280088) @@ -232,8 +232,15 @@ static int wpi_setup_beacon(struct wpi_s static void wpi_update_beacon(struct ieee80211vap *, int); static void wpi_newassoc(struct ieee80211_node *, int); static int wpi_run(struct wpi_softc *, struct ieee80211vap *); -static int wpi_key_alloc(struct ieee80211vap *, struct ieee80211_key *, - ieee80211_keyix *, ieee80211_keyix *); +static int wpi_load_key(struct ieee80211_node *, + const struct ieee80211_key *); +static void wpi_load_key_cb(void *, struct ieee80211_node *); +static int wpi_set_global_keys(struct ieee80211_node *); +static int wpi_del_key(struct ieee80211_node *, + const struct ieee80211_key *); +static void wpi_del_key_cb(void *, struct ieee80211_node *); +static int wpi_process_key(struct ieee80211vap *, + const struct ieee80211_key *, int); static int wpi_key_set(struct ieee80211vap *, const struct ieee80211_key *, const uint8_t mac[IEEE80211_ADDR_LEN]); @@ -623,7 +630,6 @@ wpi_vap_create(struct ieee80211com *ic, } /* Override with driver methods. */ - vap->iv_key_alloc = wpi_key_alloc; vap->iv_key_set = wpi_key_set; vap->iv_key_delete = wpi_key_delete; wvp->wv_newstate = vap->iv_newstate; @@ -1775,7 +1781,6 @@ wpi_rx_done(struct wpi_softc *sc, struct struct wpi_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; - const struct ieee80211_cipher *cip = NULL; struct ieee80211com *ic = ifp->if_l2com; struct wpi_rx_ring *ring = &sc->rxq; struct wpi_rx_stat *stat; @@ -1863,16 +1868,9 @@ wpi_rx_done(struct wpi_softc *sc, struct /* Grab a reference to the source node. */ wh = mtod(m, struct ieee80211_frame *); - ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); - if (ni != NULL) - cip = ni->ni_ucastkey.wk_cipher; if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && - !IEEE80211_IS_MULTICAST(wh->i_addr1) && - cip != NULL && cip->ic_cipher == IEEE80211_CIPHER_AES_CCM) { - if ((flags & WPI_RX_CIPHER_MASK) != WPI_RX_CIPHER_CCMP) - goto fail2; - + (flags & WPI_RX_CIPHER_MASK) == WPI_RX_CIPHER_CCMP) { /* Check whether decryption was successful or not. */ if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) { DPRINTF(sc, WPI_DEBUG_RECV, @@ -1882,6 +1880,8 @@ wpi_rx_done(struct wpi_softc *sc, struct m->m_flags |= M_WEP; } + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + if (ieee80211_radiotap_active(ic)) { struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; @@ -1909,8 +1909,7 @@ wpi_rx_done(struct wpi_softc *sc, struct return; -fail2: ieee80211_free_node(ni); - m_freem(m); +fail2: m_freem(m); fail1: if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } @@ -3094,8 +3093,10 @@ static int wpi_add_node(struct wpi_softc *sc, struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; + struct wpi_vap *wvp = WPI_VAP(ni->ni_vap); struct wpi_node *wn = WPI_NODE(ni); struct wpi_node_info node; + int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); @@ -3110,7 +3111,24 @@ wpi_add_node(struct wpi_softc *sc, struc node.action = htole32(WPI_ACTION_SET_RATE); node.antenna = WPI_ANTENNA_BOTH; - return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: wpi_cmd() call failed with error code %d\n", __func__, + error); + return error; + } + + if (wvp->wv_gtk != 0) { + error = wpi_set_global_keys(ni); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: error while setting global keys\n", __func__); + return ENXIO; + } + } + + return 0; } /* @@ -4237,37 +4255,11 @@ wpi_run(struct wpi_softc *sc, struct iee } static int -wpi_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, - ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) -{ - struct wpi_softc *sc = vap->iv_ic->ic_ifp->if_softc; - - if (!(&vap->iv_nw_keys[0] <= k && - k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { - if (k->wk_flags & IEEE80211_KEY_GROUP) { - /* should not happen */ - DPRINTF(sc, WPI_DEBUG_KEY, "%s: bogus group key\n", - __func__); - return 0; - } - *keyix = 0; /* NB: use key index 0 for ucast key */ - } else { - *keyix = *rxkeyix = k - vap->iv_nw_keys; - - if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) - k->wk_flags |= IEEE80211_KEY_SWCRYPT; - } - return 1; -} - -static int -wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, - const uint8_t mac[IEEE80211_ADDR_LEN]) +wpi_load_key(struct ieee80211_node *ni, const struct ieee80211_key *k) { const struct ieee80211_cipher *cip = k->wk_cipher; - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; struct wpi_node *wn = WPI_NODE(ni); struct wpi_node_info node; uint16_t kflags; @@ -4275,20 +4267,22 @@ wpi_key_set(struct ieee80211vap *vap, co DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + if (wpi_check_node_entry(sc, wn->id) == 0) { + device_printf(sc->sc_dev, "%s: node does not exist\n", + __func__); + return 0; + } + switch (cip->ic_cipher) { case IEEE80211_CIPHER_AES_CCM: - if (k->wk_flags & IEEE80211_KEY_GROUP) - return 1; - kflags = WPI_KFLAG_CCMP; break; - default: - /* null_key_set() */ - return 1; - } - if (wn->id == WPI_ID_UNDEFINED) + default: + device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__, + cip->ic_cipher); return 0; + } kflags |= WPI_KFLAG_KID(k->wk_keyix); if (k->wk_flags & IEEE80211_KEY_GROUP) @@ -4300,55 +4294,223 @@ wpi_key_set(struct ieee80211vap *vap, co node.flags = WPI_FLAG_KEY_SET; node.kflags = htole16(kflags); memcpy(node.key, k->wk_key, k->wk_keylen); - - DPRINTF(sc, WPI_DEBUG_KEY, "set key id=%d for node %d\n", k->wk_keyix, - node.id); +again: + DPRINTF(sc, WPI_DEBUG_KEY, + "%s: setting %s key id %d for node %d (%s)\n", __func__, + (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", k->wk_keyix, + node.id, ether_sprintf(ni->ni_macaddr)); error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev, "can't update node info, error %d\n", error); - return 0; + return !error; + } + + if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + kflags |= WPI_KFLAG_MULTICAST; + node.kflags = htole16(kflags); + + goto again; } return 1; } +static void +wpi_load_key_cb(void *arg, struct ieee80211_node *ni) +{ + const struct ieee80211_key *k = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) + return; + + WPI_NT_LOCK(sc); + error = wpi_load_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (error == 0) { + device_printf(sc->sc_dev, "%s: error while setting key\n", + __func__); + } +} + static int -wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +wpi_set_global_keys(struct ieee80211_node *ni) { - const struct ieee80211_cipher *cip = k->wk_cipher; - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_key *wk = &vap->iv_nw_keys[0]; + int error = 1; + + for (; wk < &vap->iv_nw_keys[IEEE80211_WEP_NKID] && error; wk++) + if (wk->wk_keyix != IEEE80211_KEYIX_NONE) + error = wpi_load_key(ni, wk); + + return !error; +} + +static int +wpi_del_key(struct ieee80211_node *ni, const struct ieee80211_key *k) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; struct wpi_node *wn = WPI_NODE(ni); struct wpi_node_info node; + uint16_t kflags; + int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - switch (cip->ic_cipher) { - case IEEE80211_CIPHER_AES_CCM: - break; - default: - /* null_key_delete() */ - return 1; + if (wpi_check_node_entry(sc, wn->id) == 0) { + DPRINTF(sc, WPI_DEBUG_KEY, "%s: node was removed\n", __func__); + return 1; /* Nothing to do. */ } - if (vap->iv_state != IEEE80211_S_RUN || - (k->wk_flags & IEEE80211_KEY_GROUP)) - return 1; /* Nothing to do. */ + kflags = WPI_KFLAG_KID(k->wk_keyix); + if (k->wk_flags & IEEE80211_KEY_GROUP) + kflags |= WPI_KFLAG_MULTICAST; memset(&node, 0, sizeof node); node.id = wn->id; node.control = WPI_NODE_UPDATE; node.flags = WPI_FLAG_KEY_SET; + node.kflags = htole16(kflags); +again: + DPRINTF(sc, WPI_DEBUG_KEY, "%s: deleting %s key %d for node %d (%s)\n", + __func__, (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", + k->wk_keyix, node.id, ether_sprintf(ni->ni_macaddr)); - DPRINTF(sc, WPI_DEBUG_KEY, "delete keys for node %d\n", node.id); - (void)wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, "can't update node info, error %d\n", + error); + return !error; + } + + if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + kflags |= WPI_KFLAG_MULTICAST; + node.kflags = htole16(kflags); + + goto again; + } return 1; } +static void +wpi_del_key_cb(void *arg, struct ieee80211_node *ni) +{ + const struct ieee80211_key *k = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) + return; + + WPI_NT_LOCK(sc); + error = wpi_del_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (error == 0) { + device_printf(sc->sc_dev, "%s: error while deleting key\n", + __func__); + } +} + +static int +wpi_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k, + int set) +{ + struct ieee80211com *ic = vap->iv_ic; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + struct ieee80211_node *ni; + int error, ni_ref = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + if (!(k->wk_flags & IEEE80211_KEY_RECV)) { + /* XMIT keys are handled in wpi_tx_data(). */ + return 1; + } + + /* Handle group keys. */ + if (&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + WPI_NT_LOCK(sc); + if (set) + wvp->wv_gtk |= WPI_VAP_KEY(k->wk_keyix); + else + wvp->wv_gtk &= ~WPI_VAP_KEY(k->wk_keyix); + WPI_NT_UNLOCK(sc); + + if (vap->iv_state == IEEE80211_S_RUN) { + ieee80211_iterate_nodes(&ic->ic_sta, + set ? wpi_load_key_cb : wpi_del_key_cb, (void *)k); + } + + return 1; + } + + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + ni = vap->iv_bss; + break; + + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, k->wk_macaddr); + if (ni == NULL) + return 0; /* should not happen */ + + ni_ref = 1; + break; + + default: + device_printf(sc->sc_dev, "%s: unknown opmode %d\n", __func__, + vap->iv_opmode); + return 0; + } + + WPI_NT_LOCK(sc); + if (set) + error = wpi_load_key(ni, k); + else + error = wpi_del_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (ni_ref) + ieee80211_node_decref(ni); + + return error; +} + +static int +wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return wpi_process_key(vap, k, 1); +} + +static int +wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + return wpi_process_key(vap, k, 0); +} + /* * This function is called after the runtime firmware notifies us of its * readiness (called in a process context). Modified: head/sys/dev/wpi/if_wpivar.h ============================================================================== --- head/sys/dev/wpi/if_wpivar.h Sun Mar 15 20:54:10 2015 (r280087) +++ head/sys/dev/wpi/if_wpivar.h Sun Mar 15 20:54:40 2015 (r280088) @@ -127,6 +127,9 @@ struct wpi_vap { struct ieee80211_beacon_offsets wv_boff; struct mtx wv_mtx; + uint32_t wv_gtk; +#define WPI_VAP_KEY(kid) (1 << kid) + int (*wv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); };