Re: athn(4): WPA2 crypto hardware offload

2020-04-24 Thread Stefan Sperling
On Fri, Apr 24, 2020 at 03:31:12PM +0200, Stefan Sperling wrote:
> On Wed, Apr 22, 2020 at 07:37:10PM +0200, Stefan Sperling wrote:
> > This makes athn(4) offload CCMP encryption and decryption to hardware.
> > CCMP is used with WPA2, so this reduces CPU load on WPA2 networks only.
> > 
> > The WPA1 (TKIP) and WEP ciphers remain in software because this simplifies
> > the driver. TKIP in particular is a annoying to deal with on this hardware.
> > Old ciphers should not be used anymore anyway and they don't work in 11n 
> > mode.
> > 
> > I have successfully tested the following on this device:
> > athn0 at pci1 dev 0 function 0 "Atheros AR9281" rev 0x01: apic 2 int 16
> > athn0: AR9280 rev 2 (2T2R), ROM rev 22, address xx:xx:xx:xx:xx:xx
> > 
> > Hostap mode with multiple clients in parallel: wpa2, wpa1, wep, plaintext
> > Client mode: wpa2, plaintext
> > 
> > Could anyone else test this?
> 
> This patch panics on athn(4) USB devices (reported to me by several people).
> I am working on a fix for USB.

New diff which also works on USB devices (tested on AR9271 and AR9280+AR7010).

diff c50b0f0215d7dd5773e0ee8623d8aa2c7d2107c6 
b593e0ca32b1c816510ec76bdd498bc9101dea15
blob - 9dd0499c9704c87fbe039b65f02e907284fea120
blob + 0f78d27c9e99239d6ec08410dd42e65c5781245d
--- sys/dev/ic/ar5008.c
+++ sys/dev/ic/ar5008.c
@@ -77,11 +77,14 @@ voidar5008_rx_free(struct athn_softc *);
 void   ar5008_rx_enable(struct athn_softc *);
 void   ar5008_rx_radiotap(struct athn_softc *, struct mbuf *,
struct ar_rx_desc *);
+intar5008_ccmp_decap(struct athn_softc *, struct mbuf *,
+   struct ieee80211_node *);
 void   ar5008_rx_intr(struct athn_softc *);
 intar5008_tx_process(struct athn_softc *, int);
 void   ar5008_tx_intr(struct athn_softc *);
 intar5008_swba_intr(struct athn_softc *);
 intar5008_intr(struct athn_softc *);
+intar5008_ccmp_encap(struct mbuf *, u_int, struct ieee80211_key *);
 intar5008_tx(struct athn_softc *, struct mbuf *, struct ieee80211_node *,
int);
 void   ar5008_set_rf_mode(struct athn_softc *, struct ieee80211_channel *);
@@ -254,6 +257,8 @@ ar5008_attach(struct athn_softc *sc)
kc_entries_log = MS(base->deviceCap, AR_EEP_DEVCAP_KC_ENTRIES);
sc->kc_entries = (kc_entries_log != 0) ?
1 << kc_entries_log : AR_KEYTABLE_SIZE;
+   if (sc->kc_entries > AR_KEYTABLE_SIZE)
+   sc->kc_entries = AR_KEYTABLE_SIZE;
 
