net80211: validate control frame TA/RA before further processing
An earlier commit relaxed the TA/RA rules around control frames to fix other issues, however it now results in control frames not specifically from a known node / to us to be handled in the control path. Specifically, rtwn(4) RTL8812/RTL8821 NICs are currently passing BARs from the AP TA to any destination to us; which is tripping up BAW tracking and causing traffic hangs. So do the check before vap->iv_recv_ctl() is called in each input path. Note that mesh doesn't seem to pass the control frames up; however I haven't tested/validated mesh in a long while and I know it's currently broken. Differential Revision: https://reviews.freebsd.org/D49575
This commit is contained in:
@@ -2732,3 +2732,57 @@ ieee80211_is_key_unicast(const struct ieee80211vap *vap,
|
||||
*/
|
||||
return (!ieee80211_is_key_global(vap, key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given control frame is from a known node
|
||||
* and destined to us.
|
||||
*
|
||||
* In some instances a control frame won't have a TA (eg ACKs), so
|
||||
* we should only verify the RA for those.
|
||||
*
|
||||
* @param ni ieee80211_node representing the sender, or BSS node
|
||||
* @param m0 mbuf representing the 802.11 frame.
|
||||
* @returns false if the frame is not a CTL frame (with a warning logged);
|
||||
* true if the frame is from a known sender / valid recipient,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool
|
||||
ieee80211_is_ctl_frame_for_vap(struct ieee80211_node *ni, const struct mbuf *m0)
|
||||
{
|
||||
const struct ieee80211vap *vap = ni->ni_vap;
|
||||
const struct ieee80211_frame *wh;
|
||||
uint8_t subtype;
|
||||
|
||||
wh = mtod(m0, const struct ieee80211_frame *);
|
||||
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
|
||||
|
||||
/* Verify it's a ctl frame. */
|
||||
KASSERT(IEEE80211_IS_CTL(wh), ("%s: not a CTL frame (fc[0]=0x%04x)",
|
||||
__func__, wh->i_fc[0]));
|
||||
if (!IEEE80211_IS_CTL(wh)) {
|
||||
if_printf(vap->iv_ifp,
|
||||
"%s: not a control frame (fc[0]=0x%04x)\n",
|
||||
__func__, wh->i_fc[0]);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* Verify the TA if present. */
|
||||
switch (subtype) {
|
||||
case IEEE80211_FC0_SUBTYPE_CTS:
|
||||
case IEEE80211_FC0_SUBTYPE_ACK:
|
||||
/* No TA. */
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Verify TA matches ni->ni_macaddr; for unknown
|
||||
* sources it will be the BSS node and ni->ni_macaddr
|
||||
* will the BSS MAC.
|
||||
*/
|
||||
if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr))
|
||||
return (false);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Verify the RA */
|
||||
return (IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr));
|
||||
}
|
||||
|
||||
@@ -661,7 +661,8 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
|
||||
case IEEE80211_FC0_TYPE_CTL:
|
||||
vap->iv_stats.is_rx_ctl++;
|
||||
IEEE80211_NODE_STAT(ni, rx_ctrl);
|
||||
vap->iv_recv_ctl(ni, m, subtype);
|
||||
if (ieee80211_is_ctl_frame_for_vap(ni, m))
|
||||
vap->iv_recv_ctl(ni, m, subtype);
|
||||
goto out;
|
||||
|
||||
default:
|
||||
|
||||
@@ -889,7 +889,8 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m,
|
||||
case IEEE80211_FC0_TYPE_CTL:
|
||||
vap->iv_stats.is_rx_ctl++;
|
||||
IEEE80211_NODE_STAT(ni, rx_ctrl);
|
||||
vap->iv_recv_ctl(ni, m, subtype);
|
||||
if (ieee80211_is_ctl_frame_for_vap(ni, m))
|
||||
vap->iv_recv_ctl(ni, m, subtype);
|
||||
goto out;
|
||||
default:
|
||||
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
|
||||
|
||||
@@ -972,7 +972,8 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
|
||||
case IEEE80211_FC0_TYPE_CTL:
|
||||
vap->iv_stats.is_rx_ctl++;
|
||||
IEEE80211_NODE_STAT(ni, rx_ctrl);
|
||||
vap->iv_recv_ctl(ni, m, subtype);
|
||||
if (ieee80211_is_ctl_frame_for_vap(ni, m))
|
||||
vap->iv_recv_ctl(ni, m, subtype);
|
||||
goto out;
|
||||
|
||||
default:
|
||||
@@ -2054,6 +2055,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
|
||||
static void
|
||||
sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
|
||||
{
|
||||
|
||||
switch (subtype) {
|
||||
case IEEE80211_FC0_SUBTYPE_BAR:
|
||||
ieee80211_recv_bar(ni, m);
|
||||
|
||||
@@ -845,6 +845,9 @@ bool ieee80211_is_key_global(const struct ieee80211vap *vap,
|
||||
bool ieee80211_is_key_unicast(const struct ieee80211vap *vap,
|
||||
const struct ieee80211_key *key);
|
||||
|
||||
bool ieee80211_is_ctl_frame_for_vap(struct ieee80211_node *,
|
||||
const struct mbuf *);
|
||||
|
||||
void ieee80211_radiotap_attach(struct ieee80211com *,
|
||||
struct ieee80211_radiotap_header *th, int tlen,
|
||||
uint32_t tx_radiotap,
|
||||
|
||||
Reference in New Issue
Block a user