aboutsummaryrefslogtreecommitdiffstats
path: root/package/madwifi/patches/309-micfail_detect.patch
blob: ca4103a206cebe1d5571ca330cdd2f4bc28a0feb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
@@ -6457,6 +6457,7 @@ ath_rx_poll(struct net_device *dev, int
 	int type;
 	u_int phyerr;
 	u_int processed = 0, early_stop = 0;
+	u_int mic_fail = 0;
 
 	DPRINTF(sc, ATH_DEBUG_RX_PROC, "invoked\n");
 process_rx_again:
@@ -6558,24 +6559,8 @@ process_rx_again:
 			}
 			if (rs->rs_status & HAL_RXERR_MIC) {
 				sc->sc_stats.ast_rx_badmic++;
-				/*
-				 * Do minimal work required to hand off
-				 * the 802.11 header for notification.
-				 */
-				/* XXX frag's and QoS frames */
-				if (len >= sizeof (struct ieee80211_frame)) {
-					bus_dma_sync_single(sc->sc_bdev,
-					    bf->bf_skbaddr, len,
-					    BUS_DMA_FROMDEVICE);
-#if 0
-/* XXX revalidate MIC, lookup ni to find VAP */
-					ieee80211_notify_michael_failure(ic,
-					    (struct ieee80211_frame *)skb->data,
-					    sc->sc_splitmic ?
-					        rs->rs_keyix - 32 : rs->rs_keyix
-					);
-#endif
-				}
+				mic_fail = 1;
+				goto rx_accept;
 			}
 			/*
 			 * Reject error frames if we have no vaps that
@@ -6614,8 +6599,9 @@ rx_accept:
 		/*
 		 * Finished monitor mode handling, now reject
 		 * error frames before passing to other vaps
+		 * Ignore MIC failures here, as we need to recheck them
 		 */
-		if (rs->rs_status != 0) {
+		if (rs->rs_status & ~(HAL_RXERR_MIC | HAL_RXERR_DECRYPT)) {
 			ieee80211_dev_kfree_skb(&skb);
 			goto rx_next;
 		}
@@ -6623,6 +6609,26 @@ rx_accept:
 		/* remove the CRC */
 		skb_trim(skb, skb->len - IEEE80211_CRC_LEN);
 
+		if (mic_fail) {
+			/* Ignore control frames which are reported with mic error */
+		    if ((((struct ieee80211_frame *)skb->data)->i_fc[0] &
+					IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+				goto drop_micfail;
+
+			ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) skb->data);
+
+			if (ni && ni->ni_table) {
+				ieee80211_check_mic(ni, skb);
+				ieee80211_unref_node(&ni);
+			}
+
+drop_micfail:
+			dev_kfree_skb_any(skb);
+			skb = NULL;
+			mic_fail = 0;
+			goto rx_next;
+		}
+
 		/*
 		 * From this point on we assume the frame is at least
 		 * as large as ieee80211_frame_min; verify that.
@@ -6635,6 +6641,7 @@ rx_accept:
 			goto rx_next;
 		}
 
+		/* MIC failure. Drop the packet in any case */
 		/*
 		 * Normal receive.
 		 */
--- a/net80211/ieee80211_crypto_ccmp.c
+++ b/net80211/ieee80211_crypto_ccmp.c
@@ -73,7 +73,7 @@ static int ccmp_setkey(struct ieee80211_
 static int ccmp_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
 static int ccmp_decap(struct ieee80211_key *, struct sk_buff *, int);
 static int ccmp_enmic(struct ieee80211_key *, struct sk_buff *, int);
-static int ccmp_demic(struct ieee80211_key *, struct sk_buff *, int);
+static int ccmp_demic(struct ieee80211_key *, struct sk_buff *, int, int);
 
 static const struct ieee80211_cipher ccmp = {
 	.ic_name	= "AES-CCM",
@@ -314,7 +314,7 @@ ccmp_decap(struct ieee80211_key *k, stru
  * Verify and strip MIC from the frame.
  */
 static int
-ccmp_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen)
+ccmp_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen, int force)
 {
 	return 1;
 }
--- a/net80211/ieee80211_crypto.h
+++ b/net80211/ieee80211_crypto.h
@@ -145,7 +145,7 @@ struct ieee80211_cipher {
 	int (*ic_encap)(struct ieee80211_key *, struct sk_buff *, u_int8_t);
 	int (*ic_decap)(struct ieee80211_key *, struct sk_buff *, int);
 	int (*ic_enmic)(struct ieee80211_key *, struct sk_buff *, int);
-	int (*ic_demic)(struct ieee80211_key *, struct sk_buff *, int);
+	int (*ic_demic)(struct ieee80211_key *, struct sk_buff *, int, int);
 };
 extern const struct ieee80211_cipher ieee80211_cipher_none;
 
@@ -163,10 +163,10 @@ struct ieee80211_key *ieee80211_crypto_d
  */
 static __inline int
 ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
-	struct sk_buff *skb, int hdrlen)
+	struct sk_buff *skb, int hdrlen, int force)
 {
 	const struct ieee80211_cipher *cip = k->wk_cipher;
-	return (cip->ic_miclen > 0 ? cip->ic_demic(k, skb, hdrlen) : 1);
+	return (cip->ic_miclen > 0 ? cip->ic_demic(k, skb, hdrlen, force) : 1);
 }
 
 /*
--- a/net80211/ieee80211_crypto_none.c
+++ b/net80211/ieee80211_crypto_none.c
@@ -52,7 +52,7 @@ static int none_setkey(struct ieee80211_
 static int none_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
 static int none_decap(struct ieee80211_key *, struct sk_buff *, int);
 static int none_enmic(struct ieee80211_key *, struct sk_buff *, int);
-static int none_demic(struct ieee80211_key *, struct sk_buff *, int);
+static int none_demic(struct ieee80211_key *, struct sk_buff *, int, int);
 
 const struct ieee80211_cipher ieee80211_cipher_none = {
 	.ic_name	= "NONE",
@@ -137,7 +137,7 @@ none_enmic(struct ieee80211_key *k, stru
 }
 
 static int
-none_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen)
+none_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen, int force)
 {
 	struct ieee80211vap *vap = k->wk_private;
 
--- a/net80211/ieee80211_crypto_tkip.c
+++ b/net80211/ieee80211_crypto_tkip.c
@@ -57,7 +57,7 @@ static int tkip_setkey(struct ieee80211_
 static int tkip_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
 static int tkip_enmic(struct ieee80211_key *, struct sk_buff *, int);
 static int tkip_decap(struct ieee80211_key *, struct sk_buff *, int);
-static int tkip_demic(struct ieee80211_key *, struct sk_buff *, int);
+static int tkip_demic(struct ieee80211_key *, struct sk_buff *, int, int);
 
 static const struct ieee80211_cipher tkip  = {
 	.ic_name	= "TKIP",
@@ -339,7 +339,7 @@ tkip_decap(struct ieee80211_key *k, stru
  * Verify and strip MIC from the frame.
  */
 static int