sc->txchainmask = base->txMask;
if (sc->mac_ver == AR_SREV_VERSION_5416_PCI &&
@@ -781,6 +786,111 @@ ar5008_rx_radiotap(struct athn_softc *sc, struct mbuf 
 }
 #endif
 
+int
+ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node 
*ni)
+{
+   struct ieee80211com *ic = >sc_ic;
+   struct ieee80211_key *k;
+   struct ieee80211_frame *wh;
+   struct ieee80211_rx_ba *ba;
+   uint64_t pn, *prsc;
+   u_int8_t *ivp, *mmie;
+   uint8_t tid;
+   uint16_t kid;
+   int hdrlen, hasqos;
+   uintptr_t entry;
+
+   wh = mtod(m, struct ieee80211_frame *);
+   hdrlen = ieee80211_get_hdrlen(wh);
+   ivp = mtod(m, u_int8_t *) + hdrlen;
+
+   /* find key for decryption */
+   if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+   k = >ni_pairwise_key;
+   } else if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+   IEEE80211_FC0_TYPE_MGT) {
+   /* retrieve group data key id from IV field */
+   /* check that IV field is present */
+   if (m->m_len < hdrlen + 4)
+   return 1;
+   kid = ivp[3] >> 6;
+   k = >ic_nw_keys[kid];
+   } else {
+   /* retrieve integrity group key id from MMIE */
+   if (m->m_len < sizeof(*wh) + IEEE80211_MMIE_LEN) {
+   return 1;
+   }
+   /* it is assumed management frames are contiguous */
+   mmie = (u_int8_t *)wh + m->m_len - IEEE80211_MMIE_LEN;
+   /* check that MMIE is valid */
+   if (mmie[0] != IEEE80211_ELEMID_MMIE || mmie[1] != 16) {
+   return 1;
+   }
+   kid = LE_READ_2([2]);
+   if (kid != 4 && kid != 5) {
+   return 1;
+   }
+   k = >ic_nw_keys[kid];
+   }
+
+   if (k->k_cipher != IEEE80211_CIPHER_CCMP)
+   return 1;
+
+   /* Sanity checks to ensure this is really a key we installed. */
+   entry = (uintptr_t)k->k_priv;
+   if (k->k_flags & IEEE80211_KEY_GROUP) {
+   if (k->k_id > IEEE80211_WEP_NKID ||
+   entry != k->k_id)
+   return 1;
+   } else if (entry != IEEE80211_WEP_NKID +
+   IEEE80211_AID(ni->ni_associd))
+   return 1;
+
+   /* Check that ExtIV bit is be set. */
+   if (!(ivp[3] & IEEE80211_WEP_EXTIV))
+   return 1;
+
+   hasqos = 

Re: athn(4): WPA2 crypto hardware offload

2020-04-24 Thread Stefan Sperling
On Wed, Apr 22, 2020 at 07:37:10PM +0200, Stefan Sperling wrote:
> This makes athn(4) offload CCMP encryption and decryption to hardware.
> CCMP is used with WPA2, so this reduces CPU load on WPA2 networks only.
> 
> The WPA1 (TKIP) and WEP ciphers remain in software because this simplifies
> the driver. TKIP in particular is a annoying to deal with on this hardware.
> Old ciphers should not be used anymore anyway and they don't work in 11n mode.
> 
> I have successfully tested the following on this device:
> athn0 at pci1 dev 0 function 0 "Atheros AR9281" rev 0x01: apic 2 int 16
> athn0: AR9280 rev 2 (2T2R), ROM rev 22, address xx:xx:xx:xx:xx:xx
> 
> Hostap mode with multiple clients in parallel: wpa2, wpa1, wep, plaintext
> Client mode: wpa2, plaintext
> 
> Could anyone else test this?

This patch panics on athn(4) USB devices (reported to me by several people).
I am working on a fix for USB.

Meanwhile, testing on PCI devices is still welcome.

Thanks!



athn(4): WPA2 crypto hardware offload

2020-04-22 Thread Stefan Sperling
This makes athn(4) offload CCMP encryption and decryption to hardware.
CCMP is used with WPA2, so this reduces CPU load on WPA2 networks only.

The WPA1 (TKIP) and WEP ciphers remain in software because this simplifies
the driver. TKIP in particular is a annoying to deal with on this hardware.
Old ciphers should not be used anymore anyway and they don't work in 11n mode.

I have successfully tested the following on this device:
athn0 at pci1 dev 0 function 0 "Atheros AR9281" rev 0x01: apic 2 int 16
athn0: AR9280 rev 2 (2T2R), ROM rev 22, address xx:xx:xx:xx:xx:xx

Hostap mode with multiple clients in parallel: wpa2, wpa1, wep, plaintext
Client mode: wpa2, plaintext

Could anyone else test this?

