svn commit: r308823 - head/sys/net80211

Adrian Chadd adrian at FreeBSD.org
Sat Nov 19 02:00:26 UTC 2016


Author: adrian
Date: Sat Nov 19 02:00:24 2016
New Revision: 308823
URL: https://svnweb.freebsd.org/changeset/base/308823

Log:
  [net80211] handle hardware encryption offload in the receive path
  
  * teach the crypto modules about receive offload - although I have
    to do some further reviewing in places where we /can't/ have an RX key
  * teach the RX data path about receive offload encryption - check the flag,
    handle NULL key, do decap and checking as appropriate.
  
  Tested:
  
  * iwn(4), STA mode
  * ath(4), STA and AP mode
  * ath10k port, STA mode (hardware encryption)
  
  Reviewed by:	avos
  Differential Revision:	https://reviews.freebsd.org/D8533

Modified:
  head/sys/net80211/ieee80211_adhoc.c
  head/sys/net80211/ieee80211_crypto.c
  head/sys/net80211/ieee80211_crypto.h
  head/sys/net80211/ieee80211_crypto_ccmp.c
  head/sys/net80211/ieee80211_crypto_tkip.c
  head/sys/net80211/ieee80211_hostap.c
  head/sys/net80211/ieee80211_sta.c
  head/sys/net80211/ieee80211_wds.c

Modified: head/sys/net80211/ieee80211_adhoc.c
==============================================================================
--- head/sys/net80211/ieee80211_adhoc.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_adhoc.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -316,6 +316,16 @@ adhoc_input(struct ieee80211_node *ni, s
 	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */	
 	uint8_t dir, type, subtype, qos;
 	uint8_t *bssid;
+	int is_hw_decrypted = 0;
+	int has_decrypted = 0;
+
+	/*
+	 * Some devices do hardware decryption all the way through
+	 * to pretending the frame wasn't encrypted in the first place.
+	 * So, tag it appropriately so it isn't discarded inappropriately.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+		is_hw_decrypted = 1;
 
 	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
@@ -479,7 +489,7 @@ adhoc_input(struct ieee80211_node *ni, s
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+		if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -490,14 +500,14 @@ adhoc_input(struct ieee80211_node *ni, s
 				IEEE80211_NODE_STAT(ni, rx_noprivacy);
 				goto out;
 			}
-			key = ieee80211_crypto_decap(ni, m, hdrspace);
-			if (key == NULL) {
+			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
 				/* NB: stats+msgs handled in crypto_decap */
 				IEEE80211_NODE_STAT(ni, rx_wepfail);
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+			has_decrypted = 1;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -528,7 +538,7 @@ adhoc_input(struct ieee80211_node *ni, s
 		/*
 		 * Next strip any MSDU crypto bits.
 		 */
-		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+		if (!ieee80211_crypto_demic(vap, key, m, 0)) {
 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
 			    ni->ni_macaddr, "data", "%s", "demic error");
 			vap->iv_stats.is_rx_demicfail++;
@@ -582,7 +592,8 @@ adhoc_input(struct ieee80211_node *ni, s
 			 * any non-PAE frames received without encryption.
 			 */
 			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
-			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
+			    ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+			    (is_hw_decrypted == 0) &&
 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
 				/*
 				 * Drop unencrypted frames.

Modified: head/sys/net80211/ieee80211_crypto.c
==============================================================================
--- head/sys/net80211/ieee80211_crypto.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_crypto.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -580,8 +580,9 @@ ieee80211_crypto_encap(struct ieee80211_
  * Validate and strip privacy headers (and trailer) for a
  * received frame that has the WEP/Privacy bit set.
  */
-struct ieee80211_key *
-ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
+int
+ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen,
+    struct ieee80211_key **key)
 {
 #define	IEEE80211_WEP_HDRLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
 #define	IEEE80211_WEP_MINLEN \
@@ -590,16 +591,38 @@ ieee80211_crypto_decap(struct ieee80211_
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_key *k;
 	struct ieee80211_frame *wh;
+	const struct ieee80211_rx_stats *rxs;
 	const struct ieee80211_cipher *cip;
 	uint8_t keyid;
 
+	/*
+	 * Check for hardware decryption and IV stripping.
+	 * If the IV is stripped then we definitely can't find a key.
+	 * Set the key to NULL but return true; upper layers
+	 * will need to handle a NULL key for a successful
+	 * decrypt.
+	 */
+	rxs = ieee80211_get_rx_params_ptr(m);
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED)) {
+		if (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) {
+			/*
+			 * Hardware decrypted, IV stripped.
+			 * We can't find a key with a stripped IV.
+			 * Return successful.
+			 */
+			*key = NULL;
+			return (1);
+		}
+	}
+
 	/* NB: this minimum size data frame could be bigger */
 	if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
 			"%s: WEP data frame too short, len %u\n",
 			__func__, m->m_pkthdr.len);
 		vap->iv_stats.is_rx_tooshort++;	/* XXX need unique stat? */
-		return NULL;
+		*key = NULL;
+		return (0);
 	}
 
 	/*
@@ -625,15 +648,29 @@ ieee80211_crypto_decap(struct ieee80211_
 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
 		    "unable to pullup %s header", cip->ic_name);
 		vap->iv_stats.is_rx_wepfail++;	/* XXX */
