Skip to content

Instantly share code, notes, and snippets.

@codedwrench
Last active July 28, 2020 18:09
Show Gist options
  • Save codedwrench/fa9304a399c7253eba821de0fceea3e5 to your computer and use it in GitHub Desktop.
Save codedwrench/fa9304a399c7253eba821de0fceea3e5 to your computer and use it in GitHub Desktop.
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 0528d4cb4..563770a6e 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1779,6 +1779,9 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw,
wiphy_dbg(hw->wiphy, "%s\n", __func__);
data->rx_filter = 0;
+
+ if (*total_flags & FIF_PROMISC_IN_BSS)
+ data->rx_filter |= FIF_PROMISC_IN_BSS;
if (*total_flags & FIF_ALLMULTI)
data->rx_filter |= FIF_ALLMULTI;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 97fec4d31..3a602e7c1 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3067,6 +3067,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
* stack. It is always safe to pass more frames than requested,
* but this has negative impact on power consumption.
*
+ * @FIF_PROMISC_IN_BSS: promiscuous mode within your BSS,
+ * think of the BSS as your network segment and then this corresponds
+ * to the regular ethernet device promiscuous mode.
+ *
* @FIF_ALLMULTI: pass all multicast frames, this is used if requested
* by the user or if the hardware is not capable of filtering by
* multicast address.
@@ -3083,8 +3087,8 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
* mac80211 needs to do and the amount of CPU wakeups, so you should
* honour this flag if possible.
*
- * @FIF_CONTROL: pass control frames (except for PS Poll) addressed to this
- * station
+ * @FIF_CONTROL: pass control frames (except for PS Poll), if PROMISC_IN_BSS
+ * is not set then only those addressed to this station.
*
* @FIF_OTHER_BSS: pass frames destined to other BSSes
*
@@ -3093,6 +3097,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
* @FIF_PROBE_REQ: pass probe request frames
*/
enum ieee80211_filter_flags {
+ FIF_PROMISC_IN_BSS = 1<<0,
FIF_ALLMULTI = 1<<1,
FIF_FCSFAIL = 1<<2,
FIF_PLCPFAIL = 1<<3,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f8ed4f621..d751e21bf 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -187,6 +187,9 @@ typedef unsigned __bitwise ieee80211_rx_result;
/**
* enum ieee80211_packet_rx_flags - packet RX flags
+ * @IEEE80211_RX_RA_MATCH: frame is destined to interface currently processed
+ * (incl. multicast frames)
+ * @IEEE80211_RX_FRAGMENTED: fragmented frame
* @IEEE80211_RX_AMSDU: a-MSDU packet
* @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
* @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering
@@ -195,6 +198,8 @@ typedef unsigned __bitwise ieee80211_rx_result;
* @rx_flags field of &struct ieee80211_rx_status.
*/
enum ieee80211_packet_rx_flags {
+ IEEE80211_RX_RA_MATCH = BIT(1),
+ IEEE80211_RX_FRAGMENTED = BIT(2),
IEEE80211_RX_AMSDU = BIT(3),
IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4),
IEEE80211_RX_DEFERRED_RELEASE = BIT(5),
@@ -732,6 +737,7 @@ struct ieee80211_if_mesh {
* enum ieee80211_sub_if_data_flags - virtual interface flags
*
* @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets
+ * @IEEE80211_SDATA_PROMISC: interface is promisc
* @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode
* @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between
* associated stations and deliver multicast frames both
@@ -741,6 +747,7 @@ struct ieee80211_if_mesh {
*/
enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_ALLMULTI = BIT(0),
+ IEEE80211_SDATA_PROMISC = BIT(1),
IEEE80211_SDATA_OPERATING_GMODE = BIT(2),
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
@@ -1258,8 +1265,8 @@ struct ieee80211_local {
atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES];
- /* number of interfaces with allmulti RX */
- atomic_t iff_allmultis;
+ /* number of interfaces with corresponding IFF_ flags */
+ atomic_t iff_allmultis, iff_promiscs;
struct rate_control_ref *rate_ctrl;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index d06982570..a49d5fb71 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -720,6 +720,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
*/
if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
atomic_inc(&local->iff_allmultis);
+
+ if (sdata->flags & IEEE80211_SDATA_PROMISC)
+ atomic_inc(&local->iff_promiscs);
if (coming_up)
local->open_count++;
@@ -855,10 +858,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
(sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)));
- /* don't count this interface for allmulti while it is down */
+ /* don't count this interface for promisc/allmulti while it is down */
if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
atomic_dec(&local->iff_allmultis);
+ if (sdata->flags & IEEE80211_SDATA_PROMISC)
+ atomic_dec(&local->iff_promiscs);
+
if (sdata->vif.type == NL80211_IFTYPE_AP) {
local->fif_pspoll--;
local->fif_probe_req--;
@@ -1082,10 +1088,12 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- int allmulti, sdata_allmulti;
+ int allmulti, promisc, sdata_allmulti, sdata_promisc;
allmulti = !!(dev->flags & IFF_ALLMULTI);
+ promisc = !!(dev->flags & IFF_PROMISC);
sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
+ sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC);
if (allmulti != sdata_allmulti) {
if (dev->flags & IFF_ALLMULTI)
@@ -1094,7 +1102,14 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
atomic_dec(&local->iff_allmultis);
sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
}
-
+
+ if (promisc != sdata_promisc) {
+ if (dev->flags & IFF_PROMISC)
+ atomic_inc(&local->iff_promiscs);
+ else
+ atomic_dec(&local->iff_promiscs);
+ sdata->flags ^= IEEE80211_SDATA_PROMISC;
+ }
spin_lock_bh(&local->filter_lock);
__hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
spin_unlock_bh(&local->filter_lock);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 6423173bb..0798926aa 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -39,6 +39,9 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
unsigned int changed_flags;
unsigned int new_flags = 0;
+ if (atomic_read(&local->iff_promiscs))
+ new_flags |= FIF_PROMISC_IN_BSS;
+
if (atomic_read(&local->iff_allmultis))
new_flags |= FIF_ALLMULTI;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 91a13aee4..29f395000 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1363,6 +1363,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
struct sk_buff *skb = rx->skb;
struct ieee80211_local *local = rx->local;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct sta_info *sta = rx->sta;
struct tid_ampdu_rx *tid_agg_rx;
u16 sc;
@@ -1371,6 +1372,12 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
if (!ieee80211_is_data_qos(hdr->frame_control) ||
is_multicast_ether_addr(hdr->addr1))
goto dont_reorder;
+
+ /* not actually part of this BA session */
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ goto dont_reorder;
+
+
/*
* filter the QoS data rx stream according to
@@ -1460,8 +1467,10 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) {
+ if (status->rx_flags & IEEE80211_RX_RA_MATCH) {
I802_DEBUG_INC(rx->local->dot11FrameDuplicateCount);
rx->sta->rx_stats.num_duplicates++;
+ }
return RX_DROP_UNUSABLE;
} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
@@ -1693,7 +1702,7 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx)
struct ieee80211_hdr *hdr = (void *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
- if (!rx->sta)
+ if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
if (sdata->vif.type != NL80211_IFTYPE_AP &&
@@ -1779,6 +1788,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control))
sta->rx_stats.last_rate = sta_stats_encode_rate(status);
}
+
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ return RX_CONTINUE;
if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_sta_rx_notify(rx->sdata, hdr);
@@ -1945,6 +1957,13 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
* every station but there's no such requirement; VLANs could be
* possible.
*/
+
+ /*
+ * No point in finding a key and decrypting if the frame is neither
+ * addressed to us nor a multicast frame.
+ */
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ return RX_CONTINUE;
/* start without a key */
rx->key = NULL;
@@ -2527,6 +2546,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
struct sk_buff *skb, *xmit_skb;
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
struct sta_info *dsta;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
skb = rx->skb;
xmit_skb = NULL;
@@ -2547,6 +2567,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
+ (status->rx_flags & IEEE80211_RX_RA_MATCH) &&
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) {
if (is_multicast_ether_addr(ehdr->h_dest) &&
ieee80211_vif_get_num_mcast_if(sdata) != 0) {
@@ -2726,6 +2747,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb, *fwd_skb;
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u16 ac, q, hdrlen;
int tailroom = 0;
@@ -2757,7 +2779,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr))
return RX_DROP_MONITOR;
- if (!ieee80211_is_data(hdr->frame_control))
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ !(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
if (!mesh_hdr->ttl)
@@ -2855,9 +2878,11 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
ieee80211_add_pending_skb(local, fwd_skb);
out:
- if (is_multicast_ether_addr(hdr->addr1))
+ if (is_multicast_ether_addr(hdr->addr1) ||
+ sdata->dev->flags & IFF_PROMISC)
return RX_CONTINUE;
- return RX_DROP_MONITOR;
+ else
+ return RX_DROP_MONITOR;
}
#endif
@@ -3087,6 +3112,9 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
status->freq, sig);
rx->flags |= IEEE80211_RX_BEACON_REPORTED;
}
+
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ return RX_DROP_MONITOR;
if (ieee80211_drop_unencrypted_mgmt(rx))
return RX_DROP_UNUSABLE;
@@ -3114,6 +3142,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED &&
mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
return RX_DROP_UNUSABLE;
+
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ return RX_DROP_UNUSABLE;
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_HT:
@@ -3895,6 +3926,15 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
case NL80211_IFTYPE_STATION:
if (!bssid && !sdata->u.mgd.use_4addr)
return false;
+
+ if (!multicast &&
+ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
+ if (!(sdata->dev->flags & IFF_PROMISC) ||
+ sdata->u.mgd.use_4addr)
+ return false;
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+ }
+
if (ieee80211_is_robust_mgmt_frame(skb) && !rx->sta)
return false;
if (multicast)
@@ -3911,8 +3951,11 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid))
return false;
if (!multicast &&
- !ether_addr_equal(sdata->vif.addr, hdr->addr1))
- return false;
+ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return false;
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+ }
if (!rx->sta) {
int rate_idx;
if (status->encoding != RX_ENC_LEGACY)
@@ -3931,8 +3974,15 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
if (!is_broadcast_ether_addr(bssid))
return false;
if (!multicast &&
- !ether_addr_equal(sdata->dev->dev_addr, hdr->addr1))
- return false;
+ !ether_addr_equal(sdata->dev->dev_addr, hdr->addr1))
+ {
+ /* if we are in promisc mode we also accept
+ * packets not destined for us
+ */
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return false;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
+ }
if (!rx->sta) {
int rate_idx;
if (status->encoding != RX_ENC_LEGACY)
@@ -3946,6 +3996,13 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
case NL80211_IFTYPE_MESH_POINT:
if (ether_addr_equal(sdata->vif.addr, hdr->addr2))
return false;
+ if (!multicast &&
+ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return false;
+
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+ }
if (multicast)
return true;
return ether_addr_equal(sdata->vif.addr, hdr->addr1);
@@ -3962,10 +4019,17 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
* itself never looks at these frames.
*/
if (!multicast &&
- !ether_addr_equal(sdata->vif.addr, hdr->addr1))
+ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
+ if (!(sdata->dev->flags & IFF_PROMISC) ||
+ sdata->u.mgd.use_4addr)
return false;
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+ }
if (ieee80211_is_public_action(hdr, skb->len))
return true;
+ if (!ieee80211_is_beacon(hdr->frame_control))
+ return false;
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
return ieee80211_is_beacon(hdr->frame_control);
}
@@ -4006,10 +4070,15 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
return false;
return ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2);
case NL80211_IFTYPE_P2P_DEVICE:
- return ieee80211_is_public_action(hdr, skb->len) ||
- ieee80211_is_probe_req(hdr->frame_control) ||
- ieee80211_is_probe_resp(hdr->frame_control) ||
- ieee80211_is_beacon(hdr->frame_control);
+ if (!ieee80211_is_public_action(hdr, skb->len) &&
+ !ieee80211_is_probe_req(hdr->frame_control) &&
+ !ieee80211_is_probe_resp(hdr->frame_control) &&
+ !ieee80211_is_beacon(hdr->frame_control))
+ return false;
+ if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) &&
+ !multicast)
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+ return true;;
case NL80211_IFTYPE_NAN:
/* Currently no frames on NAN interface are allowed */
return false;
@@ -4408,8 +4477,10 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
{
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
rx->skb = skb;
+ status->rx_flags |= IEEE80211_RX_RA_MATCH;
/* See if we can do fast-rx; if we have to copy we already lost,
* so punt in that case. We should never have to deliver a data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment