bridge: Add support for IEEE 802.11 Proxy ARP
authorKyeyoon Park <kyeyoonp@codeaurora.org>
Thu, 23 Oct 2014 21:49:17 +0000 (14:49 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 27 Oct 2014 23:02:04 +0000 (19:02 -0400)
This feature is defined in IEEE Std 802.11-2012, 10.23.13. It allows
the AP devices to keep track of the hardware-address-to-IP-address
mapping of the mobile devices within the WLAN network.

The AP will learn this mapping via observing DHCP, ARP, and NS/NA
frames. When a request for such information is made (i.e. ARP request,
Neighbor Solicitation), the AP will respond on behalf of the
associated mobile device. In the process of doing so, the AP will drop
the multicast request frame that was intended to go out to the wireless
medium.

It was recommended at the LKS workshop to do this implementation in
the bridge layer. vxlan.c is already doing something very similar.
The DHCP snooping code will be added to the userspace application
(hostapd) per the recommendation.

This RFC commit is only for IPv4. A similar approach in the bridge
layer will be taken for IPv6 as well.

Signed-off-by: Kyeyoon Park <kyeyoonp@codeaurora.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_link.h
net/bridge/br_forward.c
net/bridge/br_input.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_sysfs_if.c

index 0bdb77e16875342ad86fe165727a1034a395b731..7072d83250169c26d0c2a844122985777ea01e3f 100644 (file)
@@ -243,6 +243,7 @@ enum {
        IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave    */
        IFLA_BRPORT_LEARNING,   /* mac learning */
        IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */
+       IFLA_BRPORT_PROXYARP,   /* proxy ARP */
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
index 992ec49a96aa7e289bd3c74bca9a606762c63614..1510b54e6a2e06ffe5cf40a758f2b42e3864b73f 100644 (file)
@@ -183,6 +183,11 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
                /* Do not flood unicast traffic to ports that turn it off */
                if (unicast && !(p->flags & BR_FLOOD))
                        continue;
+
+               /* Do not flood to ports that enable proxy ARP */
+               if (p->flags & BR_PROXYARP)
+                       continue;
+
                prev = maybe_deliver(prev, p, skb, __packet_hook);
                if (IS_ERR(prev))
                        goto out;
index 6fd5522df696ce744558a4db82803c34394eed6f..1f1de715197c19ab12f5333e9a518a317754f041 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/netfilter_bridge.h>
+#include <linux/neighbour.h>
+#include <net/arp.h>
 #include <linux/export.h>
 #include <linux/rculist.h>
 #include "br_private.h"
@@ -57,6 +59,60 @@ static int br_pass_frame_up(struct sk_buff *skb)
                       netif_receive_skb);
 }
 
+static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
+                           u16 vid)
+{
+       struct net_device *dev = br->dev;
+       struct neighbour *n;
+       struct arphdr *parp;
+       u8 *arpptr, *sha;
+       __be32 sip, tip;
+
+       if (dev->flags & IFF_NOARP)
+               return;
+
+       if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
+               dev->stats.tx_dropped++;
+               return;
+       }
+       parp = arp_hdr(skb);
+
+       if (parp->ar_pro != htons(ETH_P_IP) ||
+           parp->ar_op != htons(ARPOP_REQUEST) ||
+           parp->ar_hln != dev->addr_len ||
+           parp->ar_pln != 4)
+               return;
+
+       arpptr = (u8 *)parp + sizeof(struct arphdr);
+       sha = arpptr;
+       arpptr += dev->addr_len;        /* sha */
+       memcpy(&sip, arpptr, sizeof(sip));
+       arpptr += sizeof(sip);
+       arpptr += dev->addr_len;        /* tha */
+       memcpy(&tip, arpptr, sizeof(tip));
+
+       if (ipv4_is_loopback(tip) ||
+           ipv4_is_multicast(tip))
+               return;
+
+       n = neigh_lookup(&arp_tbl, &tip, dev);
+       if (n) {
+               struct net_bridge_fdb_entry *f;
+
+               if (!(n->nud_state & NUD_VALID)) {
+                       neigh_release(n);
+                       return;
+               }
+
+               f = __br_fdb_get(br, n->ha, vid);
+               if (f)
+                       arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip,
+                                sha, n->ha, sha);
+
+               neigh_release(n);
+       }
+}
+
 /* note: already called with rcu_read_lock */
 int br_handle_frame_finish(struct sk_buff *skb)
 {
@@ -98,6 +154,10 @@ int br_handle_frame_finish(struct sk_buff *skb)
        dst = NULL;
 
        if (is_broadcast_ether_addr(dest)) {
+               if (p->flags & BR_PROXYARP &&
+                   skb->protocol == htons(ETH_P_ARP))
+                       br_do_proxy_arp(skb, br, vid);
+
                skb2 = skb;
                unicast = false;
        } else if (is_multicast_ether_addr(dest)) {
index 2ff9706647f2cb9f7930d22d0a3092fb3fe0d225..86c239b06f6e136f571ca0e74ccf3f9065ed095a 100644 (file)
@@ -60,7 +60,8 @@ static int br_port_fill_attrs(struct sk_buff *skb,
            nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) ||
            nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) ||
            nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) ||
-           nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)))
+           nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) ||
+           nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)))
                return -EMSGSIZE;
 
        return 0;
@@ -332,6 +333,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
        br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
        br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
        br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
+       br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
 
        if (tb[IFLA_BRPORT_COST]) {
                err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
index 4d783d071305a4d7aa683aaa74b57088db6142a8..8f3f081402586a3dddee01adc838b77f514db904 100644 (file)
@@ -172,6 +172,7 @@ struct net_bridge_port
 #define BR_FLOOD               0x00000040
 #define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING)
 #define BR_PROMISC             0x00000080
+#define BR_PROXYARP            0x00000100
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        struct bridge_mcast_own_query   ip4_own_query;
index e561cd59b8a6ef0e764b3028d350b13954deac05..2de5d91199e8172f9356b104bbcfa772ff460d45 100644 (file)
@@ -170,6 +170,7 @@ BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD);
 BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK);
 BRPORT_ATTR_FLAG(learning, BR_LEARNING);
 BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD);
+BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP);
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -213,6 +214,7 @@ static const struct brport_attribute *brport_attrs[] = {
        &brport_attr_multicast_router,
        &brport_attr_multicast_fast_leave,
 #endif
+       &brport_attr_proxyarp,
        NULL
 };