Skip to contentSkip to conversation

mac80211 re-transmission repair

PATCH In review Johannes Berg 3 participants 10 messages Jan 17, 2010 – Jan 25, 2010 mac80211

lore
Share & tools
lore

Johannes Berg

author maintainer
#0 lore raw

cover mac80211 re-transmission repair

I sent this series a long time ago as RFC and have been running it ever since ... never totally confirmed it works correctly but at this point it's probably good enough for wider deployment.

Basically it revamps some tx handling so we can do encryption and other stuff separate from each other for re-transmissions with frames that a device has rejected because the destination station was asleep.

johannes

author maintainer
#1 lore raw

PATCH 1/5 mac80211: move and rename misc tx handler

This TX handler is used only for assigning the
station pointer in the control information, so
give it a better name. Also move it before rate
control.

---
 net/mac80211/tx.c |   24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)
net/mac80211/tx.c +12 -12
@@ -560,6 +560,17 @@ ieee80211_tx_h_select_key(struct ieee802
 }
 
 static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_sta(struct ieee80211_tx_data *tx)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
+	if (tx->sta)
+		info->control.sta = &tx->sta->sta;
+
+	return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
@@ -734,17 +745,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
 }
 
 static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
-{
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-
-	if (tx->sta)
-		info->control.sta = &tx->sta->sta;
-
-	return TX_CONTINUE;
-}
-
-static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
@@ -1292,10 +1292,10 @@ static int invoke_tx_handlers(struct iee
 	CALL_TXH(ieee80211_tx_h_check_assoc);
 	CALL_TXH(ieee80211_tx_h_ps_buf);
 	CALL_TXH(ieee80211_tx_h_select_key);
+	CALL_TXH(ieee80211_tx_h_sta);
 	CALL_TXH(ieee80211_tx_h_michael_mic_add);
 	if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
 		CALL_TXH(ieee80211_tx_h_rate_ctrl);
-	CALL_TXH(ieee80211_tx_h_misc);
 	CALL_TXH(ieee80211_tx_h_sequence);
 	CALL_TXH(ieee80211_tx_h_fragment);
 	/* handlers after fragment must be aware of tx info fragmentation! */
author maintainer
#2 lore raw

PATCH 2/5 mac80211: clear TX control on filtered frames

In reply to Johannes Berg

When an skb survived a round-trip through the driver and needs to be re-used, its control information is definitely not valid any more, the driver will have overwritten it.

---
 net/mac80211/status.c |    8 ++++++++
 1 file changed, 8 insertions(+)
net/mac80211/status.c +8 -0
@@ -69,6 +69,14 @@ static void ieee80211_handle_filtered_fr
 	 */
 	goto drop;
 
+	/*
+	 * This skb 'survived' a round-trip through the driver, and
+	 * hopefully the driver didn't mangle it too badly. However,
+	 * we can definitely not rely on the the control information
+	 * being correct. Clear it so we don't get junk there.
+	 */
+	memset(&info->control, 0, sizeof(info->control));
+
 	sta->tx_filtered_count++;
 
 	/*
author maintainer
#3 lore raw

PATCH 3/5 mac80211: remove useless setting of IEEE80211_TX_INTFL_DONT_ENCRYPT

In reply to Johannes Berg

There's no value in setting a flag that will never be checked after this point, this seems to be legacy code -- I think previously the flag was used to check whether to encrypt the frame or not. Now, however, the flag need not be set, and setting it actually interferes if the frame will be processed again later.

---
 net/mac80211/tx.c |    3 ---
 1 file changed, 3 deletions(-)
net/mac80211/tx.c +0 -3
@@ -553,9 +553,6 @@ ieee80211_tx_h_select_key(struct ieee802
 		}
 	}
 
-	if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-		info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-
 	return TX_CONTINUE;
 }
 
author maintainer
#4 lore raw

PATCH 4/5 mac80211: move control.hw_key assignment

In reply to Johannes Berg

When mac80211 asks a driver to encrypt a frame, it must assign the control.hw_key pointer for it to know which key to use etc. Currently, mac80211 does this whenever it would software-encrypt a frame.

Change the logic of this code to assign the hw_key pointer when selecting the key, and later check it when deciding whether to encrypt the frame or let it be encrypted by the hardware. This allows us to later simply skip the encryption function since it no longer modifies the TX control.

---
 net/mac80211/tkip.c |   11 +++++-----
 net/mac80211/tx.c   |    9 ++++++++
 net/mac80211/wep.c  |   18 ++++++++--------
 net/mac80211/wpa.c  |   57 ++++++++++++++++++++--------------------------------
 4 files changed, 46 insertions(+), 49 deletions(-)
net/mac80211/tkip.c +6 -5
@@ -195,11 +195,13 @@ void ieee80211_get_tkip_key(struct ieee8
 }
 EXPORT_SYMBOL(ieee80211_get_tkip_key);
 
-/* Encrypt packet payload with TKIP using @key. @pos is a pointer to the
+/*
+ * Encrypt packet payload with TKIP using @key. @pos is a pointer to the
  * beginning of the buffer containing payload. This payload must include
- * headroom of eight octets for IV and Ext. IV and taildroom of four octets
- * for ICV. @payload_len is the length of payload (_not_ including extra
- * headroom and tailroom). @ta is the transmitter addresses. */
+ * the IV/Ext.IV and space for (taildroom) four octets for ICV.
+ * @payload_len is the length of payload (_not_ including IV/ICV length).
+ * @ta is the transmitter addresses.
+ */
 void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
 				 struct ieee80211_key *key,
 				 u8 *pos, size_t payload_len, u8 *ta)