-		return NULL;
+		*key = NULL;
+		return (0);
+	}
+
+	/*
+	 * Attempt decryption.
+	 *
+	 * If we fail then don't return the key - return NULL
+	 * and an error.
+	 */
+	if (cip->ic_decap(k, m, hdrlen)) {
+		/* success */
+		*key = k;
+		return (1);
 	}
 
-	return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
+	/* Failure */
+	*key = NULL;
+	return (0);
 #undef IEEE80211_WEP_MINLEN
 #undef IEEE80211_WEP_HDRLEN
 }
 
-
 /*
  * Check and remove any MIC.
  */

Modified: head/sys/net80211/ieee80211_crypto.h
==============================================================================
--- head/sys/net80211/ieee80211_crypto.h	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_crypto.h	Sat Nov 19 02:00:24 2016	(r308823)
@@ -114,7 +114,8 @@ struct ieee80211_key {
 
 #define IEEE80211_KEY_DEVICE		/* flags owned by device driver */\
 	(IEEE80211_KEY_DEVKEY|IEEE80211_KEY_CIPHER0|IEEE80211_KEY_CIPHER1| \
-	 IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)
+	 IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC|IEEE80211_KEY_NOIV | \
+	 IEEE80211_KEY_NOIVMGT|IEEE80211_KEY_NOMIC|IEEE80211_KEY_NOMICMGT)
 
 #define	IEEE80211_KEY_BITS \
 	"\20\1XMIT\2RECV\3GROUP\4SWENCRYPT\5SWDECRYPT\6SWENMIC\7SWDEMIC" \
@@ -207,8 +208,8 @@ struct ieee80211_key *ieee80211_crypto_g
 		struct mbuf *);
 struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *,
 		struct mbuf *);
-struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *,
-		struct mbuf *, int);
+int	ieee80211_crypto_decap(struct ieee80211_node *,
+		struct mbuf *, int, struct ieee80211_key **);
 int ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
 		struct mbuf *, int);
 /*

Modified: head/sys/net80211/ieee80211_crypto_ccmp.c
==============================================================================
--- head/sys/net80211/ieee80211_crypto_ccmp.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_crypto_ccmp.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -162,12 +162,27 @@ ccmp_setiv(struct ieee80211_key *k, uint
 static int
 ccmp_encap(struct ieee80211_key *k, struct mbuf *m)
 {
+	const struct ieee80211_frame *wh;
 	struct ccmp_ctx *ctx = k->wk_private;
 	struct ieee80211com *ic = ctx->cc_ic;
 	uint8_t *ivp;
 	int hdrlen;
+	int is_mgmt;
 
 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+	wh = mtod(m, const struct ieee80211_frame *);
+	is_mgmt = IEEE80211_IS_MGMT(wh);
+
+	/*
+	 * Check to see if we need to insert IV/MIC.
+	 *
+	 * Some offload devices don't require the IV to be inserted
+	 * as part of the hardware encryption.
+	 */
+	if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
+		return 1;
+	if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))
+		return 1;
 
 	/*
 	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
@@ -217,12 +232,18 @@ READ_6(uint8_t b0, uint8_t b1, uint8_t b
 static int
 ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
 {
+	const struct ieee80211_rx_stats *rxs;
 	struct ccmp_ctx *ctx = k->wk_private;
 	struct ieee80211vap *vap = ctx->cc_vap;
 	struct ieee80211_frame *wh;
 	uint8_t *ivp, tid;
 	uint64_t pn;
 
+	rxs = ieee80211_get_rx_params_ptr(m);
+
+	if ((rxs != NULL) & (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))
+		goto finish;
+
 	/*
 	 * Header should have extended IV and sequence number;
 	 * verify the former and validate the latter.
@@ -261,17 +282,28 @@ ccmp_decap(struct ieee80211_key *k, stru
 	    !ccmp_decrypt(k, pn, m, hdrlen))
 		return 0;
 
+finish:
 	/*
 	 * Copy up 802.11 header and strip crypto bits.
 	 */
