mwifiex: drop gratuitous ARP frames
authorAvinash Patil <patila@marvell.com>
Fri, 23 Aug 2013 23:48:23 +0000 (16:48 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 26 Aug 2013 18:09:05 +0000 (14:09 -0400)
This patch adds support for dropping gratuitous ARP frames which is
requirement for WFA Hotspot2.0.

Hotspot2.0 capability is enabled in driver if extended capabilities
IE from BSS descriptor has 11u interworking enabled.

Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_rx.c

index b579a2e54f4bd9ba3245051c5d32fc58400d982e..0b803c05cab3dae49be31b61b6cddb7e60674955 100644 (file)
@@ -399,6 +399,12 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
                       bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
                       le16_to_cpu(ext_cap->header.len));
 
+               if (hdr->len > 3 &&
+                   ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
+                       priv->hs2_enabled = true;
+               else
+                       priv->hs2_enabled = false;
+
                *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
                ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
        }
index ca149aea15175769386089c87c6d85035cd8c304..fbad00a5abc83502c3a16a7762f05bf6dcccf36a 100644 (file)
@@ -1508,6 +1508,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
                " reason code %d\n", priv->cfg_bssid, reason_code);
 
        memset(priv->cfg_bssid, 0, ETH_ALEN);
+       priv->hs2_enabled = false;
 
        return 0;
 }
index a5993475daef17b0aa09da324559bd8ea2789efa..5c85d7803d00a5856765544fc100cc38193f21a1 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/wait.h>
 #include <linux/timer.h>
 #include <linux/ieee80211.h>
+#include <uapi/linux/if_arp.h>
 #include <net/mac80211.h>
 
 
@@ -152,4 +153,12 @@ struct mwifiex_types_wmm_info {
        u8 reserved;
        struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
 } __packed;
+
+struct mwifiex_arp_eth_header {
+       struct arphdr hdr;
+       u8 ar_sha[ETH_ALEN];
+       u8 ar_sip[4];
+       u8 ar_tha[ETH_ALEN];
+       u8 ar_tip[4];
+} __packed;
 #endif /* !_MWIFIEX_DECL_H_ */
index e021a581a143872d231d6c77de60dad877bcfa2a..6499117fce43a3a73f36836c8d91d4e27b51b0ba 100644 (file)
@@ -136,6 +136,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
        priv->csa_chan = 0;
        priv->csa_expire_time = 0;
        priv->del_list_idx = 0;
+       priv->hs2_enabled = false;
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
index d2e5ccd891da2eebd7697bd1ac31989ccdc792ad..7d4d13779625e84c4b9aebe85af12e4e5d9e197c 100644 (file)
@@ -516,6 +516,7 @@ struct mwifiex_private {
        u8 csa_chan;
        unsigned long csa_expire_time;
        u8 del_list_idx;
+       bool hs2_enabled;
 };
 
 enum mwifiex_ba_status {
index b5c109504393d22a3bc5eae4a5510f47f4353b56..bb22664923efd24c84aaa5855a13f913b0418182 100644 (file)
@@ -17,6 +17,8 @@
  * this warranty disclaimer.
  */
 
+#include <uapi/linux/ipv6.h>
+#include <net/ndisc.h>
 #include "decl.h"
 #include "ioctl.h"
 #include "util.h"
 #include "11n_aggr.h"
 #include "11n_rxreorder.h"
 
+/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
+ * frame. If frame has both source and destination mac address as same, this
+ * function drops such gratuitous frames.
+ */
+static bool
+mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv,
+                              struct sk_buff *skb)
+{
+       const struct mwifiex_arp_eth_header *arp;
+       struct ethhdr *eth_hdr;
+       struct ipv6hdr *ipv6;
+       struct icmp6hdr *icmpv6;
+
+       eth_hdr = (struct ethhdr *)skb->data;
+       switch (ntohs(eth_hdr->h_proto)) {
+       case ETH_P_ARP:
+               arp = (void *)(skb->data + sizeof(struct ethhdr));
+               if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
+                   arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
+                       if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
+                               return true;
+               }
+               break;
+       case ETH_P_IPV6:
+               ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
+               icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
+                                 sizeof(struct ipv6hdr));
+               if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) {
+                       if (!memcmp(&ipv6->saddr, &ipv6->daddr,
+                                   sizeof(struct in6_addr)))
+                               return true;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return false;
+}
+
 /*
  * This function processes the received packet and forwards it
  * to kernel/upper layer.
@@ -90,6 +132,13 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
           either the reconstructed EthII frame or the 802.2/llc/snap frame */
        skb_pull(skb, hdr_chop);
 
+       if (priv->hs2_enabled &&
+           mwifiex_discard_gratuitous_arp(priv, skb)) {
+               dev_dbg(priv->adapter->dev, "Bypassed Gratuitous ARP\n");
+               dev_kfree_skb_any(skb);
+               return 0;
+       }
+
        priv->rxpd_rate = local_rx_pd->rx_rate;
 
        priv->rxpd_htinfo = local_rx_pd->ht_info;