|
@@ -0,0 +1,313 @@
|
|
|
+From: Johannes Berg <[email protected]>
|
|
|
+Date: Tue, 11 May 2021 20:02:47 +0200
|
|
|
+Subject: [PATCH] mac80211: add fragment cache to sta_info
|
|
|
+
|
|
|
+Prior patches protected against fragmentation cache attacks
|
|
|
+by coloring keys, but this shows that it can lead to issues
|
|
|
+when multiple stations use the same sequence number. Add a
|
|
|
+fragment cache to struct sta_info (in addition to the one in
|
|
|
+the interface) to separate fragments for different stations
|
|
|
+properly.
|
|
|
+
|
|
|
+This then automatically clear most of the fragment cache when a
|
|
|
+station disconnects (or reassociates) from an AP, or when client
|
|
|
+interfaces disconnect from the network, etc.
|
|
|
+
|
|
|
+On the way, also fix the comment there since this brings us in line
|
|
|
+with the recommendation in 802.11-2016 ("An AP should support ...").
|
|
|
+Additionally, remove a useless condition (since there's no problem
|
|
|
+purging an already empty list).
|
|
|
+
|
|
|
+Cc: [email protected]
|
|
|
+Signed-off-by: Johannes Berg <[email protected]>
|
|
|
+---
|
|
|
+
|
|
|
+--- a/net/mac80211/ieee80211_i.h
|
|
|
++++ b/net/mac80211/ieee80211_i.h
|
|
|
+@@ -50,12 +50,6 @@ struct ieee80211_local;
|
|
|
+ #define IEEE80211_ENCRYPT_HEADROOM 8
|
|
|
+ #define IEEE80211_ENCRYPT_TAILROOM 18
|
|
|
+
|
|
|
+-/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent
|
|
|
+- * reception of at least three fragmented frames. This limit can be increased
|
|
|
+- * by changing this define, at the cost of slower frame reassembly and
|
|
|
+- * increased memory use (about 2 kB of RAM per entry). */
|
|
|
+-#define IEEE80211_FRAGMENT_MAX 4
|
|
|
+-
|
|
|
+ /* power level hasn't been configured (or set to automatic) */
|
|
|
+ #define IEEE80211_UNSET_POWER_LEVEL INT_MIN
|
|
|
+
|
|
|
+@@ -88,19 +82,6 @@ extern const u8 ieee80211_ac_to_qos_mask
|
|
|
+
|
|
|
+ #define IEEE80211_MAX_NAN_INSTANCE_ID 255
|
|
|
+
|
|
|
+-struct ieee80211_fragment_entry {
|
|
|
+- struct sk_buff_head skb_list;
|
|
|
+- unsigned long first_frag_time;
|
|
|
+- u16 seq;
|
|
|
+- u16 extra_len;
|
|
|
+- u16 last_frag;
|
|
|
+- u8 rx_queue;
|
|
|
+- bool check_sequential_pn; /* needed for CCMP/GCMP */
|
|
|
+- u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
|
|
+- unsigned int key_color;
|
|
|
+-};
|
|
|
+-
|
|
|
+-
|
|
|
+ struct ieee80211_bss {
|
|
|
+ u32 device_ts_beacon, device_ts_presp;
|
|
|
+
|
|
|
+@@ -912,9 +893,7 @@ struct ieee80211_sub_if_data {
|
|
|
+
|
|
|
+ char name[IFNAMSIZ];
|
|
|
+
|
|
|
+- /* Fragment table for host-based reassembly */
|
|
|
+- struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
|
|
|
+- unsigned int fragment_next;
|
|
|
++ struct ieee80211_fragment_cache frags;
|
|
|
+
|
|
|
+ /* TID bitmap for NoAck policy */
|
|
|
+ u16 noack_map;
|
|
|
+@@ -2329,4 +2308,7 @@ u32 ieee80211_calc_expected_tx_airtime(s
|
|
|
+ #define debug_noinline
|
|
|
+ #endif
|
|
|
+
|
|
|
++void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache);
|
|
|
++void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache);
|
|
|
++
|
|
|
+ #endif /* IEEE80211_I_H */
|
|
|
+--- a/net/mac80211/iface.c
|
|
|
++++ b/net/mac80211/iface.c
|
|
|
+@@ -8,7 +8,7 @@
|
|
|
+ * Copyright 2008, Johannes Berg <[email protected]>
|
|
|
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
|
|
|
+ * Copyright (c) 2016 Intel Deutschland GmbH
|
|
|
+- * Copyright (C) 2018-2020 Intel Corporation
|
|
|
++ * Copyright (C) 2018-2021 Intel Corporation
|
|
|
+ */
|
|
|
+ #include <linux/slab.h>
|
|
|
+ #include <linux/kernel.h>
|
|
|
+@@ -679,16 +679,12 @@ static void ieee80211_set_multicast_list
|
|
|
+ */
|
|
|
+ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
|
|
|
+ {
|
|
|
+- int i;
|
|
|
+-
|
|
|
+ /* free extra data */
|
|
|
+ ieee80211_free_keys(sdata, false);
|
|
|
+
|
|
|
+ ieee80211_debugfs_remove_netdev(sdata);
|
|
|
+
|
|
|
+- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
|
|
+- __skb_queue_purge(&sdata->fragments[i].skb_list);
|
|
|
+- sdata->fragment_next = 0;
|
|
|
++ ieee80211_destroy_frag_cache(&sdata->frags);
|
|
|
+
|
|
|
+ if (ieee80211_vif_is_mesh(&sdata->vif))
|
|
|
+ ieee80211_mesh_teardown_sdata(sdata);
|
|
|
+@@ -2038,8 +2034,7 @@ int ieee80211_if_add(struct ieee80211_lo
|
|
|
+ sdata->wdev.wiphy = local->hw.wiphy;
|
|
|
+ sdata->local = local;
|
|
|
+
|
|
|
+- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
|
|
+- skb_queue_head_init(&sdata->fragments[i].skb_list);
|
|
|
++ ieee80211_init_frag_cache(&sdata->frags);
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&sdata->key_list);
|
|
|
+
|
|
|
+--- a/net/mac80211/rx.c
|
|
|
++++ b/net/mac80211/rx.c
|
|
|
+@@ -2133,19 +2133,34 @@ ieee80211_rx_h_decrypt(struct ieee80211_
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
++void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache)
|
|
|
++{
|
|
|
++ int i;
|
|
|
++
|
|
|
++ for (i = 0; i < ARRAY_SIZE(cache->entries); i++)
|
|
|
++ skb_queue_head_init(&cache->entries[i].skb_list);
|
|
|
++}
|
|
|
++
|
|
|
++void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache)
|
|
|
++{
|
|
|
++ int i;
|
|
|
++
|
|
|
++ for (i = 0; i < ARRAY_SIZE(cache->entries); i++)
|
|
|
++ __skb_queue_purge(&cache->entries[i].skb_list);
|
|
|
++}
|
|
|
++
|
|
|
+ static inline struct ieee80211_fragment_entry *
|
|
|
+-ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
|
|
|
++ieee80211_reassemble_add(struct ieee80211_fragment_cache *cache,
|
|
|
+ unsigned int frag, unsigned int seq, int rx_queue,
|
|
|
+ struct sk_buff **skb)
|
|
|
+ {
|
|
|
+ struct ieee80211_fragment_entry *entry;
|
|
|
+
|
|
|
+- entry = &sdata->fragments[sdata->fragment_next++];
|
|
|
+- if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
|
|
|
+- sdata->fragment_next = 0;
|
|
|
++ entry = &cache->entries[cache->next++];
|
|
|
++ if (cache->next >= IEEE80211_FRAGMENT_MAX)
|
|
|
++ cache->next = 0;
|
|
|
+
|
|
|
+- if (!skb_queue_empty(&entry->skb_list))
|
|
|
+- __skb_queue_purge(&entry->skb_list);
|
|
|
++ __skb_queue_purge(&entry->skb_list);
|
|
|
+
|
|
|
+ __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
|
|
|
+ *skb = NULL;
|
|
|
+@@ -2160,14 +2175,14 @@ ieee80211_reassemble_add(struct ieee8021
|
|
|
+ }
|
|
|
+
|
|
|
+ static inline struct ieee80211_fragment_entry *
|
|
|
+-ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
|
|
|
++ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache,
|
|
|
+ unsigned int frag, unsigned int seq,
|
|
|
+ int rx_queue, struct ieee80211_hdr *hdr)
|
|
|
+ {
|
|
|
+ struct ieee80211_fragment_entry *entry;
|
|
|
+ int i, idx;
|
|
|
+
|
|
|
+- idx = sdata->fragment_next;
|
|
|
++ idx = cache->next;
|
|
|
+ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
|
|
|
+ struct ieee80211_hdr *f_hdr;
|
|
|
+ struct sk_buff *f_skb;
|
|
|
+@@ -2176,7 +2191,7 @@ ieee80211_reassemble_find(struct ieee802
|
|
|
+ if (idx < 0)
|
|
|
+ idx = IEEE80211_FRAGMENT_MAX - 1;
|
|
|
+
|
|
|
+- entry = &sdata->fragments[idx];
|
|
|
++ entry = &cache->entries[idx];
|
|
|
+ if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
|
|
|
+ entry->rx_queue != rx_queue ||
|
|
|
+ entry->last_frag + 1 != frag)
|
|
|
+@@ -2217,6 +2232,7 @@ static bool requires_sequential_pn(struc
|
|
|
+ static ieee80211_rx_result debug_noinline
|
|
|
+ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
|
|
+ {
|
|
|
++ struct ieee80211_fragment_cache *cache = &rx->sdata->frags;
|
|
|
+ struct ieee80211_hdr *hdr;
|
|
|
+ u16 sc;
|
|
|
+ __le16 fc;
|
|
|
+@@ -2238,6 +2254,9 @@ ieee80211_rx_h_defragment(struct ieee802
|
|
|
+ goto out_no_led;
|
|
|
+ }
|
|
|
+
|
|
|
++ if (rx->sta)
|
|
|
++ cache = &rx->sta->frags;
|
|
|
++
|
|
|
+ if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+@@ -2256,7 +2275,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
|
|
+
|
|
|
+ if (frag == 0) {
|
|
|
+ /* This is the first fragment of a new frame. */
|
|
|
+- entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
|
|
|
++ entry = ieee80211_reassemble_add(cache, frag, seq,
|
|
|
+ rx->seqno_idx, &(rx->skb));
|
|
|
+ if (requires_sequential_pn(rx, fc)) {
|
|
|
+ int queue = rx->security_idx;
|
|
|
+@@ -2284,7 +2303,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
|
|
+ /* This is a fragment for a frame that should already be pending in
|
|
|
+ * fragment cache. Add this fragment to the end of the pending entry.
|
|
|
+ */
|
|
|
+- entry = ieee80211_reassemble_find(rx->sdata, frag, seq,
|
|
|
++ entry = ieee80211_reassemble_find(cache, frag, seq,
|
|
|
+ rx->seqno_idx, hdr);
|
|
|
+ if (!entry) {
|
|
|
+ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
|
|
|
+--- a/net/mac80211/sta_info.c
|
|
|
++++ b/net/mac80211/sta_info.c
|
|
|
+@@ -4,7 +4,7 @@
|
|
|
+ * Copyright 2006-2007 Jiri Benc <[email protected]>
|
|
|
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
|
|
|
+ * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
|
|
|
+- * Copyright (C) 2018-2020 Intel Corporation
|
|
|
++ * Copyright (C) 2018-2021 Intel Corporation
|
|
|
+ */
|
|
|
+
|
|
|
+ #include <linux/module.h>
|
|
|
+@@ -393,6 +393,8 @@ struct sta_info *sta_info_alloc(struct i
|
|
|
+
|
|
|
+ u64_stats_init(&sta->rx_stats.syncp);
|
|
|
+
|
|
|
++ ieee80211_init_frag_cache(&sta->frags);
|
|
|
++
|
|
|
+ sta->sta_state = IEEE80211_STA_NONE;
|
|
|
+
|
|
|
+ /* Mark TID as unreserved */
|
|
|
+@@ -1103,6 +1105,8 @@ static void __sta_info_destroy_part2(str
|
|
|
+
|
|
|
+ ieee80211_sta_debugfs_remove(sta);
|
|
|
+
|
|
|
++ ieee80211_destroy_frag_cache(&sta->frags);
|
|
|
++
|
|
|
+ cleanup_single_sta(sta);
|
|
|
+ }
|
|
|
+
|
|
|
+--- a/net/mac80211/sta_info.h
|
|
|
++++ b/net/mac80211/sta_info.h
|
|
|
+@@ -3,7 +3,7 @@
|
|
|
+ * Copyright 2002-2005, Devicescape Software, Inc.
|
|
|
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
|
|
|
+ * Copyright(c) 2015-2017 Intel Deutschland GmbH
|
|
|
+- * Copyright(c) 2020 Intel Corporation
|
|
|
++ * Copyright(c) 2020-2021 Intel Corporation
|
|
|
+ */
|
|
|
+
|
|
|
+ #ifndef STA_INFO_H
|
|
|
+@@ -439,6 +439,33 @@ struct ieee80211_sta_rx_stats {
|
|
|
+ };
|
|
|
+
|
|
|
+ /*
|
|
|
++ * IEEE 802.11-2016 (10.6 "Defragmentation") recommends support for "concurrent
|
|
|
++ * reception of at least one MSDU per access category per associated STA"
|
|
|
++ * on APs, or "at least one MSDU per access category" on other interface types.
|
|
|
++ *
|
|
|
++ * This limit can be increased by changing this define, at the cost of slower
|
|
|
++ * frame reassembly and increased memory use while fragments are pending.
|
|
|
++ */
|
|
|
++#define IEEE80211_FRAGMENT_MAX 4
|
|
|
++
|
|
|
++struct ieee80211_fragment_entry {
|
|
|
++ struct sk_buff_head skb_list;
|
|
|
++ unsigned long first_frag_time;
|
|
|
++ u16 seq;
|
|
|
++ u16 extra_len;
|
|
|
++ u16 last_frag;
|
|
|
++ u8 rx_queue;
|
|
|
++ bool check_sequential_pn; /* needed for CCMP/GCMP */
|
|
|
++ u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
|
|
++ unsigned int key_color;
|
|
|
++};
|
|
|
++
|
|
|
++struct ieee80211_fragment_cache {
|
|
|
++ struct ieee80211_fragment_entry entries[IEEE80211_FRAGMENT_MAX];
|
|
|
++ unsigned int next;
|
|
|
++};
|
|
|
++
|
|
|
++/*
|
|
|
+ * The bandwidth threshold below which the per-station CoDel parameters will be
|
|
|
+ * scaled to be more lenient (to prevent starvation of slow stations). This
|
|
|
+ * value will be scaled by the number of active stations when it is being
|
|
|
+@@ -531,6 +558,7 @@ struct ieee80211_sta_rx_stats {
|
|
|
+ * @status_stats.last_ack_signal: last ACK signal
|
|
|
+ * @status_stats.ack_signal_filled: last ACK signal validity
|
|
|
+ * @status_stats.avg_ack_signal: average ACK signal
|
|
|
++ * @frags: fragment cache
|
|
|
+ */
|
|
|
+ struct sta_info {
|
|
|
+ /* General information, mostly static */
|
|
|
+@@ -639,6 +667,8 @@ struct sta_info {
|
|
|
+
|
|
|
+ struct cfg80211_chan_def tdls_chandef;
|
|
|
+
|
|
|
++ struct ieee80211_fragment_cache frags;
|
|
|
++
|
|
|
+ /* keep last! */
|
|
|
+ struct ieee80211_sta sta;
|
|
|
+ };
|