diff c50b0f0215d7dd5773e0ee8623d8aa2c7d2107c6 
72190e7ad96414fa4e11cb3c5ff1dc74b1d69795
blob - 9dd0499c9704c87fbe039b65f02e907284fea120
blob + 8ebdc15cbec490ea4a2f28a0fadab014e3d62645
--- sys/dev/ic/ar5008.c
+++ sys/dev/ic/ar5008.c
@@ -254,6 +254,8 @@ ar5008_attach(struct athn_softc *sc)
kc_entries_log = MS(base->deviceCap, AR_EEP_DEVCAP_KC_ENTRIES);
sc->kc_entries = (kc_entries_log != 0) ?
1 << kc_entries_log : AR_KEYTABLE_SIZE;
+   if (sc->kc_entries > AR_KEYTABLE_SIZE)
+   sc->kc_entries = AR_KEYTABLE_SIZE;
 
sc->txchainmask = base->txMask;
if (sc->mac_ver == AR_SREV_VERSION_5416_PCI &&
@@ -781,6 +783,111 @@ ar5008_rx_radiotap(struct athn_softc *sc, struct mbuf 
 }
 #endif
 
+int
+ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node 
*ni)
+{
+   struct ieee80211com *ic = >sc_ic;
+   struct ieee80211_key *k;
+   struct ieee80211_frame *wh;
+   struct ieee80211_rx_ba *ba;
+   uint64_t pn, *prsc;
+   u_int8_t *ivp, *mmie;
+   uint8_t tid;
+   uint16_t kid;
+   int hdrlen, hasqos;
+   uintptr_t entry;
+
+   wh = mtod(m, struct ieee80211_frame *);
+   hdrlen = ieee80211_get_hdrlen(wh);
+   ivp = mtod(m, u_int8_t *) + hdrlen;
+
+   /* find key for decryption */
+   if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+   k = >ni_pairwise_key;
+   } else if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+   IEEE80211_FC0_TYPE_MGT) {
+   /* retrieve group data key id from IV field */
+   /* check that IV field is present */
+   if (m->m_len < hdrlen + 4)
+   return 1;
+   kid = ivp[3] >> 6;
+   k = >ic_nw_keys[kid];
+   } else {
+   /* retrieve integrity group key id from MMIE */
+   if (m->m_len < sizeof(*wh) + IEEE80211_MMIE_LEN) {
+   return 1;
+   }
+   /* it is assumed management frames are contiguous */
+   mmie = (u_int8_t *)wh + m->m_len - IEEE80211_MMIE_LEN;
+   /* check that MMIE is valid */
+   if (mmie[0] != IEEE80211_ELEMID_MMIE || mmie[1] != 16) {
+   return 1;
+   }
+   kid = LE_READ_2([2]);
+   if (kid != 4 && kid != 5) {
+   return 1;
+   }
+   k = >ic_nw_keys[kid];
+   }
+
+   if (k->k_cipher != IEEE80211_CIPHER_CCMP)
+   return 1;
+
+   /* Sanity checks to ensure this is really a key we installed. */
+   entry = (uintptr_t)k->k_priv;
+   if (k->k_flags & IEEE80211_KEY_GROUP) {
+   if (k->k_id > IEEE80211_WEP_NKID ||
+   entry != k->k_id)
+   return 1;
+   } else if (entry != IEEE80211_WEP_NKID +
+   IEEE80211_AID(ni->ni_associd))
+   return 1;
+
+   /* Check that ExtIV bit is be set. */
+   if (!(ivp[3] & IEEE80211_WEP_EXTIV))
+   return 1;
+
+   hasqos = ieee80211_has_qos(wh);
+   tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+   ba = hasqos ? >ni_rx_ba[tid] : NULL;
+   prsc = >k_rsc[0];
+
+   /* 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) {
+   if (hasqos && ba->ba_state == IEEE80211_BA_AGREED) {
+   /*
+* This is an A-MPDU subframe.
+* Such frames may be received out of order due to
+* legitimate retransmissions of failed subframes
+* in previous A-MPDUs. Duplicates will be handled
+* in ieee80211_inputm() as part of A-MPDU reordering.
+*
+* XXX TODO We can probably do better than this! Store
+* re-ordered PN in BA agreement state and check it?
+*/
+   } else {
+