| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- 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;
- };
|