svn commit: r280088 - head/sys/dev/wpi
Adrian Chadd
adrian at FreeBSD.org
Sun Mar 15 20:54:41 UTC 2015
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 <s3erios at gmail.com>
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);
};
More information about the svn-src-all
mailing list