-	ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen);
-	m_adj(m, ccmp.ic_header);
-	m_adj(m, -ccmp.ic_trailer);
+	if (! ((rxs != NULL) & (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {
+		ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header,
+		    hdrlen);
+		m_adj(m, ccmp.ic_header);
+	}
+
+	/*
+	 * XXX TODO: see if MMIC_STRIP also covers CCMP MIC trailer.
+	 */
+	if (! ((rxs != NULL) & (rxs->c_pktflags & IEEE80211_RX_F_MMIC_STRIP)))
+		m_adj(m, -ccmp.ic_trailer);
 
 	/*
 	 * Ok to update rsc now.
 	 */
-	k->wk_keyrsc[tid] = pn;
+	if (! ((rxs != NULL) & (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {
+		k->wk_keyrsc[tid] = pn;
+	}
 
 	return 1;
 }

Modified: head/sys/net80211/ieee80211_crypto_tkip.c
==============================================================================
--- head/sys/net80211/ieee80211_crypto_tkip.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_crypto_tkip.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -177,8 +177,13 @@ tkip_encap(struct ieee80211_key *k, stru
 	struct tkip_ctx *ctx = k->wk_private;
 	struct ieee80211vap *vap = ctx->tc_vap;
 	struct ieee80211com *ic = vap->iv_ic;
+	struct ieee80211_frame *wh;
 	uint8_t *ivp;
 	int hdrlen;
+	int is_mgmt;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	is_mgmt = IEEE80211_IS_MGMT(wh);
 
 	/*
 	 * Handle TKIP counter measures requirement.
@@ -193,6 +198,16 @@ tkip_encap(struct ieee80211_key *k, stru
 		vap->iv_stats.is_crypto_tkipcm++;
 		return 0;
 	}
+
+	/*
+	 * Check to see whether IV needs to be included.
+	 */
+	if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
+		return 1;
+	if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))
+		return 1;
+
+
 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
 
 	/*
@@ -224,6 +239,19 @@ static int
 tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
 {
 	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211_frame *wh;
+	int is_mgmt;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	is_mgmt = IEEE80211_IS_MGMT(wh);
+
+	/*
+	 * Check to see whether MIC needs to be included.
+	 */
+	if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOMICMGT))
+		return 1;
+	if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOMIC))
+		return 1;
 
 	if (force || (k->wk_flags & IEEE80211_KEY_SWENMIC)) {
 		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
@@ -259,11 +287,20 @@ READ_6(uint8_t b0, uint8_t b1, uint8_t b
 static int
 tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
 {
+	const struct ieee80211_rx_stats *rxs;
 	struct tkip_ctx *ctx = k->wk_private;
 	struct ieee80211vap *vap = ctx->tc_vap;
 	struct ieee80211_frame *wh;
 	uint8_t *ivp, tid;
 
+	rxs = ieee80211_get_rx_params_ptr(m);
+
+	/*
+	 * If IV has been stripped, we skip most of the below.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))
+		goto finish;
+
 	/*
 	 * Header should have extended IV and sequence number;
 	 * verify the former and validate the latter.
@@ -318,11 +355,22 @@ tkip_decap(struct ieee80211_key *k, stru
 	    !tkip_decrypt(ctx, k, m, hdrlen))
 		return 0;
 
+finish:
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits - but only if we
+	 * are required to.
+	 */
+	if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {
+		memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *),
+		    hdrlen);
+		m_adj(m, tkip.ic_header);
+	}
+
 	/*
-	 * Copy up 802.11 header and strip crypto bits.
+	 * XXX TODO: do we need an option to potentially not strip the
+	 * WEP trailer?  Does "MMIC_STRIP" also mean this? Or?
 	 */
-	memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *), hdrlen);
-	m_adj(m, tkip.ic_header);
 	m_adj(m, -tkip.ic_trailer);
 
 	return 1;