-tkip_demic(struct ieee80211_key *k, struct sk_buff *skb0, int hdrlen)
+tkip_demic(struct ieee80211_key *k, struct sk_buff *skb0, int hdrlen, int force)
 {
 	struct tkip_ctx *ctx = k->wk_private;
 	struct sk_buff *skb;
@@ -355,7 +355,7 @@ tkip_demic(struct ieee80211_key *k, stru
 	}
 	wh = (struct ieee80211_frame *) skb0->data;
 	/* NB: skb left pointing at last in chain */
-	if (k->wk_flags & IEEE80211_KEY_SWMIC) {
+	if ((k->wk_flags & IEEE80211_KEY_SWMIC) || force) {
 		struct ieee80211vap *vap = ctx->tc_vap;
 		u8 mic[IEEE80211_WEP_MICLEN];
 		u8 mic0[IEEE80211_WEP_MICLEN];
--- a/net80211/ieee80211_crypto_wep.c
+++ b/net80211/ieee80211_crypto_wep.c
@@ -54,7 +54,7 @@ static int wep_setkey(struct ieee80211_k
 static int wep_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
 static int wep_decap(struct ieee80211_key *, struct sk_buff *, int);
 static int wep_enmic(struct ieee80211_key *, struct sk_buff *, int);
-static int wep_demic(struct ieee80211_key *, struct sk_buff *, int);
+static int wep_demic(struct ieee80211_key *, struct sk_buff *, int, int);
 
 static const struct ieee80211_cipher wep = {
 	.ic_name	= "WEP",
@@ -244,7 +244,7 @@ wep_decap(struct ieee80211_key *k, struc
  * Verify and strip MIC from the frame.
  */
 static int
-wep_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen)
+wep_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen, int force)
 {
 	return 1;
 }
--- a/net80211/ieee80211_input.c
+++ b/net80211/ieee80211_input.c
@@ -669,7 +669,7 @@ ieee80211_input(struct ieee80211vap * va
 		 * Next strip any MSDU crypto bits.
 		 */
 		if (key != NULL &&
-		    !ieee80211_crypto_demic(vap, key, skb, hdrspace)) {
+		    !ieee80211_crypto_demic(vap, key, skb, hdrspace, 0)) {
 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
 				ni->ni_macaddr, "data", "%s", "demic error");
 			IEEE80211_NODE_STAT(ni, rx_demicfail);
@@ -4293,6 +4293,47 @@ ath_eth_type_trans(struct sk_buff *skb,
 }
 #endif
 
+/*
+ * Process a frame w/ hw detected MIC failure.
+ * The frame will be dropped in any case.
+ */
+void
+ieee80211_check_mic(struct ieee80211_node *ni, struct sk_buff *skb)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+
+	struct ieee80211_frame *wh;
+	struct ieee80211_key *key;
+	int hdrspace;
+	struct ieee80211com *ic = vap->iv_ic;
+
+	if (skb->len < sizeof(struct ieee80211_frame_min)) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL,
+		    "too short (1): len %u", skb->len);
+		vap->iv_stats.is_rx_tooshort++;
+		return;
+	}
+
+	wh = (struct ieee80211_frame *)skb->data;
+
+	hdrspace = ieee80211_hdrspace(ic, wh);
+	key = ieee80211_crypto_decap(ni, skb, hdrspace);
+	if (key == NULL) {
+		/* NB: stats+msgs handled in crypto_decap */
+		IEEE80211_NODE_STAT(ni, rx_wepfail);
+		return;
+	}
+
+	if (!ieee80211_crypto_demic(vap, key, skb, hdrspace, 1)) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+			ni->ni_macaddr, "data", "%s", "demic error");
+			IEEE80211_NODE_STAT(ni, rx_demicfail);
+	}
+	return;
+}
+EXPORT_SYMBOL(ieee80211_check_mic);
+
 #ifdef IEEE80211_DEBUG
 /*
  * Debugging support.
--- a/net80211/ieee80211_proto.h
+++ b/net80211/ieee80211_proto.h
@@ -90,6 +90,7 @@ int ieee80211_iserp_rateset(struct ieee8
 void ieee80211_set11gbasicrates(struct ieee80211_rateset *, enum ieee80211_phymode);
 enum ieee80211_phymode ieee80211_get11gbasicrates(struct ieee80211_rateset *);
 void ieee80211_send_pspoll(struct ieee80211_node *);
+void ieee80211_check_mic(struct ieee80211_node *, struct sk_buff *);
 
 /*
  * Return the size of the 802.11 header for a management or data frame.
--- a/net80211/ieee80211_linux.c
+++ b/net80211/ieee80211_linux.c
@@ -337,8 +337,8 @@ ieee80211_notify_replay_failure(struct i
 	/* TODO: needed parameters: count, keyid, key type, src address, TSC */
 	snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=" MAC_FMT ")", tag,
 		k->wk_keyix,
