Rafał Miłecki
2018-Mar-14 11:01 UTC
[Bridge] [PATCH] brcmfmac: drop Inter-Access Point Protocol packets by default
From: Rafa? Mi?ecki <rafal at milecki.pl> Testing brcmfmac with more recent firmwares resulted in AP interfaces not working in some specific setups. Debugging resulted in discovering support for IAPP in Broadcom's firmwares. This is an obsoleted standard and its implementation is something that: 1) Most people don't need / want to use 2) Can allow local DoS attacks 3) Breaks AP interfaces in some specific bridge setups To solve issues it can cause this commit modifies brcmfmac to drop IAPP packets. If affects: 1) Rx path: driver won't be sending these unwanted packets up. 2) Tx path: driver will reject packets that would trigger STA disassociation perfromed by a firmware (possible local DoS attack). It appears there are some Broadcom's clients/users who care about this feature despite the drawbacks. They can switch it on by a newly added Kconfig option. Signed-off-by: Rafa? Mi?ecki <rafal at milecki.pl> --- drivers/net/wireless/broadcom/brcm80211/Kconfig | 20 +++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/core.c | 39 ++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig index 9d99eb42d917..876787ef991a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -68,6 +68,26 @@ config BRCMFMAC_PCIE IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to use the driver for an PCIE wireless card. +config BRCMFMAC_IAPP + bool "Partial support for obsoleted Inter-Access Point Protocol" + depends on BRCMFMAC + ---help--- + Most of Broadcom's firmwares can send 802.11f ADD frame every + time new STA connects to the AP interface. Some recent ones + can also disassociate STA when they receive such a frame. + + It's important to understand this behavior can lead to a local + DoS security issue. Attacker may trigger disassociation of any + STA by sending a proper Ethernet frame to the wireless + interface. + + Moreover this feature may break AP interfaces in some specific + setups. This applies e.g. to the bridge with hairpin mode + enabled and IFLA_BRPORT_MCAST_TO_UCAST set. IAPP packet + generated by a firmware will get passed back to the wireless + interface and cause immediate disassociation of just-connected + STA. + config BRCM_TRACING bool "Broadcom device tracing" depends on BRCMSMAC || BRCMFMAC diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 19048526b4af..db6987015fb1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -230,6 +230,34 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev) schedule_work(&ifp->multicast_work); } +/** + * brcmf_skb_is_iapp - checks if skb is an IAPP packet + * + * @skb: skb to check + */ +static bool brcmf_skb_is_iapp(struct sk_buff *skb) +{ + const u8 iapp_l2_update_packet[6] __aligned(2) = { + 0x00, 0x01, 0xaf, 0x81, 0x01, 0x00, + }; + unsigned char *eth_data = skb_mac_header(skb) + ETH_HLEN; +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + const u16 *a = (const u16 *)eth_data; + const u16 *b = (const u16 *)iapp_l2_update_packet; +#endif + + if (skb->len - skb->mac_len != 6 || + !is_multicast_ether_addr(eth_hdr(skb)->h_dest)) + return false; + +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + return !(((*(const u32 *)eth_data) ^ (*(const u32 *)iapp_l2_update_packet)) | + ((*(const u16 *)(eth_data + 4)) ^ (*(const u16 *)(iapp_l2_update_packet + 4)))); +#else + return !((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])); +#endif +} + static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { @@ -250,6 +278,12 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } + if (!IS_ENABLED(CONFIG_BRCMFMAC_IAPP) && brcmf_skb_is_iapp(skb)) { + dev_kfree_skb(skb); + ret = -EINVAL; + goto done; + } + /* Make sure there's enough writeable headroom */ if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) { head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0); @@ -325,6 +359,11 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) { + if (!IS_ENABLED(CONFIG_BRCMFMAC_IAPP) && brcmf_skb_is_iapp(skb)) { + brcmu_pkt_buf_free_skb(skb); + return; + } + if (skb->pkt_type == PACKET_MULTICAST) ifp->ndev->stats.multicast++; -- 2.11.0
Arend van Spriel
2018-Mar-14 12:58 UTC
[Bridge] [PATCH] brcmfmac: drop Inter-Access Point Protocol packets by default
On 3/14/2018 12:01 PM, Rafa? Mi?ecki wrote:> From: Rafa? Mi?ecki <rafal at milecki.pl> > > Testing brcmfmac with more recent firmwares resulted in AP interfaces > not working in some specific setups. Debugging resulted in discovering > support for IAPP in Broadcom's firmwares. This is an obsoleted standard > and its implementation is something that: > 1) Most people don't need / want to use > 2) Can allow local DoS attacks > 3) Breaks AP interfaces in some specific bridge setups > > To solve issues it can cause this commit modifies brcmfmac to drop IAPP > packets. If affects: > 1) Rx path: driver won't be sending these unwanted packets up. > 2) Tx path: driver will reject packets that would trigger STA > disassociation perfromed by a firmware (possible local DoS attack). > > It appears there are some Broadcom's clients/users who care about this > feature despite the drawbacks. They can switch it on by a newly added > Kconfig option.Thanks for taking this approach. Looks fine except for .... (see below) Reviewed-by: Arend van Spriel <arend.vanspriel at broadcom.com>> Signed-off-by: Rafa? Mi?ecki <rafal at milecki.pl> > --- > drivers/net/wireless/broadcom/brcm80211/Kconfig | 20 +++++++++++ > .../wireless/broadcom/brcm80211/brcmfmac/core.c | 39 ++++++++++++++++++++++ > 2 files changed, 59 insertions(+) > > diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig > index 9d99eb42d917..876787ef991a 100644 > --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig > +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig > @@ -68,6 +68,26 @@ config BRCMFMAC_PCIE > IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to > use the driver for an PCIE wireless card. > > +config BRCMFMAC_IAPP > + bool "Partial support for obsoleted Inter-Access Point Protocol" > + depends on BRCMFMAC > + ---help--- > + Most of Broadcom's firmwares can send 802.11f ADD frame every > + time new STA connects to the AP interface. Some recent ones > + can also disassociate STA when they receive such a frame.I do not see any evidence that this would occur only for recent firmware. That stuff is old and not touched recently.> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c > index 19048526b4af..db6987015fb1 100644 > --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c > +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c[...]> static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, > struct net_device *ndev) > { > @@ -250,6 +278,12 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, > goto done; > } > > + if (!IS_ENABLED(CONFIG_BRCMFMAC_IAPP) && brcmf_skb_is_iapp(skb)) { > + dev_kfree_skb(skb); > + ret = -EINVAL; > + goto done; > + }This is not right. The function must return netdev_tx_t type. Here is kerneldoc of .start_xmit(): * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, * struct net_device *dev); * Called when a packet needs to be transmitted. * Returns NETDEV_TX_OK. Can return NETDEV_TX_BUSY, but you should stop * the queue before that can happen; it's for obsolete devices and weird * corner cases, but the stack really does a non-trivial amount * of useless work if you return NETDEV_TX_BUSY. * Required; cannot be NULL. You may want to increase dropped netstat or add driver internal statistic counter so there is visibility of IAPP packets being dropped. Regards, Arend
Kalle Valo
2018-Mar-14 14:24 UTC
[Bridge] [PATCH] brcmfmac: drop Inter-Access Point Protocol packets by default
Rafa? Mi?ecki <zajec5 at gmail.com> writes:> From: Rafa? Mi?ecki <rafal at milecki.pl> > > Testing brcmfmac with more recent firmwares resulted in AP interfaces > not working in some specific setups. Debugging resulted in discovering > support for IAPP in Broadcom's firmwares. This is an obsoleted standard > and its implementation is something that: > 1) Most people don't need / want to use > 2) Can allow local DoS attacks > 3) Breaks AP interfaces in some specific bridge setups > > To solve issues it can cause this commit modifies brcmfmac to drop IAPP > packets. If affects: > 1) Rx path: driver won't be sending these unwanted packets up. > 2) Tx path: driver will reject packets that would trigger STA > disassociation perfromed by a firmware (possible local DoS attack). > > It appears there are some Broadcom's clients/users who care about this > feature despite the drawbacks. They can switch it on by a newly added > Kconfig option. > > Signed-off-by: Rafa? Mi?ecki <rafal at milecki.pl>[...]> --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig > +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig > @@ -68,6 +68,26 @@ config BRCMFMAC_PCIE > IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to > use the driver for an PCIE wireless card. > > +config BRCMFMAC_IAPP > + bool "Partial support for obsoleted Inter-Access Point Protocol" > + depends on BRCMFMAC > + ---help--- > + Most of Broadcom's firmwares can send 802.11f ADD frame every > + time new STA connects to the AP interface. Some recent ones > + can also disassociate STA when they receive such a frame. > + > + It's important to understand this behavior can lead to a local > + DoS security issue. Attacker may trigger disassociation of any > + STA by sending a proper Ethernet frame to the wireless > + interface. > + > + Moreover this feature may break AP interfaces in some specific > + setups. This applies e.g. to the bridge with hairpin mode > + enabled and IFLA_BRPORT_MCAST_TO_UCAST set. IAPP packet > + generated by a firmware will get passed back to the wireless > + interface and cause immediate disassociation of just-connected > + STA.Sorry for jumping late, but does it really make sense to have a Kconfig option for this? I don't think we should add a Kconfig option for every strange feature, there should be stronger reasons (size savings etc) before adding a Kconfig option. And in this case the size savings can't be much. Wouldn't a module parameter be simpler for a functionality change like this?> +/** > + * brcmf_skb_is_iapp - checks if skb is an IAPP packet > + * > + * @skb: skb to check > + */ > +static bool brcmf_skb_is_iapp(struct sk_buff *skb) > +{ > + const u8 iapp_l2_update_packet[6] __aligned(2) = { > + 0x00, 0x01, 0xaf, 0x81, 0x01, 0x00, > + };static?> + unsigned char *eth_data = skb_mac_header(skb) + ETH_HLEN; > +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)#ifndef?> + const u16 *a = (const u16 *)eth_data; > + const u16 *b = (const u16 *)iapp_l2_update_packet; > +#endif > + > + if (skb->len - skb->mac_len != 6 || > + !is_multicast_ether_addr(eth_hdr(skb)->h_dest)) > + return false; > + > +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)#ifdef? -- Kalle Valo
Stephen Hemminger
2018-Mar-14 15:08 UTC
[Bridge] [PATCH] brcmfmac: drop Inter-Access Point Protocol packets by default
On Wed, 14 Mar 2018 12:01:19 +0100 Rafa? Mi?ecki <zajec5 at gmail.com> wrote:> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c > index 19048526b4af..db6987015fb1 100644 > --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c > +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c > @@ -230,6 +230,34 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev) > schedule_work(&ifp->multicast_work); > } > > +/** > + * brcmf_skb_is_iapp - checks if skb is an IAPP packet > + * > + * @skb: skb to check > + */ > +static bool brcmf_skb_is_iapp(struct sk_buff *skb) > +{ > + const u8 iapp_l2_update_packet[6] __aligned(2) = { > + 0x00, 0x01, 0xaf, 0x81, 0x01, 0x00, > + }; > + unsigned char *eth_data = skb_mac_header(skb) + ETH_HLEN; > +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) > + const u16 *a = (const u16 *)eth_data; > + const u16 *b = (const u16 *)iapp_l2_update_packet; > +#endif > + > + if (skb->len - skb->mac_len != 6 || > + !is_multicast_ether_addr(eth_hdr(skb)->h_dest)) > + return false; > + > +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) > + return !(((*(const u32 *)eth_data) ^ (*(const u32 *)iapp_l2_update_packet)) | > + ((*(const u16 *)(eth_data + 4)) ^ (*(const u16 *)(iapp_l2_update_packet + 4)))); > +#else > + return !((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])); > +#endif > +} > + > static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, > struct net_device *ndev) > { > @@ -250,6 +278,12 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, > goto done; > } > > + if (!IS_ENABLED(CONFIG_BRCMFMAC_IAPP) && brcmf_skb_is_iapp(skb)) { > + dev_kfree_skb(skb); > + ret = -EINVAL; > + goto done; > + } > +The usual way to handle config options in kernel is either inline stub function or #define #ifdef CONFIG_BRFMMAC_IAPP static bool brcmf_skb_is_app(...) { real code } #else #define brcmf_skb_is_app (false) #endif