@@ -334,11 +382,33 @@ tkip_decap(struct ieee80211_key *k, stru
 static int
 tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
 {
+	const struct ieee80211_rx_stats *rxs;
 	struct tkip_ctx *ctx = k->wk_private;
 	struct ieee80211_frame *wh;
 	uint8_t tid;
 
 	wh = mtod(m, struct ieee80211_frame *);
+	rxs = ieee80211_get_rx_params_ptr(m);
+
+	/*
+	 * If we are told about a MIC failure from the driver,
+	 * directly notify as a michael failure to the upper
+	 * layers.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_FAIL_MIC)) {
+		struct ieee80211vap *vap = ctx->tc_vap;
+		ieee80211_notify_michael_failure(vap, wh,
+		    k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
+		    k->wk_rxkeyix : k->wk_keyix);
+		return 0;
+	}
+
+	/*
+	 * If IV has been stripped, we skip most of the below.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MMIC_STRIP))
+		goto finish;
+
 	if ((k->wk_flags & IEEE80211_KEY_SWDEMIC) || force) {
 		struct ieee80211vap *vap = ctx->tc_vap;
 		int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh);
@@ -371,6 +441,7 @@ tkip_demic(struct ieee80211_key *k, stru
 	tid = ieee80211_gettid(wh);
 	k->wk_keyrsc[tid] = ctx->rx_rsc;
 
+finish:
 	return 1;
 }
 

Modified: head/sys/net80211/ieee80211_hostap.c
==============================================================================
--- head/sys/net80211/ieee80211_hostap.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_hostap.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -479,6 +479,16 @@ hostap_input(struct ieee80211_node *ni, 
 	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
 	uint8_t dir, type, subtype, qos;
 	uint8_t *bssid;
+	int is_hw_decrypted = 0;
+	int has_decrypted = 0;
+
+	/*
+	 * Some devices do hardware decryption all the way through
+	 * to pretending the frame wasn't encrypted in the first place.
+	 * So, tag it appropriately so it isn't discarded inappropriately.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+		is_hw_decrypted = 1;
 
 	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
@@ -668,7 +678,7 @@ hostap_input(struct ieee80211_node *ni, 
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+		if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -679,14 +689,14 @@ hostap_input(struct ieee80211_node *ni, 
 				IEEE80211_NODE_STAT(ni, rx_noprivacy);
 				goto out;
 			}
-			key = ieee80211_crypto_decap(ni, m, hdrspace);
-			if (key == NULL) {
+			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
 				/* NB: stats+msgs handled in crypto_decap */
 				IEEE80211_NODE_STAT(ni, rx_wepfail);
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+			has_decrypted = 1;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -769,7 +779,8 @@ hostap_input(struct ieee80211_node *ni, 
 			 * any non-PAE frames received without encryption.
 			 */
 			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
-			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
+			    ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+			    (is_hw_decrypted == 0) &&
 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
 				/*
 				 * Drop unencrypted frames.
@@ -851,13 +862,13 @@ hostap_input(struct ieee80211_node *ni, 
 				goto out;
 			}
 			hdrspace = ieee80211_hdrspace(ic, wh);
-			key = ieee80211_crypto_decap(ni, m, hdrspace);
-			if (key == NULL) {
+			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
 				/* NB: stats+msgs handled in crypto_decap */
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+			has_decrypted = 1;
 		}
 		/*
 		 * Pass the packet to radiotap before calling iv_recv_mgmt().

Modified: head/sys/net80211/ieee80211_sta.c
==============================================================================
--- head/sys/net80211/ieee80211_sta.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_sta.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -546,6 +546,16 @@ sta_input(struct ieee80211_node *ni, str
 	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
 	uint8_t dir, type, subtype, qos;
 	uint8_t *bssid;
+	int is_hw_decrypted = 0;
+	int has_decrypted = 0;
+
+	/*
+	 * Some devices do hardware decryption all the way through
+	 * to pretending the frame wasn't encrypted in the first place.
+	 * So, tag it appropriately so it isn't discarded inappropriately.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+		is_hw_decrypted = 1;
 
 	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
@@ -724,6 +734,21 @@ sta_input(struct ieee80211_node *ni, str
 		}
 
 		/*
+		 * Handle privacy requirements for hardware decryption
+		 * devices.
+		 *
+		 * For those devices, a handful of things happen.
+		 *
+		 * + If IV has been stripped, then we can't run
+		 *   ieee80211_crypto_decap() - none of the key
+		 * + If MIC has been stripped, we can't validate
+		 *   MIC here.
+		 * + If MIC fails, then we need to communicate a
+		 *   MIC failure up to the stack - but we don't know
+		 *   which key was used.
+		 */
+
+		/*
 		 * Handle privacy requirements.  Note that we
 		 * must not be preempted from here until after
 		 * we (potentially) call ieee80211_crypto_demic;
@@ -731,7 +756,7 @@ sta_input(struct ieee80211_node *ni, str
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+		if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -742,14 +767,14 @@ sta_input(struct ieee80211_node *ni, str
 				IEEE80211_NODE_STAT(ni, rx_noprivacy);
 				goto out;
 			}
-			key = ieee80211_crypto_decap(ni, m, hdrspace);
-			if (key == NULL) {
+			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
 				/* NB: stats+msgs handled in crypto_decap */
 				IEEE80211_NODE_STAT(ni, rx_wepfail);
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+			has_decrypted = 1;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -779,8 +804,13 @@ sta_input(struct ieee80211_node *ni, str
 
 		/*
 		 * Next strip any MSDU crypto bits.
+		 *
+		 * Note: we can't do MIC stripping/verification if the
+		 * upper layer has stripped it.  We have to check MIC
+		 * ourselves.  So, key may be NULL, but we have to check
+		 * the RX status.
 		 */
-		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+		if (!ieee80211_crypto_demic(vap, key, m, 0)) {
 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
 			    ni->ni_macaddr, "data", "%s", "demic error");
 			vap->iv_stats.is_rx_demicfail++;
@@ -834,7 +864,8 @@ sta_input(struct ieee80211_node *ni, str
 			 * any non-PAE frames received without encryption.
 			 */
 			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
-			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
+			    ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+			    (is_hw_decrypted == 0) &&
 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
 				/*
 				 * Drop unencrypted frames.
@@ -883,6 +914,16 @@ sta_input(struct ieee80211_node *ni, str
 			    ether_sprintf(wh->i_addr2), rssi);
 		}
 #endif
+
+		/*
+		 * Note: See above for hardware offload privacy requirements.
+		 *       It also applies here.
+		 */
+
+		/*
+		 * Again, having encrypted flag set check would be good, but
+		 * then we have to also handle crypto_decap() like above.
+		 */
 		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
 				/*
@@ -905,11 +946,16 @@ sta_input(struct ieee80211_node *ni, str
 				goto out;
 			}
 			hdrspace = ieee80211_hdrspace(ic, wh);
-			key = ieee80211_crypto_decap(ni, m, hdrspace);
-			if (key == NULL) {
+
+			/*
+			 * Again, if IV/MIC was stripped, then this whole
+			 * setup will fail.  That's going to need some poking.
+			 */
+			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
 				/* NB: stats+msgs handled in crypto_decap */
 				goto out;
 			}
+			has_decrypted = 1;
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		}

Modified: head/sys/net80211/ieee80211_wds.c
==============================================================================
--- head/sys/net80211/ieee80211_wds.c	Sat Nov 19 01:51:56 2016	(r308822)
+++ head/sys/net80211/ieee80211_wds.c	Sat Nov 19 02:00:24 2016	(r308823)
@@ -417,6 +417,16 @@ wds_input(struct ieee80211_node *ni, str
 	struct ether_header *eh;
 	int hdrspace, need_tap = 1;	/* mbuf need to be tapped. */
 	uint8_t dir, type, subtype, qos;
+	int is_hw_decrypted = 0;
+	int has_decrypted = 0;
+
+	/*
+	 * Some devices do hardware decryption all the way through
+	 * to pretending the frame wasn't encrypted in the first place.
+	 * So, tag it appropriately so it isn't discarded inappropriately.
+	 */
+	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+		is_hw_decrypted = 1;
 
 	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
@@ -544,7 +554,7 @@ wds_input(struct ieee80211_node *ni, str
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+		if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -555,14 +565,14 @@ wds_input(struct ieee80211_node *ni, str
 				IEEE80211_NODE_STAT(ni, rx_noprivacy);
 				goto out;
 			}
-			key = ieee80211_crypto_decap(ni, m, hdrspace);
-			if (key == NULL) {
+			if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
 				/* NB: stats+msgs handled in crypto_decap */
 				IEEE80211_NODE_STAT(ni, rx_wepfail);
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
 			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+			has_decrypted = 1;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -593,7 +603,7 @@ wds_input(struct ieee80211_node *ni, str
 		/*
 		 * Next strip any MSDU crypto bits.
 		 */
-		if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+		if (!ieee80211_crypto_demic(vap, key, m, 0)) {
 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
 			    ni->ni_macaddr, "data", "%s", "demic error");
 			vap->iv_stats.is_rx_demicfail++;
@@ -647,7 +657,8 @@ wds_input(struct ieee80211_node *ni, str
 			 * any non-PAE frames received without encryption.
 			 */
 			if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
-			    (key == NULL && (m->m_flags & M_WEP) == 0) &&
+			    ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+			    (is_hw_decrypted == 0) &&
 			    eh->ether_type != htons(ETHERTYPE_PAE)) {
 				/*
 				 * Drop unencrypted frames.


More information about the svn-src-head mailing list