-		IEEE80211_IS_MULTICAST(wh->i_addr1) ?  "broad" : "uni",
-		MAC_ADDR(wh->i_addr1));
+		IEEE80211_IS_MULTICAST(wh->i_addr2) ?  "broad" : "uni",
+		MAC_ADDR(wh->i_addr2));
 	memset(&wrqu, 0, sizeof(wrqu));
 	wrqu.data.length = strlen(buf);
 	wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
--- a/net80211/ieee80211_output.c
+++ b/net80211/ieee80211_output.c
@@ -1074,13 +1074,16 @@ ieee80211_encap(struct ieee80211_node *n
 			cip = (struct ieee80211_cipher *) key->wk_cipher;
 			ciphdrsize = cip->ic_header;
 			tailsize += (cip->ic_trailer + cip->ic_miclen);
+
+			/* add the 8 bytes MIC length */
+			if (cip->ic_cipher == IEEE80211_CIPHER_TKIP)
+				pktlen += IEEE80211_WEP_MICLEN;
 		}
 
 		pdusize = vap->iv_fragthreshold - (hdrsize_nopad + ciphdrsize);
 		fragcnt = *framecnt =
-			((pktlen - (hdrsize_nopad + ciphdrsize)) / pdusize) +
-			(((pktlen - (hdrsize_nopad + ciphdrsize)) %
-				pdusize == 0) ? 0 : 1);
+			((pktlen - hdrsize_nopad) / pdusize) +
+			(((pktlen - hdrsize_nopad) % pdusize == 0) ? 0 : 1);
 
 		/*
 		 * Allocate sk_buff for each subsequent fragment; First fragment
--- a/net80211/ieee80211_node.c
+++ b/net80211/ieee80211_node.c
@@ -2264,11 +2264,13 @@ ieee80211_node_leave(struct ieee80211_no
 	/* From this point onwards we can no longer find the node,
 	 * so no more references are generated
 	 */
-	ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
-	ieee80211_del_wds_node(nt, ni);
-	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
-	node_table_leave_locked(nt, ni);
-	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+	if (nt) {
+		ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
+		ieee80211_del_wds_node(nt, ni);
+		IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+		node_table_leave_locked(nt, ni);
+		IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+	}
 
 	/*
 	 * If node wasn't previously associated all