f25dea01d292462600326b829b3c8390754bae12
[openwrt/staging/neocturne.git] /
1 From 1259055170287a350cad453e9eac139c81609860 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
3 Date: Thu, 15 Mar 2018 08:29:09 +0100
4 Subject: [PATCH] brcmfmac: drop Inter-Access Point Protocol packets by default
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 Testing brcmfmac with more recent firmwares resulted in AP interfaces
10 not working in some specific setups. Debugging resulted in discovering
11 support for IAPP in Broadcom's firmwares.
12
13 Older firmwares were only generating 802.11f frames. Newer ones like:
14 1) 10.10 (TOB) (r663589)
15 2) 10.10.122.20 (r683106)
16 for 4366b1 and 4366c0 respectively seem to also /respect/ 802.11f frames
17 in the Tx path by performing a STA disassociation.
18
19 This obsoleted standard and its implementation is something that:
20 1) Most people don't need / want to use
21 2) Can allow local DoS attacks
22 3) Breaks AP interfaces in some specific bridge setups
23
24 To solve issues it can cause this commit modifies brcmfmac to drop IAPP
25 packets. If affects:
26 1) Rx path: driver won't be sending these unwanted packets up.
27 2) Tx path: driver will reject packets that would trigger STA
28 disassociation perfromed by a firmware (possible local DoS attack).
29
30 It appears there are some Broadcom's clients/users who care about this
31 feature despite the drawbacks. They can switch it on using a new module
32 param.
33
34 This change results in only two more comparisons (check for module param
35 and check for Ethernet packet length) for 99.9% of packets. Its overhead
36 should be very minimal.
37
38 Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
39 Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
40 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
41 ---
42 .../wireless/broadcom/brcm80211/brcmfmac/common.c | 5 ++
43 .../wireless/broadcom/brcm80211/brcmfmac/common.h | 1 +
44 .../wireless/broadcom/brcm80211/brcmfmac/core.c | 57 ++++++++++++++++++++++
45 3 files changed, 63 insertions(+)
46
47 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
48 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
49 @@ -75,6 +75,10 @@ static int brcmf_roamoff;
50 module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
51 MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
52
53 +static int brcmf_iapp_enable;
54 +module_param_named(iapp, brcmf_iapp_enable, int, 0);
55 +MODULE_PARM_DESC(iapp, "Enable partial support for the obsoleted Inter-Access Point Protocol");
56 +
57 #ifdef DEBUG
58 /* always succeed brcmf_bus_started() */
59 static int brcmf_ignore_probe_fail;
60 @@ -441,6 +445,7 @@ struct brcmf_mp_device *brcmf_get_module
61 settings->feature_disable = brcmf_feature_disable;
62 settings->fcmode = brcmf_fcmode;
63 settings->roamoff = !!brcmf_roamoff;
64 + settings->iapp = !!brcmf_iapp_enable;
65 #ifdef DEBUG
66 settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
67 #endif
68 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
69 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
70 @@ -58,6 +58,7 @@ struct brcmf_mp_device {
71 unsigned int feature_disable;
72 int fcmode;
73 bool roamoff;
74 + bool iapp;
75 bool ignore_probe_fail;
76 struct brcmfmac_pd_cc *country_codes;
77 union {
78 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
79 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
80 @@ -230,6 +230,37 @@ static void brcmf_netdev_set_multicast_l
81 schedule_work(&ifp->multicast_work);
82 }
83
84 +/**
85 + * brcmf_skb_is_iapp - checks if skb is an IAPP packet
86 + *
87 + * @skb: skb to check
88 + */
89 +static bool brcmf_skb_is_iapp(struct sk_buff *skb)
90 +{
91 + static const u8 iapp_l2_update_packet[6] __aligned(2) = {
92 + 0x00, 0x01, 0xaf, 0x81, 0x01, 0x00,
93 + };
94 + unsigned char *eth_data;
95 +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
96 + const u16 *a, *b;
97 +#endif
98 +
99 + if (skb->len - skb->mac_len != 6 ||
100 + !is_multicast_ether_addr(eth_hdr(skb)->h_dest))
101 + return false;
102 +
103 + eth_data = skb_mac_header(skb) + ETH_HLEN;
104 +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
105 + return !(((*(const u32 *)eth_data) ^ (*(const u32 *)iapp_l2_update_packet)) |
106 + ((*(const u16 *)(eth_data + 4)) ^ (*(const u16 *)(iapp_l2_update_packet + 4))));
107 +#else
108 + a = (const u16 *)eth_data;
109 + b = (const u16 *)iapp_l2_update_packet;
110 +
111 + return !((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]));
112 +#endif
113 +}
114 +
115 static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
116 struct net_device *ndev)
117 {
118 @@ -250,6 +281,23 @@ static netdev_tx_t brcmf_netdev_start_xm
119 goto done;
120 }
121
122 + /* Some recent Broadcom's firmwares disassociate STA when they receive
123 + * an 802.11f ADD frame. This behavior can lead to a local DoS security
124 + * issue. Attacker may trigger disassociation of any STA by sending a
125 + * proper Ethernet frame to the wireless interface.
126 + *
127 + * Moreover this feature may break AP interfaces in some specific
128 + * setups. This applies e.g. to the bridge with hairpin mode enabled and
129 + * IFLA_BRPORT_MCAST_TO_UCAST set. IAPP packet generated by a firmware
130 + * will get passed back to the wireless interface and cause immediate
131 + * disassociation of a just-connected STA.
132 + */
133 + if (!drvr->settings->iapp && brcmf_skb_is_iapp(skb)) {
134 + dev_kfree_skb(skb);
135 + ret = -EINVAL;
136 + goto done;
137 + }
138 +
139 /* Make sure there's enough writeable headroom */
140 if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) {
141 head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0);
142 @@ -325,6 +373,15 @@ void brcmf_txflowblock_if(struct brcmf_i
143
144 void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
145 {
146 + /* Most of Broadcom's firmwares send 802.11f ADD frame every time a new
147 + * STA connects to the AP interface. This is an obsoleted standard most
148 + * users don't use, so don't pass these frames up unless requested.
149 + */
150 + if (!ifp->drvr->settings->iapp && brcmf_skb_is_iapp(skb)) {
151 + brcmu_pkt_buf_free_skb(skb);
152 + return;
153 + }
154 +
155 if (skb->pkt_type == PACKET_MULTICAST)
156 ifp->ndev->stats.multicast++;
157