@@ -214,7 +216,6 @@ void ieee80211_tkip_encrypt_data(struct 
 
 	tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key);
 
-	pos = ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16);
 	ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len);
 }
 
net/mac80211/wpa.c +22 -35

122 lines not on this page · raw message

net/mac80211/wep.c +9 -9
@@ -305,20 +305,20 @@ static int wep_encrypt_skb(struct ieee80
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
-	if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
+	if (!info->control.hw_key) {
 		if (ieee80211_wep_encrypt(tx->local, skb, tx->key->conf.key,
 					  tx->key->conf.keylen,
 					  tx->key->conf.keyidx))
 			return -1;
-	} else {
-		info->control.hw_key = &tx->key->conf;
-		if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
-			if (!ieee80211_wep_add_iv(tx->local, skb,
-						  tx->key->conf.keylen,
-						  tx->key->conf.keyidx))
-				return -1;
-		}
 	}
+
+	if (info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
+		if (!ieee80211_wep_add_iv(tx->local, skb,
+					  tx->key->conf.keylen,
+					  tx->key->conf.keyidx))
+			return -1;
+	}
+
 	return 0;
 }
 
net/mac80211/tx.c +9 -0
@@ -529,6 +529,8 @@ ieee80211_tx_h_select_key(struct ieee802
 		tx->key = NULL;
 
 	if (tx->key) {
+		bool skip_hw = false;
+
 		tx->key->tx_rx_count++;
 		/* TODO: add threshold stuff again */
 
@@ -545,12 +547,19 @@ ieee80211_tx_h_select_key(struct ieee802
 			    !ieee80211_use_mfp(hdr->frame_control, tx->sta,
 					       tx->skb))
 				tx->key = NULL;
+			skip_hw = (tx->key->conf.flags &
+						IEEE80211_KEY_FLAG_SW_MGMT) &&
+				   ieee80211_is_mgmt(hdr->frame_control);
 			break;
 		case ALG_AES_CMAC:
 			if (!ieee80211_is_mgmt(hdr->frame_control))
 				tx->key = NULL;
 			break;
 		}
+
+		if (!skip_hw &&
+		    tx->key->conf.flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
+			info->control.hw_key = &tx->key->conf;
 	}
 
 	return TX_CONTINUE;
#5 lore raw

Re: [PATCH 4/5] mac80211: move control.hw_key assignment

Quoted 5 lines · On Sunday 17 January 2010 01:47:58 Johannes Berg wrote:

When mac80211 asks a driver to encrypt a frame, it must assign the control.hw_key pointer for it to know which key to use etc. Currently, mac80211 does this whenever it would software-encrypt a frame.

Quoted 8 lines

Change the logic of this code to assign the hw_key pointer when selecting the key, and later check it when deciding whether to encrypt the frame or let it be encrypted by the hardware. This allows us to later simply skip the encryption function since it no longer modifies the TX control.

Hi Johannes,
this patch breaks my wireless connection. I'm able to authenticate and associate to my AP but I can't use the connection, a simple ping towards my AP doesn't work. I bisected the problem and your patch is the bad commit.

I have the latest wireless-testing.git (v2.6.33-rc5-47285-g96ada3c). After having removed all the commits related to this patch, i.e.

- commit f12553ebe045a8a40ab33fa500fb57d10706e226
- commit e4fca007b06165900d0e44e8d5e251376819bf5d
- commit 813d76694043d00b59475baa1fbfaf54a2eb7fad

the connection is working again.

Fabio

author maintainer
#6 lore raw

Re: [PATCH 4/5] mac80211: move control.hw_key assignment

Hi,

Quoted 4 lines · Fabio Rossi

this patch breaks my wireless connection. I'm able to authenticate and associate to my AP but I can't use the connection, a simple ping towards my AP doesn't work. I bisected the problem and your patch is the bad commit.

Quoted 8 lines

I have the latest wireless-testing.git (v2.6.33-rc5-47285-g96ada3c). After having removed all the commits related to this patch, i.e.

- commit f12553ebe045a8a40ab33fa500fb57d10706e226
- commit e4fca007b06165900d0e44e8d5e251376819bf5d
- commit 813d76694043d00b59475baa1fbfaf54a2eb7fad

the connection is working again.

Hmm. Are you using hw crypto?

johannes

#7 lore raw

Re: [PATCH 4/5] mac80211: move control.hw_key assignment

Quoted 11 lines · On Sunday 24 January 2010 12:29:51 Johannes Berg wrote:

Hi,

Quoted 8 lines · Fabio Rossi

this patch breaks my wireless connection. I'm able to authenticate and associate to my AP but I can't use the connection, a simple ping towards my AP doesn't work. I bisected the problem and your patch is the bad commit.

I have the latest wireless-testing.git (v2.6.33-rc5-47285-g96ada3c). After having removed all the commits related to this patch, i.e.

Quoted 7 lines
Quoted 5 lines

- commit f12553ebe045a8a40ab33fa500fb57d10706e226
- commit e4fca007b06165900d0e44e8d5e251376819bf5d
- commit 813d76694043d00b59475baa1fbfaf54a2eb7fad

the connection is working again.

Hmm. Are you using hw crypto?

Yes, from what I have understood it should be the default using ath5k. Anyway I added your last patch

	[PATCH] mac80211: fix sw crypto

fixing my problem.

Thanks,
Fabio

#8 lore raw

Re: [PATCH 4/5] mac80211: move control.hw_key assignment

In reply to Fabio Rossi
Quoted 21 lines · Fabio Rossi
Quoted 5 lines · On Sunday 17 January 2010 01:47:58 Johannes Berg wrote:

When mac80211 asks a driver to encrypt a frame, it must assign the control.hw_key pointer for it to know which key to use etc. Currently, mac80211 does this whenever it would software-encrypt a frame.

Quoted 8 lines

Change the logic of this code to assign the hw_key pointer when selecting the key, and later check it when deciding whether to encrypt the frame or let it be encrypted by the hardware. This allows us to later simply skip the encryption function since it no longer modifies the TX control.

Hi Johannes,
this patch breaks my wireless connection. I'm able to authenticate and associate to my AP but I can't use the connection, a simple ping towards my AP doesn't work. I bisected the problem and your patch is the bad commit.

Quoted 8 lines

I have the latest wireless-testing.git (v2.6.33-rc5-47285-g96ada3c). After having removed all the commits related to this patch, i.e.

- commit f12553ebe045a8a40ab33fa500fb57d10706e226
- commit e4fca007b06165900d0e44e8d5e251376819bf5d
- commit 813d76694043d00b59475baa1fbfaf54a2eb7fad

the connection is working again.

Exactly same problem, here with iwl3945.
I am using software encryption (CCMP), and I noticed that it is the default in iwl3945. Also hardware encryption doesn't work (unrelated issue)

Best regards,
Maxim Levitsky

author maintainer
#9 lore raw

PATCH 5/5 mac80211: re-enable re-transmission of filtered frames

In reply to Johannes Berg

In an earlier commit,

mac80211: disable software retry for now

Pavel Roskin reported a problem that seems to be due to software retry of already transmitted frames. It turns out that we've never done that correctly, but due to some recent changes it now crashes in the TX code. I've added a comment in the patch that explains the problem better and also points to possible solutions -- which I can't implement right now.

I disabled software retry of failed/filtered frames because it was broken. With the work of the previous patches, it now becomes fairly easy to re-enable it by adding a flag indicating that the frame shouldn't be modified, but still running it through the transmit handlers to populate the control information.

---
 include/net/mac80211.h |    4 ++++
 net/mac80211/status.c  |   32 +++++---------------------------
 net/mac80211/tx.c      |    7 ++++++-
 3 files changed, 15 insertions(+), 28 deletions(-)
net/mac80211/status.c +5 -27
@@ -45,37 +45,16 @@ static void ieee80211_handle_filtered_fr
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
 	/*
-	 * XXX: This is temporary!
-	 *
-	 *	The problem here is that when we get here, the driver will
-	 *	quite likely have pretty much overwritten info->control by
-	 *	using info->driver_data or info->rate_driver_data. Thus,
-	 *	when passing out the frame to the driver again, we would be
-	 *	passing completely bogus data since the driver would then
-	 *	expect a properly filled info->control. In mac80211 itself
-	 *	the same problem occurs, since we need info->control.vif
-	 *	internally.
-	 *
-	 *	To fix this, we should send the frame through TX processing
-	 *	again. However, it's not that simple, since the frame will
-	 *	have been software-encrypted (if applicable) already, and
-	 *	encrypting it again doesn't do much good. So to properly do
-	 *	that, we not only have to skip the actual 'raw' encryption
-	 *	(key selection etc. still has to be done!) but also the
-	 *	sequence number assignment since that impacts the crypto
-	 *	encapsulation, of course.
-	 *
-	 *	Hence, for now, fix the bug by just dropping the frame.
-	 */
-	goto drop;
-
-	/*
 	 * This skb 'survived' a round-trip through the driver, and
 	 * hopefully the driver didn't mangle it too badly. However,
 	 * we can definitely not rely on the the control information
-	 * being correct. Clear it so we don't get junk there.
+	 * being correct. Clear it so we don't get junk there, and
+	 * indicate that it needs new processing, but must not be
+	 * modified/encrypted again.
 	 */
 	memset(&info->control, 0, sizeof(info->control));
+	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING |
+		       IEEE80211_TX_INTFL_RETRANSMISSION;
 
 	sta->tx_filtered_count++;
 
@@ -130,7 +109,6 @@ static void ieee80211_handle_filtered_fr
 		return;
 	}
 
