summaryrefslogtreecommitdiff
path: root/sys/dev/usb/if_urtwn.c
diff options
context:
space:
mode:
authorJonathan Matthew <jmatthew@cvs.openbsd.org>2020-06-11 00:56:13 +0000
committerJonathan Matthew <jmatthew@cvs.openbsd.org>2020-06-11 00:56:13 +0000
commitf236aded699815c67d4d7c22b2ee81a99928541f (patch)
tree4ed437af59a1b612df4b20b1c7ffdadf1364efb6 /sys/dev/usb/if_urtwn.c
parentf87b93d6b490577049684e1a99f415d537c1f0cb (diff)
Make use of hardware crypto for CCMP. The tricky bits had been lying
dormant in the driver for years, we just needed to insert the IV before transmission and do packet number checks on received frames. tested by kevlo@ solene@ benno@ and me on a variety of hardware ok kevlo@ stsp@
Diffstat (limited to 'sys/dev/usb/if_urtwn.c')
-rw-r--r--sys/dev/usb/if_urtwn.c158
1 files changed, 123 insertions, 35 deletions
diff --git a/sys/dev/usb/if_urtwn.c b/sys/dev/usb/if_urtwn.c
index e7cc2f4bb2b..c2d12f00339 100644
--- a/sys/dev/usb/if_urtwn.c
+++ b/sys/dev/usb/if_urtwn.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_urtwn.c,v 1.89 2020/05/26 06:04:30 jsg Exp $ */
+/* $OpenBSD: if_urtwn.c,v 1.90 2020/06/11 00:56:12 jmatthew Exp $ */
/*-
* Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -490,10 +490,8 @@ urtwn_attach(struct device *parent, struct device *self, void *aux)
ic->ic_updateslot = urtwn_updateslot;
ic->ic_updateedca = urtwn_updateedca;
-#ifdef notyet
ic->ic_set_key = urtwn_set_key;
ic->ic_delete_key = urtwn_delete_key;
-#endif
/* Override state transition machine. */
ic->ic_newstate = urtwn_newstate;
@@ -1035,6 +1033,10 @@ urtwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
struct urtwn_softc *sc = (struct urtwn_softc *)self;
struct urtwn_cmd_key cmd;
+ /* Only handle keys for CCMP */
+ if (k->k_cipher != IEEE80211_CIPHER_CCMP)
+ return ieee80211_set_key(ic, ni, k);
+
/* Defer setting of WEP keys until interface is brought up. */
if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
(IFF_UP | IFF_RUNNING))
@@ -1065,6 +1067,12 @@ urtwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
struct urtwn_softc *sc = (struct urtwn_softc *)self;
struct urtwn_cmd_key cmd;
+ /* Only handle keys for CCMP */
+ if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
+ ieee80211_delete_key(ic, ni, k);
+ return;
+ }
+
if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
ic->ic_state != IEEE80211_S_RUN)
return; /* Nothing to do. */
@@ -1084,6 +1092,52 @@ urtwn_delete_key_cb(struct urtwn_softc *sc, void *arg)
rtwn_delete_key(ic, cmd->ni, &cmd->key);
}
+int
+urtwn_ccmp_decap(struct urtwn_softc *sc, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_sc.sc_ic;
+ struct ieee80211_key *k;
+ struct ieee80211_frame *wh;
+ uint64_t pn, *prsc;
+ uint8_t *ivp;
+ uint8_t tid;
+ int hdrlen, hasqos;
+
+ k = ieee80211_get_rxkey(ic, m, ni);
+ if (k == NULL)
+ return 1;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ hdrlen = ieee80211_get_hdrlen(wh);
+ ivp = (uint8_t *)wh + hdrlen;
+
+ /* Check that ExtIV bit is set. */
+ if (!(ivp[3] & IEEE80211_WEP_EXTIV))
+ return 1;
+
+ hasqos = ieee80211_has_qos(wh);
+ tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+ prsc = &k->k_rsc[tid];
+
+ /* Extract the 48-bit PN from the CCMP header. */
+ pn = (uint64_t)ivp[0] |
+ (uint64_t)ivp[1] << 8 |
+ (uint64_t)ivp[4] << 16 |
+ (uint64_t)ivp[5] << 24 |
+ (uint64_t)ivp[6] << 32 |
+ (uint64_t)ivp[7] << 40;
+ if (pn <= *prsc) {
+ ic->ic_stats.is_ccmp_replays++;
+ return 1;
+ }
+ /* Last seen packet number is updated in ieee80211_inputm(). */
+
+ /* Strip MIC. IV will be stripped by ieee80211_inputm(). */
+ m_adj(m, -IEEE80211_CCMP_MICLEN);
+ return 0;
+}
+
void
urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen,
struct mbuf_list *ml)
@@ -1197,6 +1251,21 @@ urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen,
rxi.rxi_flags = 0;
rxi.rxi_rssi = rssi;
rxi.rxi_tstamp = 0; /* Unused. */
+
+ /* Handle hardware decryption. */
+ if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL)
+ && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+ (ni->ni_flags & IEEE80211_NODE_RXPROT) &&
+ ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) {
+ if (urtwn_ccmp_decap(sc, m, ni) != 0) {
+ ifp->if_ierrors++;
+ m_freem(m);
+ ieee80211_release_node(ic, ni);
+ return;
+ }
+ rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
+ }
+
ieee80211_inputm(ifp, m, ni, &rxi, ml);
/* Node is no longer needed. */
ieee80211_release_node(ic, ni);
@@ -1360,37 +1429,28 @@ urtwn_tx_fill_desc(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
struct r92c_tx_desc_usb *txd;
struct ieee80211com *ic = &sc->sc_sc.sc_ic;
uint8_t raid, type;
+ uint32_t pktlen;
txd = (struct r92c_tx_desc_usb *)*txdp;
(*txdp) += sizeof(*txd);
memset(txd, 0, sizeof(*txd));
+ pktlen = m->m_pkthdr.len;
+ if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) {
+ txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER,
+ R92C_TXDW1_CIPHER_AES));
+ pktlen += IEEE80211_CCMP_HDRLEN;
+ }
+
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
txd->txdw0 |= htole32(
- SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) |
+ SM(R92C_TXDW0_PKTLEN, pktlen) |
SM(R92C_TXDW0_OFFSET, sizeof(*txd)) |
R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
-#ifdef notyet
- if (k != NULL) {
- switch (k->k_cipher) {
- case IEEE80211_CIPHER_WEP40:
- case IEEE80211_CIPHER_WEP104:
- case IEEE80211_CIPHER_TKIP:
- cipher = R92C_TXDW1_CIPHER_RC4;
- break;
- case IEEE80211_CIPHER_CCMP:
- cipher = R92C_TXDW1_CIPHER_AES;
- break;
- default:
- cipher = R92C_TXDW1_CIPHER_NONE;
- }
- txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher));
- }
-#endif
if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
type == IEEE80211_FC0_TYPE_DATA) {
if (ic->ic_curmode == IEEE80211_MODE_11B ||
@@ -1413,7 +1473,7 @@ urtwn_tx_fill_desc(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK);
}
- if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
+ if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
R92C_TXDW4_HWRTSEN);
} else if (ic->ic_flags & IEEE80211_F_USEPROT) {
@@ -1468,6 +1528,7 @@ urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
struct r92e_tx_desc_usb *txd;
struct ieee80211com *ic = &sc->sc_sc.sc_ic;
uint8_t raid, type;
+ uint32_t pktlen;
txd = (struct r92e_tx_desc_usb *)*txdp;
(*txdp) += sizeof(*txd);
@@ -1475,17 +1536,20 @@ urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ pktlen = m->m_pkthdr.len;
+ if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) {
+ txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER,
+ R92C_TXDW1_CIPHER_AES));
+ pktlen += IEEE80211_CCMP_HDRLEN;
+ }
+
txd->txdw0 |= htole32(
- SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) |
+ SM(R92C_TXDW0_PKTLEN, pktlen) |
SM(R92C_TXDW0_OFFSET, sizeof(*txd)) |
R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
-#ifdef notyet
- /* cipher */
-#endif
-
if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
type == IEEE80211_FC0_TYPE_DATA) {
if (ic->ic_curmode == IEEE80211_MODE_11B ||
@@ -1500,7 +1564,7 @@ urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
/* Request TX status report for AMRR */
txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT | R88E_TXDW2_AGGBK);
- if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
+ if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
R92C_TXDW4_HWRTSEN);
} else if (ic->ic_flags & IEEE80211_F_USEPROT) {
@@ -1549,16 +1613,18 @@ urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni)
struct usbd_pipe *pipe;
uint16_t qos, sum;
uint8_t tid, qid;
- int i, xferlen, error;
+ int i, xferlen, error, headerlen;
uint8_t *txdp;
wh = mtod(m, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
k = ieee80211_get_txkey(ic, wh, ni);
- if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
- return (ENOBUFS);
- wh = mtod(m, struct ieee80211_frame *);
+ if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
+ if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
+ return (ENOBUFS);
+ wh = mtod(m, struct ieee80211_frame *);
+ }
}
if (ieee80211_has_qos(wh)) {
@@ -1611,9 +1677,31 @@ urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni)
}
#endif
- xferlen = (txdp - data->buf) + m->m_pkthdr.len;
- m_copydata(m, 0, m->m_pkthdr.len, txdp);
- m_freem(m);
+ if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) {
+ xferlen = (txdp - data->buf) + m->m_pkthdr.len +
+ IEEE80211_CCMP_HDRLEN;
+ headerlen = ieee80211_get_hdrlen(wh);
+
+ m_copydata(m, 0, headerlen, txdp);
+ txdp += headerlen;
+
+ k->k_tsc++;
+ txdp[0] = k->k_tsc;
+ txdp[1] = k->k_tsc >> 8;
+ txdp[2] = 0;
+ txdp[3] = k->k_id | IEEE80211_WEP_EXTIV;
+ txdp[4] = k->k_tsc >> 16;
+ txdp[5] = k->k_tsc >> 24;
+ txdp[6] = k->k_tsc >> 32;
+ txdp[7] = k->k_tsc >> 40;
+ txdp += IEEE80211_CCMP_HDRLEN;
+
+ m_copydata(m, headerlen, m->m_pkthdr.len - headerlen, txdp);
+ } else {
+ xferlen = (txdp - data->buf) + m->m_pkthdr.len;
+ m_copydata(m, 0, m->m_pkthdr.len, txdp);
+ m_freem(m);
+ }
data->pipe = pipe;
usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen,