mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
Core & protocols
----------------
- Support HW queue leasing, allowing containers to be granted access
to HW queues for zero-copy operations and AF_XDP.
- Number of code moves to help the compiler with inlining.
Avoid output arguments for returning drop reason where possible.
- Rework drop handling within qdiscs to include more metadata
about the reason and dropping qdisc in the tracepoints.
- Remove the rtnl_lock use from IP Multicast Routing.
- Pack size information into the Rx Flow Steering table pointer
itself. This allows making the table itself a flat array of u32s,
thus making the table allocation size a power of two.
- Report TCP delayed ack timer information via socket diag.
- Add ip_local_port_step_width sysctl to allow distributing the randomly
selected ports more evenly throughout the allowed space.
- Add support for per-route tunsrc in IPv6 segment routing.
- Start work of switching sockopt handling to iov_iter.
- Improve dynamic recvbuf sizing in MPTCP, limit burstiness and avoid
buffer size drifting up.
- Support MSG_EOR in MPTCP.
- Add stp_mode attribute to the bridge driver for STP mode selection.
This addresses concerns about call_usermodehelper() usage.
- Remove UDP-Lite support (as announced in 2023).
- Remove support for building IPv6 as a module.
Remove the now unnecessary function calling indirection.
Cross-tree stuff
----------------
- Move Michael MIC code from generic crypto into wireless,
it's considered insecure but some WiFi networks still need it.
Netfilter
---------
- Switch nft_fib_ipv6 module to no longer need temporary dst_entry
object allocations by using fib6_lookup() + RCU.
Florian W reports this gets us ~13% higher packet rate.
- Convert IPVS's global __ip_vs_mutex to per-net service_mutex and
switch the service tables to be per-net. Convert some code that
walks the service lists to use RCU instead of the service_mutex.
- Add more opinionated input validation to lower security exposure.
- Make IPVS hash tables to be per-netns and resizable.
Wireless
--------
- Finished assoc frame encryption/EPPKE/802.1X-over-auth.
- Radar detection improvements.
- Add 6 GHz incumbent signal detection APIs.
- Multi-link support for FILS, probe response templates and
client probing.
- New APIs and mac80211 support for NAN (Neighbor Aware Networking,
aka Wi-Fi Aware) so less work must be in firmware.
Driver API
----------
- Add numerical ID for devlink instances (to avoid having to create
fake bus/device pairs just to have an ID). Support shared devlink
instances which span multiple PFs.
- Add standard counters for reporting pause storm events
(implement in mlx5 and fbnic).
- Add configuration API for completion writeback buffering
(implement in mana).
- Support driver-initiated change of RSS context sizes.
- Support DPLL monitoring input frequency (implement in zl3073x).
- Support per-port resources in devlink (implement in mlx5).
Misc
----
- Expand the YAML spec for Netfilter.
Drivers
-------
- Software:
- macvlan: support multicast rx for bridge ports with shared source
MAC address
- team: decouple receive and transmit enablement for IEEE 802.3ad
LACP "independent control"
- Ethernet high-speed NICs:
- nVidia/Mellanox:
- support high order pages in zero-copy mode (for payload
coalescing)
- support multiple packets in a page (for systems with 64kB pages)
- Broadcom 25-400GE (bnxt):
- implement XDP RSS hash metadata extraction
- add software fallback for UDP GSO, lowering the IOMMU cost
- Broadcom 800GE (bnge):
- add link status and configuration handling
- add various HW and SW statistics
- Marvell/Cavium:
- NPC HW block support for cn20k
- Huawei (hinic3):
- add mailbox / control queue
- add rx VLAN offload
- add driver info and link management
- Ethernet NICs:
- Marvell/Aquantia:
- support reading SFP module info on some AQC100 cards
- Realtek PCI (r8169):
- add support for RTL8125cp
- Realtek USB (r8152):
- support for the RTL8157 5Gbit chip
- add 2500baseT EEE status/configuration support
- Ethernet NICs embedded and off-the-shelf IP:
- Synopsys (stmmac):
- cleanup and reorganize SerDes handling and PCS support
- cleanup descriptor handling and per-platform data
- cleanup and consolidate MDIO defines and handling
- shrink driver memory use for internal structures
- improve Tx IRQ coalescing
- improve TCP segmentation handling
- add support for Spacemit K3
- Cadence (macb):
- support PHYs that have inband autoneg disabled with GEM
- support IEEE 802.3az EEE
- rework usrio capabilities and handling
- AMD (xgbe):
- improve power management for S0i3
- improve TX resilience for link-down handling
- Virtual:
- Google cloud vNIC:
- support larger ring sizes in DQO-QPL mode
- improve HW-GRO handling
- support UDP GSO for DQO format
- PCIe NTB:
- support queue count configuration
- Ethernet PHYs:
- automatically disable PHY autonomous EEE if MAC is in charge
- Broadcom:
- add BCM84891/BCM84892 support
- Micrel:
- support for LAN9645X internal PHY
- Realtek:
- add RTL8224 pair order support
- support PHY LEDs on RTL8211F-VD
- support spread spectrum clocking (SSC)
- Maxlinear:
- add PHY-level statistics via ethtool
- Ethernet switches:
- Maxlinear (mxl862xx):
- support for bridge offloading
- support for VLANs
- support driver statistics
- Bluetooth:
- large number of fixes and new device IDs
- Mediatek:
- support MT6639 (MT7927)
- support MT7902 SDIO
- WiFi:
- Intel (iwlwifi):
- UNII-9 and continuing UHR work
- MediaTek (mt76):
- mt7996/mt7925 MLO fixes/improvements
- mt7996 NPU support (HW eth/wifi traffic offload)
- Qualcomm (ath12k):
- monitor mode support on IPQ5332
- basic hwmon temperature reporting
- support IPQ5424
- Realtek:
- add USB RX aggregation to improve performance
- add USB TX flow control by tracking in-flight URBs
- Cellular:
- IPA v5.2 support
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAmnelNoACgkQMUZtbf5S
IrtWFw//WyiXuEiGawVQONnbu1dtR+3nw/cvNpSYi0IM66vbRUB9n+9fxm2MIyG4
4jI/c/X/fxIvUxEqGez3yPn5P7KqkQR8WRYwkxrMYKRpXeukN0IDk5Euew5DskCe
wtBKNJOQWKdKXff0bLQoJ9dHWYuJ2IMRVil5M3fhUbeUOXeyJD7Yn1w2ICvJAbj+
T/Hw7sEtchNaHp6h6SbaQfahkUFHQG5peNoETkZF4UDF6ALGY29WH91GXeO2lrgN
IxX203KtaavV0oU8T0oixZgOc57Ns081YfFL/F1JP2HV6lgkwhuq+zxCrRTi1c9M
HPTXgwD7Z80Y74nM3YTLrPfoMOP8GLBZgdV3rUpwmteM26+gMTm+O1zHUur5ZoGy
D6TaMFguPTIqiRyrARa9xY/J6r9TQkc2Wfu4bIuPndKFg8xPoepuEObODnh0+5Hg
4j4pdFhIo2huENhSg7kVb/yl+1q68SFwM3RqTmx+OhCa0AyjcKIKgt/UBhismdnG
r8obxzb+nXeJc2rRDuwNMwlBlcMSbep27uGt64zeHMMXVhTVqOoytNaL/X/ZpH2m
A0DscUrpHvb36IoDPtanc6irP+JOh5Xe7Nw5qhkgwsMc7hlf8SyyHB4OUBBaz1qA
ETSnHlfwklRmXSpWqH2LyGXjdOQpDKP46+h0W3dttMD2/cRBqYo=
=EhQZ
-----END PGP SIGNATURE-----
Merge tag 'net-next-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from Jakub Kicinski:
"Core & protocols:
- Support HW queue leasing, allowing containers to be granted access
to HW queues for zero-copy operations and AF_XDP
- Number of code moves to help the compiler with inlining. Avoid
output arguments for returning drop reason where possible
- Rework drop handling within qdiscs to include more metadata about
the reason and dropping qdisc in the tracepoints
- Remove the rtnl_lock use from IP Multicast Routing
- Pack size information into the Rx Flow Steering table pointer
itself. This allows making the table itself a flat array of u32s,
thus making the table allocation size a power of two
- Report TCP delayed ack timer information via socket diag
- Add ip_local_port_step_width sysctl to allow distributing the
randomly selected ports more evenly throughout the allowed space
- Add support for per-route tunsrc in IPv6 segment routing
- Start work of switching sockopt handling to iov_iter
- Improve dynamic recvbuf sizing in MPTCP, limit burstiness and avoid
buffer size drifting up
- Support MSG_EOR in MPTCP
- Add stp_mode attribute to the bridge driver for STP mode selection.
This addresses concerns about call_usermodehelper() usage
- Remove UDP-Lite support (as announced in 2023)
- Remove support for building IPv6 as a module. Remove the now
unnecessary function calling indirection
Cross-tree stuff:
- Move Michael MIC code from generic crypto into wireless, it's
considered insecure but some WiFi networks still need it
Netfilter:
- Switch nft_fib_ipv6 module to no longer need temporary dst_entry
object allocations by using fib6_lookup() + RCU.
Florian W reports this gets us ~13% higher packet rate
- Convert IPVS's global __ip_vs_mutex to per-net service_mutex and
switch the service tables to be per-net. Convert some code that
walks the service lists to use RCU instead of the service_mutex
- Add more opinionated input validation to lower security exposure
- Make IPVS hash tables to be per-netns and resizable
Wireless:
- Finished assoc frame encryption/EPPKE/802.1X-over-auth
- Radar detection improvements
- Add 6 GHz incumbent signal detection APIs
- Multi-link support for FILS, probe response templates and client
probing
- New APIs and mac80211 support for NAN (Neighbor Aware Networking,
aka Wi-Fi Aware) so less work must be in firmware
Driver API:
- Add numerical ID for devlink instances (to avoid having to create
fake bus/device pairs just to have an ID). Support shared devlink
instances which span multiple PFs
- Add standard counters for reporting pause storm events (implement
in mlx5 and fbnic)
- Add configuration API for completion writeback buffering (implement
in mana)
- Support driver-initiated change of RSS context sizes
- Support DPLL monitoring input frequency (implement in zl3073x)
- Support per-port resources in devlink (implement in mlx5)
Misc:
- Expand the YAML spec for Netfilter
Drivers
- Software:
- macvlan: support multicast rx for bridge ports with shared
source MAC address
- team: decouple receive and transmit enablement for IEEE 802.3ad
LACP "independent control"
- Ethernet high-speed NICs:
- nVidia/Mellanox:
- support high order pages in zero-copy mode (for payload
coalescing)
- support multiple packets in a page (for systems with 64kB
pages)
- Broadcom 25-400GE (bnxt):
- implement XDP RSS hash metadata extraction
- add software fallback for UDP GSO, lowering the IOMMU cost
- Broadcom 800GE (bnge):
- add link status and configuration handling
- add various HW and SW statistics
- Marvell/Cavium:
- NPC HW block support for cn20k
- Huawei (hinic3):
- add mailbox / control queue
- add rx VLAN offload
- add driver info and link management
- Ethernet NICs:
- Marvell/Aquantia:
- support reading SFP module info on some AQC100 cards
- Realtek PCI (r8169):
- add support for RTL8125cp
- Realtek USB (r8152):
- support for the RTL8157 5Gbit chip
- add 2500baseT EEE status/configuration support
- Ethernet NICs embedded and off-the-shelf IP:
- Synopsys (stmmac):
- cleanup and reorganize SerDes handling and PCS support
- cleanup descriptor handling and per-platform data
- cleanup and consolidate MDIO defines and handling
- shrink driver memory use for internal structures
- improve Tx IRQ coalescing
- improve TCP segmentation handling
- add support for Spacemit K3
- Cadence (macb):
- support PHYs that have inband autoneg disabled with GEM
- support IEEE 802.3az EEE
- rework usrio capabilities and handling
- AMD (xgbe):
- improve power management for S0i3
- improve TX resilience for link-down handling
- Virtual:
- Google cloud vNIC:
- support larger ring sizes in DQO-QPL mode
- improve HW-GRO handling
- support UDP GSO for DQO format
- PCIe NTB:
- support queue count configuration
- Ethernet PHYs:
- automatically disable PHY autonomous EEE if MAC is in charge
- Broadcom:
- add BCM84891/BCM84892 support
- Micrel:
- support for LAN9645X internal PHY
- Realtek:
- add RTL8224 pair order support
- support PHY LEDs on RTL8211F-VD
- support spread spectrum clocking (SSC)
- Maxlinear:
- add PHY-level statistics via ethtool
- Ethernet switches:
- Maxlinear (mxl862xx):
- support for bridge offloading
- support for VLANs
- support driver statistics
- Bluetooth:
- large number of fixes and new device IDs
- Mediatek:
- support MT6639 (MT7927)
- support MT7902 SDIO
- WiFi:
- Intel (iwlwifi):
- UNII-9 and continuing UHR work
- MediaTek (mt76):
- mt7996/mt7925 MLO fixes/improvements
- mt7996 NPU support (HW eth/wifi traffic offload)
- Qualcomm (ath12k):
- monitor mode support on IPQ5332
- basic hwmon temperature reporting
- support IPQ5424
- Realtek:
- add USB RX aggregation to improve performance
- add USB TX flow control by tracking in-flight URBs
- Cellular:
- IPA v5.2 support"
* tag 'net-next-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1561 commits)
net: pse-pd: fix kernel-doc function name for pse_control_find_by_id()
wireguard: device: use exit_rtnl callback instead of manual rtnl_lock in pre_exit
wireguard: allowedips: remove redundant space
tools: ynl: add sample for wireguard
wireguard: allowedips: Use kfree_rcu() instead of call_rcu()
MAINTAINERS: Add netkit selftest files
selftests/net: Add additional test coverage in nk_qlease
selftests/net: Split netdevsim tests from HW tests in nk_qlease
tools/ynl: Make YnlFamily closeable as a context manager
net: airoha: Add missing PPE configurations in airoha_ppe_hw_init()
net: airoha: Fix VIP configuration for AN7583 SoC
net: caif: clear client service pointer on teardown
net: strparser: fix skb_head leak in strp_abort_strp()
net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()
selftests/bpf: add test for xdp_master_redirect with bond not up
net, bpf: fix null-ptr-deref in xdp_master_redirect() for down master
net: airoha: Remove PCE_MC_EN_MASK bit in REG_FE_PCE_CFG configuration
sctp: disable BH before calling udp_tunnel_xmit_skb()
sctp: fix missing encap_port propagation for GSO fragments
net: airoha: Rely on net_device pointer in ETS callbacks
...
1076 lines
28 KiB
C
1076 lines
28 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright 2002-2004, Instant802 Networks, Inc.
|
|
* Copyright 2008, Jouni Malinen <j@w1.fi>
|
|
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
|
* Copyright (C) 2020-2023 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/types.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/ieee80211.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/unaligned.h>
|
|
#include <net/mac80211.h>
|
|
#include <crypto/aes.h>
|
|
#include <crypto/utils.h>
|
|
|
|
#include "ieee80211_i.h"
|
|
#include "tkip.h"
|
|
#include "aes_ccm.h"
|
|
#include "aes_cmac.h"
|
|
#include "aes_gmac.h"
|
|
#include "aes_gcm.h"
|
|
#include "wpa.h"
|
|
|
|
ieee80211_tx_result
|
|
ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
|
|
{
|
|
u8 *data, *key, *mic;
|
|
size_t data_len;
|
|
unsigned int hdrlen;
|
|
struct ieee80211_hdr *hdr;
|
|
struct sk_buff *skb = tx->skb;
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
int tail;
|
|
|
|
hdr = (struct ieee80211_hdr *)skb->data;
|
|
if (!tx->key || tx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
|
|
skb->len < 24 || !ieee80211_is_data_present(hdr->frame_control))
|
|
return TX_CONTINUE;
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
if (skb->len < hdrlen)
|
|
return TX_DROP;
|
|
|
|
data = skb->data + hdrlen;
|
|
data_len = skb->len - hdrlen;
|
|
|
|
if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) {
|
|
/* Need to use software crypto for the test */
|
|
info->control.hw_key = NULL;
|
|
}
|
|
|
|
if (info->control.hw_key &&
|
|
(info->flags & IEEE80211_TX_CTL_DONTFRAG ||
|
|
ieee80211_hw_check(&tx->local->hw, SUPPORTS_TX_FRAG)) &&
|
|
!(tx->key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
|
|
IEEE80211_KEY_FLAG_PUT_MIC_SPACE))) {
|
|
/* hwaccel - with no need for SW-generated MMIC or MIC space */
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
tail = MICHAEL_MIC_LEN;
|
|
if (!info->control.hw_key)
|
|
tail += IEEE80211_TKIP_ICV_LEN;
|
|
|
|
if (WARN(skb_tailroom(skb) < tail ||
|
|
skb_headroom(skb) < IEEE80211_TKIP_IV_LEN,
|
|
"mmic: not enough head/tail (%d/%d,%d/%d)\n",
|
|
skb_headroom(skb), IEEE80211_TKIP_IV_LEN,
|
|
skb_tailroom(skb), tail))
|
|
return TX_DROP;
|
|
|
|
mic = skb_put(skb, MICHAEL_MIC_LEN);
|
|
|
|
if (tx->key->conf.flags & IEEE80211_KEY_FLAG_PUT_MIC_SPACE) {
|
|
/* Zeroed MIC can help with debug */
|
|
memset(mic, 0, MICHAEL_MIC_LEN);
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
|
|
michael_mic(key, hdr, data, data_len, mic);
|
|
if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE))
|
|
mic[0]++;
|
|
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
|
|
ieee80211_rx_result
|
|
ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
|
|
{
|
|
u8 *data, *key = NULL;
|
|
size_t data_len;
|
|
unsigned int hdrlen;
|
|
u8 mic[MICHAEL_MIC_LEN];
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
|
|
/*
|
|
* it makes no sense to check for MIC errors on anything other
|
|
* than data frames.
|
|
*/
|
|
if (!ieee80211_is_data_present(hdr->frame_control))
|
|
return RX_CONTINUE;
|
|
|
|
/*
|
|
* No way to verify the MIC if the hardware stripped it or
|
|
* the IV with the key index. In this case we have solely rely
|
|
* on the driver to set RX_FLAG_MMIC_ERROR in the event of a
|
|
* MIC failure report.
|
|
*/
|
|
if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
|
|
if (status->flag & RX_FLAG_MMIC_ERROR)
|
|
goto mic_fail_no_key;
|
|
|
|
if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key &&
|
|
rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
|
|
goto update_iv;
|
|
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* Some hardware seems to generate Michael MIC failure reports; even
|
|
* though, the frame was not encrypted with TKIP and therefore has no
|
|
* MIC. Ignore the flag them to avoid triggering countermeasures.
|
|
*/
|
|
if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
|
|
!(status->flag & RX_FLAG_DECRYPTED))
|
|
return RX_CONTINUE;
|
|
|
|
if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) {
|
|
/*
|
|
* APs with pairwise keys should never receive Michael MIC
|
|
* errors for non-zero keyidx because these are reserved for
|
|
* group keys and only the AP is sending real multicast
|
|
* frames in the BSS.
|
|
*/
|
|
return RX_DROP_U_AP_RX_GROUPCAST;
|
|
}
|
|
|
|
if (status->flag & RX_FLAG_MMIC_ERROR)
|
|
goto mic_fail;
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
if (skb->len < hdrlen + MICHAEL_MIC_LEN)
|
|
return RX_DROP_U_SHORT_MMIC;
|
|
|
|
if (skb_linearize(rx->skb))
|
|
return RX_DROP_U_OOM;
|
|
hdr = (void *)skb->data;
|
|
|
|
data = skb->data + hdrlen;
|
|
data_len = skb->len - hdrlen - MICHAEL_MIC_LEN;
|
|
key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
|
|
michael_mic(key, hdr, data, data_len, mic);
|
|
if (crypto_memneq(mic, data + data_len, MICHAEL_MIC_LEN))
|
|
goto mic_fail;
|
|
|
|
/* remove Michael MIC from payload */
|
|
skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
|
|
|
|
update_iv:
|
|
/* update IV in key information to be able to detect replays */
|
|
rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip.iv32;
|
|
rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip.iv16;
|
|
|
|
return RX_CONTINUE;
|
|
|
|
mic_fail:
|
|
rx->key->u.tkip.mic_failures++;
|
|
|
|
mic_fail_no_key:
|
|
/*
|
|
* In some cases the key can be unset - e.g. a multicast packet, in
|
|
* a driver that supports HW encryption. Send up the key idx only if
|
|
* the key is set.
|
|
*/
|
|
cfg80211_michael_mic_failure(rx->sdata->dev, hdr->addr2,
|
|
is_multicast_ether_addr(hdr->addr1) ?
|
|
NL80211_KEYTYPE_GROUP :
|
|
NL80211_KEYTYPE_PAIRWISE,
|
|
rx->key ? rx->key->conf.keyidx : -1,
|
|
NULL, GFP_ATOMIC);
|
|
return RX_DROP_U_MMIC_FAIL;
|
|
}
|
|
|
|
static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
struct ieee80211_key *key = tx->key;
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
unsigned int hdrlen;
|
|
int len, tail;
|
|
u64 pn;
|
|
u8 *pos;
|
|
|
|
if (info->control.hw_key &&
|
|
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
|
|
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
|
|
/* hwaccel - with no need for software-generated IV */
|
|
return 0;
|
|
}
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
len = skb->len - hdrlen;
|
|
|
|
if (info->control.hw_key)
|
|
tail = 0;
|
|
else
|
|
tail = IEEE80211_TKIP_ICV_LEN;
|
|
|
|
if (WARN_ON(skb_tailroom(skb) < tail ||
|
|
skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
|
|
return -1;
|
|
|
|
pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
|
|
memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
|
|
pos += hdrlen;
|
|
|
|
/* the HW only needs room for the IV, but not the actual IV */
|
|
if (info->control.hw_key &&
|
|
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
|
|
return 0;
|
|
|
|
/* Increase IV for the frame */
|
|
pn = atomic64_inc_return(&key->conf.tx_pn);
|
|
pos = ieee80211_tkip_add_iv(pos, &key->conf, pn);
|
|
|
|
/* hwaccel - with software IV */
|
|
if (info->control.hw_key)
|
|
return 0;
|
|
|
|
/* Add room for ICV */
|
|
skb_put(skb, IEEE80211_TKIP_ICV_LEN);
|
|
|
|
return ieee80211_tkip_encrypt_data(&tx->local->wep_tx_ctx,
|
|
key, skb, pos, len);
|
|
}
|
|
|
|
|
|
ieee80211_tx_result
|
|
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
ieee80211_tx_set_protected(tx);
|
|
|
|
skb_queue_walk(&tx->skbs, skb) {
|
|
if (tkip_encrypt_skb(tx, skb) < 0)
|
|
return TX_DROP;
|
|
}
|
|
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
|
|
ieee80211_rx_result
|
|
ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
|
|
int hdrlen, res, hwaccel = 0;
|
|
struct ieee80211_key *key = rx->key;
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
|
|
if (!ieee80211_is_data(hdr->frame_control))
|
|
return RX_CONTINUE;
|
|
|
|
if (!rx->sta || skb->len - hdrlen < 12)
|
|
return RX_DROP_U_SHORT_TKIP;
|
|
|
|
/* it may be possible to optimize this a bit more */
|
|
if (skb_linearize(rx->skb))
|
|
return RX_DROP_U_OOM;
|
|
hdr = (void *)skb->data;
|
|
|
|
/*
|
|
* Let TKIP code verify IV, but skip decryption.
|
|
* In the case where hardware checks the IV as well,
|
|
* we don't even get here, see ieee80211_rx_h_decrypt()
|
|
*/
|
|
if (status->flag & RX_FLAG_DECRYPTED)
|
|
hwaccel = 1;
|
|
|
|
res = ieee80211_tkip_decrypt_data(&rx->local->wep_rx_ctx,
|
|
key, skb->data + hdrlen,
|
|
skb->len - hdrlen, rx->sta->sta.addr,
|
|
hdr->addr1, hwaccel, rx->security_idx,
|
|
&rx->tkip.iv32,
|
|
&rx->tkip.iv16);
|
|
if (res != TKIP_DECRYPT_OK)
|
|
return RX_DROP_U_TKIP_FAIL;
|
|
|
|
/* Trim ICV */
|
|
if (!(status->flag & RX_FLAG_ICV_STRIPPED))
|
|
skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
|
|
|
|
/* Remove IV */
|
|
memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
|
|
skb_pull(skb, IEEE80211_TKIP_IV_LEN);
|
|
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* Calculate AAD for CCMP/GCMP, returning qos_tid since we
|
|
* need that in CCMP also for b_0.
|
|
*/
|
|
static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu,
|
|
bool aad_nonce_computed)
|
|
{
|
|
struct ieee80211_hdr *hdr = (void *)skb->data;
|
|
__le16 mask_fc;
|
|
int a4_included, mgmt;
|
|
u8 qos_tid;
|
|
u16 len_a = 22;
|
|
|
|
/*
|
|
* Mask FC: zero subtype b4 b5 b6 (if not mgmt)
|
|
* Retry, PwrMgt, MoreData, Order (if Qos Data); set Protected
|
|
*/
|
|
mgmt = ieee80211_is_mgmt(hdr->frame_control);
|
|
mask_fc = hdr->frame_control;
|
|
mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |
|
|
IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
|
|
if (!mgmt)
|
|
mask_fc &= ~cpu_to_le16(0x0070);
|
|
mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
|
|
a4_included = ieee80211_has_a4(hdr->frame_control);
|
|
if (a4_included)
|
|
len_a += 6;
|
|
|
|
if (ieee80211_is_data_qos(hdr->frame_control)) {
|
|
qos_tid = *ieee80211_get_qos_ctl(hdr);
|
|
|
|
if (spp_amsdu)
|
|
qos_tid &= IEEE80211_QOS_CTL_TID_MASK |
|
|
IEEE80211_QOS_CTL_A_MSDU_PRESENT;
|
|
else
|
|
qos_tid &= IEEE80211_QOS_CTL_TID_MASK;
|
|
|
|
mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_ORDER);
|
|
len_a += 2;
|
|
} else {
|
|
qos_tid = 0;
|
|
}
|
|
|
|
/* AAD (extra authenticate-only data) / masked 802.11 header
|
|
* FC | A1 | A2 | A3 | SC | [A4] | [QC] */
|
|
put_unaligned_be16(len_a, &aad[0]);
|
|
put_unaligned(mask_fc, (__le16 *)&aad[2]);
|
|
if (!aad_nonce_computed)
|
|
memcpy(&aad[4], &hdr->addrs, 3 * ETH_ALEN);
|
|
|
|
/* Mask Seq#, leave Frag# */
|
|
aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f;
|
|
aad[23] = 0;
|
|
|
|
if (a4_included) {
|
|
memcpy(&aad[24], hdr->addr4, ETH_ALEN);
|
|
aad[30] = qos_tid;
|
|
aad[31] = 0;
|
|
} else {
|
|
memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN);
|
|
aad[24] = qos_tid;
|
|
}
|
|
|
|
return qos_tid;
|
|
}
|
|
|
|
static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
|
|
bool spp_amsdu, bool aad_nonce_computed)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
u8 qos_tid = ccmp_gcmp_aad(skb, aad, spp_amsdu, aad_nonce_computed);
|
|
|
|
/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
|
|
* mode authentication are not allowed to collide, yet both are derived
|
|
* from this vector b_0. We only set L := 1 here to indicate that the
|
|
* data size can be represented in (L+1) bytes. The CCM layer will take
|
|
* care of storing the data length in the top (L+1) bytes and setting
|
|
* and clearing the other bits as is required to derive the two IVs.
|
|
*/
|
|
b_0[0] = 0x1;
|
|
|
|
/* Nonce: Nonce Flags | A2 | PN
|
|
* Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
|
|
*/
|
|
b_0[1] = qos_tid | (ieee80211_is_mgmt(hdr->frame_control) << 4);
|
|
if (!aad_nonce_computed)
|
|
memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
|
|
memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
|
|
}
|
|
|
|
static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
|
|
{
|
|
hdr[0] = pn[5];
|
|
hdr[1] = pn[4];
|
|
hdr[2] = 0;
|
|
hdr[3] = 0x20 | (key_id << 6);
|
|
hdr[4] = pn[3];
|
|
hdr[5] = pn[2];
|
|
hdr[6] = pn[1];
|
|
hdr[7] = pn[0];
|
|
}
|
|
|
|
|
|
static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr)
|
|
{
|
|
pn[0] = hdr[7];
|
|
pn[1] = hdr[6];
|
|
pn[2] = hdr[5];
|
|
pn[3] = hdr[4];
|
|
pn[4] = hdr[1];
|
|
pn[5] = hdr[0];
|
|
}
|
|
|
|
|
|
static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb,
|
|
unsigned int mic_len)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
struct ieee80211_key *key = tx->key;
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
int hdrlen, len, tail;
|
|
u8 *pos;
|
|
u8 pn[6];
|
|
u64 pn64;
|
|
u8 aad[CCM_AAD_LEN];
|
|
u8 b_0[AES_BLOCK_SIZE];
|
|
|
|
if (info->control.hw_key &&
|
|
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
|
|
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
|
|
!((info->control.hw_key->flags &
|
|
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
|
|
ieee80211_is_mgmt(hdr->frame_control))) {
|
|
/*
|
|
* hwaccel has no need for preallocated room for CCMP
|
|
* header or MIC fields
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
len = skb->len - hdrlen;
|
|
|
|
if (info->control.hw_key)
|
|
tail = 0;
|
|
else
|
|
tail = mic_len;
|
|
|
|
if (WARN_ON(skb_tailroom(skb) < tail ||
|
|
skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
|
|
return -1;
|
|
|
|
pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
|
|
memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
|
|
|
|
/* the HW only needs room for the IV, but not the actual IV */
|
|
if (info->control.hw_key &&
|
|
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
|
|
return 0;
|
|
|
|
pos += hdrlen;
|
|
|
|
pn64 = atomic64_inc_return(&key->conf.tx_pn);
|
|
|
|
pn[5] = pn64;
|
|
pn[4] = pn64 >> 8;
|
|
pn[3] = pn64 >> 16;
|
|
pn[2] = pn64 >> 24;
|
|
pn[1] = pn64 >> 32;
|
|
pn[0] = pn64 >> 40;
|
|
|
|
ccmp_pn2hdr(pos, pn, key->conf.keyidx);
|
|
|
|
/* hwaccel - with software CCMP header */
|
|
if (info->control.hw_key)
|
|
return 0;
|
|
|
|
pos += IEEE80211_CCMP_HDR_LEN;
|
|
ccmp_special_blocks(skb, pn, b_0, aad,
|
|
key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
|
|
false);
|
|
return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
|
|
skb_put(skb, mic_len));
|
|
}
|
|
|
|
|
|
ieee80211_tx_result
|
|
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
|
|
unsigned int mic_len)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
ieee80211_tx_set_protected(tx);
|
|
|
|
skb_queue_walk(&tx->skbs, skb) {
|
|
if (ccmp_encrypt_skb(tx, skb, mic_len) < 0)
|
|
return TX_DROP;
|
|
}
|
|
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
|
|
ieee80211_rx_result
|
|
ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
|
|
unsigned int mic_len)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
|
int hdrlen;
|
|
struct ieee80211_key *key = rx->key;
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
|
u8 pn[IEEE80211_CCMP_PN_LEN];
|
|
int data_len;
|
|
int queue;
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
|
|
if (!ieee80211_is_data(hdr->frame_control) &&
|
|
!ieee80211_is_robust_mgmt_frame(skb) &&
|
|
!ieee80211_require_encrypted_assoc(hdr->frame_control, rx->sta))
|
|
return RX_CONTINUE;
|
|
|
|
if (status->flag & RX_FLAG_DECRYPTED) {
|
|
if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN))
|
|
return RX_DROP_U_SHORT_CCMP;
|
|
if (status->flag & RX_FLAG_MIC_STRIPPED)
|
|
mic_len = 0;
|
|
} else {
|
|
if (skb_linearize(rx->skb))
|
|
return RX_DROP_U_OOM;
|
|
}
|
|
|
|
/* reload hdr - skb might have been reallocated */
|
|
hdr = (void *)rx->skb->data;
|
|
|
|
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
|
|
if (!rx->sta || data_len < 0)
|
|
return RX_DROP_U_SHORT_CCMP;
|
|
|
|
if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
|
|
int res;
|
|
|
|
ccmp_hdr2pn(pn, skb->data + hdrlen);
|
|
|
|
queue = rx->security_idx;
|
|
|
|
res = memcmp(pn, key->u.ccmp.rx_pn[queue],
|
|
IEEE80211_CCMP_PN_LEN);
|
|
if (res < 0 ||
|
|
(!res && !(status->flag & RX_FLAG_ALLOW_SAME_PN))) {
|
|
key->u.ccmp.replays++;
|
|
return RX_DROP_U_REPLAY;
|
|
}
|
|
|
|
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
|
u8 aad[2 * AES_BLOCK_SIZE];
|
|
u8 b_0[AES_BLOCK_SIZE];
|
|
bool aad_nonce_computed = false;
|
|
|
|
if (is_unicast_ether_addr(hdr->addr1) &&
|
|
!ieee80211_is_data(hdr->frame_control)) {
|
|
/* AAD computation */
|
|
memcpy(&aad[4], rx->link_addrs, 3 * ETH_ALEN);
|
|
/* Nonce computation */
|
|
ether_addr_copy(&b_0[2],
|
|
&rx->link_addrs[ETH_ALEN]);
|
|
aad_nonce_computed = true;
|
|
}
|
|
|
|
/* hardware didn't decrypt/verify MIC */
|
|
ccmp_special_blocks(skb, pn, b_0, aad,
|
|
key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
|
|
aad_nonce_computed);
|
|
|
|
if (ieee80211_aes_ccm_decrypt(
|
|
key->u.ccmp.tfm, b_0, aad,
|
|
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
|
|
data_len,
|
|
skb->data + skb->len - mic_len))
|
|
return RX_DROP_U_MIC_FAIL;
|
|
}
|
|
|
|
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
|
|
if (unlikely(ieee80211_is_frag(hdr)))
|
|
memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN);
|
|
}
|
|
|
|
/* Remove CCMP header and MIC */
|
|
if (pskb_trim(skb, skb->len - mic_len))
|
|
return RX_DROP_U_SHORT_CCMP_MIC;
|
|
memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
|
|
skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
|
|
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad,
|
|
bool spp_amsdu, bool aad_nonce_computed)
|
|
{
|
|
struct ieee80211_hdr *hdr = (void *)skb->data;
|
|
|
|
if (!aad_nonce_computed)
|
|
memcpy(j_0, hdr->addr2, ETH_ALEN);
|
|
memcpy(&j_0[ETH_ALEN], pn, IEEE80211_GCMP_PN_LEN);
|
|
|
|
ccmp_gcmp_aad(skb, aad, spp_amsdu, aad_nonce_computed);
|
|
}
|
|
|
|
static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
|
|
{
|
|
hdr[0] = pn[5];
|
|
hdr[1] = pn[4];
|
|
hdr[2] = 0;
|
|
hdr[3] = 0x20 | (key_id << 6);
|
|
hdr[4] = pn[3];
|
|
hdr[5] = pn[2];
|
|
hdr[6] = pn[1];
|
|
hdr[7] = pn[0];
|
|
}
|
|
|
|
static inline void gcmp_hdr2pn(u8 *pn, const u8 *hdr)
|
|
{
|
|
pn[0] = hdr[7];
|
|
pn[1] = hdr[6];
|
|
pn[2] = hdr[5];
|
|
pn[3] = hdr[4];
|
|
pn[4] = hdr[1];
|
|
pn[5] = hdr[0];
|
|
}
|
|
|
|
static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
struct ieee80211_key *key = tx->key;
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
int hdrlen, len, tail;
|
|
u8 *pos;
|
|
u8 pn[6];
|
|
u64 pn64;
|
|
u8 aad[GCM_AAD_LEN];
|
|
u8 j_0[AES_BLOCK_SIZE];
|
|
|
|
if (info->control.hw_key &&
|
|
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
|
|
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
|
|
!((info->control.hw_key->flags &
|
|
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
|
|
ieee80211_is_mgmt(hdr->frame_control))) {
|
|
/* hwaccel has no need for preallocated room for GCMP
|
|
* header or MIC fields
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
len = skb->len - hdrlen;
|
|
|
|
if (info->control.hw_key)
|
|
tail = 0;
|
|
else
|
|
tail = IEEE80211_GCMP_MIC_LEN;
|
|
|
|
if (WARN_ON(skb_tailroom(skb) < tail ||
|
|
skb_headroom(skb) < IEEE80211_GCMP_HDR_LEN))
|
|
return -1;
|
|
|
|
pos = skb_push(skb, IEEE80211_GCMP_HDR_LEN);
|
|
memmove(pos, pos + IEEE80211_GCMP_HDR_LEN, hdrlen);
|
|
skb_set_network_header(skb, skb_network_offset(skb) +
|
|
IEEE80211_GCMP_HDR_LEN);
|
|
|
|
/* the HW only needs room for the IV, but not the actual IV */
|
|
if (info->control.hw_key &&
|
|
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
|
|
return 0;
|
|
|
|
pos += hdrlen;
|
|
|
|
pn64 = atomic64_inc_return(&key->conf.tx_pn);
|
|
|
|
pn[5] = pn64;
|
|
pn[4] = pn64 >> 8;
|
|
pn[3] = pn64 >> 16;
|
|
pn[2] = pn64 >> 24;
|
|
pn[1] = pn64 >> 32;
|
|
pn[0] = pn64 >> 40;
|
|
|
|
gcmp_pn2hdr(pos, pn, key->conf.keyidx);
|
|
|
|
/* hwaccel - with software GCMP header */
|
|
if (info->control.hw_key)
|
|
return 0;
|
|
|
|
pos += IEEE80211_GCMP_HDR_LEN;
|
|
gcmp_special_blocks(skb, pn, j_0, aad,
|
|
key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
|
|
false);
|
|
return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
|
|
skb_put(skb, IEEE80211_GCMP_MIC_LEN));
|
|
}
|
|
|
|
ieee80211_tx_result
|
|
ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
ieee80211_tx_set_protected(tx);
|
|
|
|
skb_queue_walk(&tx->skbs, skb) {
|
|
if (gcmp_encrypt_skb(tx, skb) < 0)
|
|
return TX_DROP;
|
|
}
|
|
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
ieee80211_rx_result
|
|
ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
|
int hdrlen;
|
|
struct ieee80211_key *key = rx->key;
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
|
u8 pn[IEEE80211_GCMP_PN_LEN];
|
|
int data_len, queue, mic_len = IEEE80211_GCMP_MIC_LEN;
|
|
|
|
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
|
|
|
if (!ieee80211_is_data(hdr->frame_control) &&
|
|
!ieee80211_is_robust_mgmt_frame(skb) &&
|
|
!ieee80211_require_encrypted_assoc(hdr->frame_control, rx->sta))
|
|
return RX_CONTINUE;
|
|
|
|
if (status->flag & RX_FLAG_DECRYPTED) {
|
|
if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
|
|
return RX_DROP_U_SHORT_GCMP;
|
|
if (status->flag & RX_FLAG_MIC_STRIPPED)
|
|
mic_len = 0;
|
|
} else {
|
|
if (skb_linearize(rx->skb))
|
|
return RX_DROP_U_OOM;
|
|
}
|
|
|
|
/* reload hdr - skb might have been reallocated */
|
|
hdr = (void *)rx->skb->data;
|
|
|
|
data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len;
|
|
if (!rx->sta || data_len < 0)
|
|
return RX_DROP_U_SHORT_GCMP;
|
|
|
|
if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
|
|
int res;
|
|
|
|
gcmp_hdr2pn(pn, skb->data + hdrlen);
|
|
|
|
queue = rx->security_idx;
|
|
|
|
res = memcmp(pn, key->u.gcmp.rx_pn[queue],
|
|
IEEE80211_GCMP_PN_LEN);
|
|
if (res < 0 ||
|
|
(!res && !(status->flag & RX_FLAG_ALLOW_SAME_PN))) {
|
|
key->u.gcmp.replays++;
|
|
return RX_DROP_U_REPLAY;
|
|
}
|
|
|
|
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
|
u8 aad[2 * AES_BLOCK_SIZE];
|
|
u8 j_0[AES_BLOCK_SIZE];
|
|
bool aad_nonce_computed = false;
|
|
|
|
if (is_unicast_ether_addr(hdr->addr1) &&
|
|
!ieee80211_is_data(hdr->frame_control)) {
|
|
/* AAD computation */
|
|
memcpy(&aad[4], rx->link_addrs, 3 * ETH_ALEN);
|
|
/* Nonce computation */
|
|
ether_addr_copy(&j_0[0],
|
|
&rx->link_addrs[ETH_ALEN]);
|
|
aad_nonce_computed = true;
|
|
}
|
|
/* hardware didn't decrypt/verify MIC */
|
|
gcmp_special_blocks(skb, pn, j_0, aad,
|
|
key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
|
|
aad_nonce_computed);
|
|
|
|
if (ieee80211_aes_gcm_decrypt(
|
|
key->u.gcmp.tfm, j_0, aad,
|
|
skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
|
|
data_len,
|
|
skb->data + skb->len -
|
|
IEEE80211_GCMP_MIC_LEN))
|
|
return RX_DROP_U_MIC_FAIL;
|
|
}
|
|
|
|
memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
|
|
if (unlikely(ieee80211_is_frag(hdr)))
|
|
memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN);
|
|
}
|
|
|
|
/* Remove GCMP header and MIC */
|
|
if (pskb_trim(skb, skb->len - mic_len))
|
|
return RX_DROP_U_SHORT_GCMP_MIC;
|
|
memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
|
|
skb_pull(skb, IEEE80211_GCMP_HDR_LEN);
|
|
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
static void bip_aad(struct sk_buff *skb, u8 *aad)
|
|
{
|
|
__le16 mask_fc;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
|
|
/* BIP AAD: FC(masked) || A1 || A2 || A3 */
|
|
|
|
/* FC type/subtype */
|
|
/* Mask FC Retry, PwrMgt, MoreData flags to zero */
|
|
mask_fc = hdr->frame_control;
|
|
mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM |
|
|
IEEE80211_FCTL_MOREDATA);
|
|
put_unaligned(mask_fc, (__le16 *) &aad[0]);
|
|
/* A1 || A2 || A3 */
|
|
memcpy(aad + 2, &hdr->addrs, 3 * ETH_ALEN);
|
|
}
|
|
|
|
|
|
static inline void bip_ipn_set64(u8 *d, u64 pn)
|
|
{
|
|
*d++ = pn;
|
|
*d++ = pn >> 8;
|
|
*d++ = pn >> 16;
|
|
*d++ = pn >> 24;
|
|
*d++ = pn >> 32;
|
|
*d = pn >> 40;
|
|
}
|
|
|
|
static inline void bip_ipn_swap(u8 *d, const u8 *s)
|
|
{
|
|
*d++ = s[5];
|
|
*d++ = s[4];
|
|
*d++ = s[3];
|
|
*d++ = s[2];
|
|
*d++ = s[1];
|
|
*d = s[0];
|
|
}
|
|
|
|
|
|
ieee80211_tx_result
|
|
ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx,
|
|
unsigned int mic_len)
|
|
{
|
|
struct sk_buff *skb;
|
|
struct ieee80211_tx_info *info;
|
|
struct ieee80211_key *key = tx->key;
|
|
struct ieee80211_mmie_var *mmie;
|
|
size_t mmie_len;
|
|
u8 aad[20];
|
|
u64 pn64;
|
|
|
|
if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
|
|
return TX_DROP;
|
|
|
|
skb = skb_peek(&tx->skbs);
|
|
|
|
info = IEEE80211_SKB_CB(skb);
|
|
|
|
if (info->control.hw_key &&
|
|
!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
|
|
return TX_CONTINUE;
|
|
|
|
mmie_len = sizeof(*mmie) + mic_len;
|
|
|
|
if (WARN_ON(skb_tailroom(skb) < mmie_len))
|
|
return TX_DROP;
|
|
|
|
mmie = skb_put(skb, mmie_len);
|
|
mmie->element_id = WLAN_EID_MMIE;
|
|
mmie->length = mmie_len - 2;
|
|
mmie->key_id = cpu_to_le16(key->conf.keyidx);
|
|
|
|
/* PN = PN + 1 */
|
|
pn64 = atomic64_inc_return(&key->conf.tx_pn);
|
|
|
|
bip_ipn_set64(mmie->sequence_number, pn64);
|
|
|
|
if (info->control.hw_key)
|
|
return TX_CONTINUE;
|
|
|
|
bip_aad(skb, aad);
|
|
|
|
ieee80211_aes_cmac(&key->u.aes_cmac.key, aad, skb->data + 24,
|
|
skb->len - 24, mmie->mic, mic_len);
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
ieee80211_rx_result
|
|
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx,
|
|
unsigned int mic_len)
|
|
{
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
|
struct ieee80211_key *key = rx->key;
|
|
struct ieee80211_mmie_var *mmie;
|
|
size_t mmie_len;
|
|
u8 aad[20], mic[IEEE80211_CMAC_256_MIC_LEN], ipn[6];
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
|
|
if (!ieee80211_is_mgmt(hdr->frame_control))
|
|
return RX_CONTINUE;
|
|
|
|
mmie_len = sizeof(*mmie) + mic_len;
|
|
|
|
/* management frames are already linear */
|
|
|
|
if (skb->len < 24 + mmie_len)
|
|
return mic_len == IEEE80211_CMAC_128_MIC_LEN ?
|
|
RX_DROP_U_SHORT_CMAC : RX_DROP_U_SHORT_CMAC256;
|
|
|
|
mmie = (struct ieee80211_mmie_var *)(skb->data + skb->len - mmie_len);
|
|
if (mmie->element_id != WLAN_EID_MMIE ||
|
|
mmie->length != mmie_len - 2)
|
|
return RX_DROP_U_BAD_MMIE; /* Invalid MMIE */
|
|
|
|
bip_ipn_swap(ipn, mmie->sequence_number);
|
|
|
|
if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
|
|
key->u.aes_cmac.replays++;
|
|
return RX_DROP_U_REPLAY;
|
|
}
|
|
|
|
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
|
/* hardware didn't decrypt/verify MIC */
|
|
bip_aad(skb, aad);
|
|
ieee80211_aes_cmac(&key->u.aes_cmac.key, aad, skb->data + 24,
|
|
skb->len - 24, mic, mic_len);
|
|
if (crypto_memneq(mic, mmie->mic, mic_len)) {
|
|
key->u.aes_cmac.icverrors++;
|
|
return RX_DROP_U_MIC_FAIL;
|
|
}
|
|
}
|
|
|
|
memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
|
|
|
|
/* Remove MMIE */
|
|
skb_trim(skb, skb->len - mmie_len);
|
|
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
ieee80211_tx_result
|
|
ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
|
|
{
|
|
struct sk_buff *skb;
|
|
struct ieee80211_tx_info *info;
|
|
struct ieee80211_key *key = tx->key;
|
|
struct ieee80211_mmie_16 *mmie;
|
|
struct ieee80211_hdr *hdr;
|
|
u8 aad[GMAC_AAD_LEN];
|
|
u64 pn64;
|
|
u8 nonce[GMAC_NONCE_LEN];
|
|
|
|
if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
|
|
return TX_DROP;
|
|
|
|
skb = skb_peek(&tx->skbs);
|
|
|
|
info = IEEE80211_SKB_CB(skb);
|
|
|
|
if (info->control.hw_key &&
|
|
!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
|
|
return TX_CONTINUE;
|
|
|
|
if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
|
|
return TX_DROP;
|
|
|
|
mmie = skb_put(skb, sizeof(*mmie));
|
|
mmie->element_id = WLAN_EID_MMIE;
|
|
mmie->length = sizeof(*mmie) - 2;
|
|
mmie->key_id = cpu_to_le16(key->conf.keyidx);
|
|
|
|
/* PN = PN + 1 */
|
|
pn64 = atomic64_inc_return(&key->conf.tx_pn);
|
|
|
|
bip_ipn_set64(mmie->sequence_number, pn64);
|
|
|
|
if (info->control.hw_key)
|
|
return TX_CONTINUE;
|
|
|
|
bip_aad(skb, aad);
|
|
|
|
hdr = (struct ieee80211_hdr *)skb->data;
|
|
memcpy(nonce, hdr->addr2, ETH_ALEN);
|
|
bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
|
|
|
|
/* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
|
|
if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
|
|
skb->data + 24, skb->len - 24, mmie->mic) < 0)
|
|
return TX_DROP;
|
|
|
|
return TX_CONTINUE;
|
|
}
|
|
|
|
ieee80211_rx_result
|
|
ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
|
|
{
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
|
struct ieee80211_key *key = rx->key;
|
|
struct ieee80211_mmie_16 *mmie;
|
|
u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN];
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
|
|
if (!ieee80211_is_mgmt(hdr->frame_control))
|
|
return RX_CONTINUE;
|
|
|
|
/* management frames are already linear */
|
|
|
|
if (skb->len < 24 + sizeof(*mmie))
|
|
return RX_DROP_U_SHORT_GMAC;
|
|
|
|
mmie = (struct ieee80211_mmie_16 *)
|
|
(skb->data + skb->len - sizeof(*mmie));
|
|
if (mmie->element_id != WLAN_EID_MMIE ||
|
|
mmie->length != sizeof(*mmie) - 2)
|
|
return RX_DROP_U_BAD_MMIE; /* Invalid MMIE */
|
|
|
|
bip_ipn_swap(ipn, mmie->sequence_number);
|
|
|
|
if (memcmp(ipn, key->u.aes_gmac.rx_pn, 6) <= 0) {
|
|
key->u.aes_gmac.replays++;
|
|
return RX_DROP_U_REPLAY;
|
|
}
|
|
|
|
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
|
/* hardware didn't decrypt/verify MIC */
|
|
bip_aad(skb, aad);
|
|
|
|
memcpy(nonce, hdr->addr2, ETH_ALEN);
|
|
memcpy(nonce + ETH_ALEN, ipn, 6);
|
|
|
|
mic = kmalloc(IEEE80211_GMAC_MIC_LEN, GFP_ATOMIC);
|
|
if (!mic)
|
|
return RX_DROP_U_OOM;
|
|
if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
|
|
skb->data + 24, skb->len - 24,
|
|
mic) < 0 ||
|
|
crypto_memneq(mic, mmie->mic, sizeof(mmie->mic))) {
|
|
key->u.aes_gmac.icverrors++;
|
|
kfree(mic);
|
|
return RX_DROP_U_MIC_FAIL;
|
|
}
|
|
kfree(mic);
|
|
}
|
|
|
|
memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
|
|
|
|
/* Remove MMIE */
|
|
skb_trim(skb, skb->len - sizeof(*mmie));
|
|
|
|
return RX_CONTINUE;
|
|
}
|