kettenis@ noticed that an athn(4) node cache can contain bogus clients
with MAC addreses that look like bit-flipped versions of MAC addresses
of legitimate clients.
I think this is due to my recent fix to allow block ack requests to
pass through athn(4).
The driver calls ieee80211_find_rxnode() on such frames.
In hostap mode this function is responsible for creating new node cache
entries in case the transmitter's address is not known yet. This results
in bogus cache entries. This patch attempts to fix that issue by searching
the transmitter address in the cache and dropping the frame if the address
is unknown.
In client mode ieee80211_find_rxnode() always returns ic->ic_bss.
We can simply validate the transmitter address in the frame against
the AP's MAC adress which is always known.
In monitor mode we can pass such frames since they will be dropped
anyway after bpf_mtap.
Even if the address is correct other parts of the frame might still be
corrupt. I don't think there is a way to verify the frame's checksum if
the hardware cannot do it for us.
I hope that net80211's input path is robust enough to deal with this...
ok?
diff 61810dc72eb09d7c410d5b2c7982a940951e4dd4 /usr/src
blob - 816a3b09ee21111f59b2a22c4a2d236e39272b10
file + sys/dev/ic/ar5008.c
--- sys/dev/ic/ar5008.c
+++ sys/dev/ic/ar5008.c
@@ -970,29 +970,52 @@ ar5008_rx_process(struct athn_softc *sc, struct mbuf_l
/* Finalize mbuf. */
m->m_pkthdr.len = m->m_len = len;
- /* Grab a reference to the source node. */
wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic, wh);
if (michael_mic_failure) {
/*
* Check that it is not a control frame
* (invalid MIC failures on valid ctl frames).
+ * Validate the transmitter's address to avoid passing
+ * corrupt frames with bogus addresses to net80211.
*/
- if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL) &&
- (ic->ic_flags & IEEE80211_F_RSNON) &&
- (ni->ni_rsncipher == IEEE80211_CIPHER_TKIP ||
- ni->ni_rsngroupcipher == IEEE80211_CIPHER_TKIP)) {
- /* Report Michael MIC failures to net80211. */
- ic->ic_stats.is_rx_locmicfail++;
- ieee80211_michael_mic_failure(ic, 0);
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) {
+ switch (ic->ic_opmode) {
+#ifndef IEEE80211_STA_ONLY
+ case IEEE80211_M_HOSTAP:
+ if (ieee80211_find_node(ic, wh->i_addr2))
+ michael_mic_failure = 0;
+ break;
+#endif
+ case IEEE80211_M_STA:
+ if (IEEE80211_ADDR_EQ(wh->i_addr2,
+ ic->ic_bss->ni_macaddr))
+ michael_mic_failure = 0;
+ break;
+ case IEEE80211_M_MONITOR:
+ michael_mic_failure = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (michael_mic_failure) {
+ /* Report Michael MIC failures to net80211. */
+ if ((ic->ic_rsnciphers & IEEE80211_CIPHER_TKIP) ||
+ ic->ic_rsngroupcipher == IEEE80211_CIPHER_TKIP) {
+ ic->ic_stats.is_rx_locmicfail++;
+ ieee80211_michael_mic_failure(ic, 0);
+ }
ifp->if_ierrors++;
- ieee80211_release_node(ic, ni);
m_freem(m);
goto skip;
}
}
+ /* Grab a reference to the source node. */
+ ni = ieee80211_find_rxnode(ic, wh);
+
/* Remove any HW padding after the 802.11 header. */
if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) {
u_int hdrlen = ieee80211_get_hdrlen(wh);