- drop:
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 	if (net_ratelimit())
 		printk(KERN_DEBUG "%s: dropped TX filtered frame, "
include/net/mac80211.h +4 -0
@@ -271,6 +271,9 @@ struct ieee80211_bss_conf {
  *	transmit function after the current frame, this can be used
  *	by drivers to kick the DMA queue only if unset or when the
  *	queue gets full.
+ * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted
+ *	after TX status because the destination was asleep, it must not
+ *	be modified again (no seqno assignment, crypto, etc.)
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_REQ_TX_STATUS		= BIT(0),
@@ -291,6 +294,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_INTFL_DONT_ENCRYPT		= BIT(16),
 	IEEE80211_TX_CTL_PSPOLL_RESPONSE	= BIT(17),
 	IEEE80211_TX_CTL_MORE_FRAMES		= BIT(18),
+	IEEE80211_TX_INTFL_RETRANSMISSION	= BIT(19),
 };
 
 /**
net/mac80211/tx.c +6 -1
@@ -1285,6 +1285,7 @@ static int __ieee80211_tx(struct ieee802
 static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
 {
 	struct sk_buff *skb = tx->skb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	ieee80211_tx_result res = TX_DROP;
 
 #define CALL_TXH(txh) \
@@ -1299,9 +1300,13 @@ static int invoke_tx_handlers(struct iee
 	CALL_TXH(ieee80211_tx_h_ps_buf);
 	CALL_TXH(ieee80211_tx_h_select_key);
 	CALL_TXH(ieee80211_tx_h_sta);
-	CALL_TXH(ieee80211_tx_h_michael_mic_add);
 	if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
 		CALL_TXH(ieee80211_tx_h_rate_ctrl);
+
+	if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION))
+		goto txh_done;
+
+	CALL_TXH(ieee80211_tx_h_michael_mic_add);
 	CALL_TXH(ieee80211_tx_h_sequence);
 	CALL_TXH(ieee80211_tx_h_fragment);
 	/* handlers after fragment must be aware of tx info fragmentation! */