diff options
Diffstat (limited to 'sys/dev/usb/if_urtwn.c')
-rw-r--r-- | sys/dev/usb/if_urtwn.c | 158 |
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, |