vxlan: add ipv6 proxy support
authorCong Wang <amwang@redhat.com>
Sat, 31 Aug 2013 05:44:36 +0000 (13:44 +0800)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Sep 2013 02:30:01 +0000 (22:30 -0400)
This patch adds the IPv6 version of "arp_reduce", ndisc_send_na()
will be needed.

Cc: David S. Miller <davem@davemloft.net>
Cc: David Stevens <dlstevens@us.ibm.com>
Signed-off-by: Cong Wang <amwang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vxlan.c
include/net/addrconf.h
include/net/ndisc.h
net/ipv6/af_inet6.c
net/ipv6/ndisc.c

index c833763fe5c49924c2ad88c50564e660af4e9713..3ffb22d684a991b4b4f2ff50692dd5b86216628a 100644 (file)
@@ -1196,6 +1196,70 @@ out:
        return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct neighbour *n;
+       union vxlan_addr ipa;
+       const struct ipv6hdr *iphdr;
+       const struct in6_addr *saddr, *daddr;
+       struct nd_msg *msg;
+       struct inet6_dev *in6_dev = NULL;
+
+       in6_dev = __in6_dev_get(dev);
+       if (!in6_dev)
+               goto out;
+
+       if (!pskb_may_pull(skb, skb->len))
+               goto out;
+
+       iphdr = ipv6_hdr(skb);
+       saddr = &iphdr->saddr;
+       daddr = &iphdr->daddr;
+
+       if (ipv6_addr_loopback(daddr) ||
+           ipv6_addr_is_multicast(daddr))
+               goto out;
+
+       msg = (struct nd_msg *)skb_transport_header(skb);
+       if (msg->icmph.icmp6_code != 0 ||
+           msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
+               goto out;
+
+       n = neigh_lookup(ipv6_stub->nd_tbl, daddr, dev);
+
+       if (n) {
+               struct vxlan_fdb *f;
+
+               if (!(n->nud_state & NUD_CONNECTED)) {
+                       neigh_release(n);
+                       goto out;
+               }
+
+               f = vxlan_find_mac(vxlan, n->ha);
+               if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) {
+                       /* bridge-local neighbor */
+                       neigh_release(n);
+                       goto out;
+               }
+
+               ipv6_stub->ndisc_send_na(dev, n, saddr, &msg->target,
+                                        !!in6_dev->cnf.forwarding,
+                                        true, false, false);
+               neigh_release(n);
+       } else if (vxlan->flags & VXLAN_F_L3MISS) {
+               ipa.sin6.sin6_addr = *daddr;
+               ipa.sa.sa_family = AF_INET6;
+               vxlan_ip_miss(dev, &ipa);
+       }
+
+out:
+       consume_skb(skb);
+       return NETDEV_TX_OK;
+}
+#endif
+
 static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
@@ -1677,8 +1741,22 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
 
-       if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP)
-               return arp_reduce(dev, skb);
+       if ((vxlan->flags & VXLAN_F_PROXY)) {
+               if (ntohs(eth->h_proto) == ETH_P_ARP)
+                       return arp_reduce(dev, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+               else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
+                        skb->len >= sizeof(struct ipv6hdr) + sizeof(struct nd_msg) &&
+                        ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
+                               struct nd_msg *msg;
+
+                               msg = (struct nd_msg *)skb_transport_header(skb);
+                               if (msg->icmph.icmp6_code == 0 &&
+                                   msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
+                                       return neigh_reduce(dev, skb);
+               }
+#endif
+       }
 
        f = vxlan_find_mac(vxlan, eth->h_dest);
        did_rsc = false;
index bcf957341b62c8fc9c43bbad7e64f286dc889df7..fb314de2b61ba228059b1f09cb916d7d275a6a9b 100644 (file)
@@ -153,6 +153,10 @@ struct ipv6_stub {
        int (*ipv6_dst_lookup)(struct sock *sk, struct dst_entry **dst,
                                struct flowi6 *fl6);
        void (*udpv6_encap_enable)(void);
+       void (*ndisc_send_na)(struct net_device *dev, struct neighbour *neigh,
+                             const struct in6_addr *daddr,
+                             const struct in6_addr *solicited_addr,
+                             bool router, bool solicited, bool override, bool inc_opt);
        struct neigh_table *nd_tbl;
 };
 extern const struct ipv6_stub *ipv6_stub __read_mostly;
index 6fea32340ae8a5c697a18b7909c9b44d86cddeaf..3c4211f0bed60fe9035c3fc12c7190c6c564918c 100644 (file)
@@ -204,6 +204,11 @@ extern void                        ndisc_send_ns(struct net_device *dev,
 extern void                    ndisc_send_rs(struct net_device *dev,
                                              const struct in6_addr *saddr,
                                              const struct in6_addr *daddr);
+extern void                    ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
+                                             const struct in6_addr *daddr,
+                                             const struct in6_addr *solicited_addr,
+                                             bool router, bool solicited, bool override,
+                                             bool inc_opt);
 
 extern void                    ndisc_send_redirect(struct sk_buff *skb,
                                                    const struct in6_addr *target);
index 1996a7c34f73280acb2ab18951c6945ffb10f3a2..136fe55c1a471c72a959fbb33add244416fc93f6 100644 (file)
@@ -56,6 +56,7 @@
 #include <net/transp_v6.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
+#include <net/ndisc.h>
 #ifdef CONFIG_IPV6_TUNNEL
 #include <net/ip6_tunnel.h>
 #endif
@@ -815,6 +816,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
        .ipv6_sock_mc_drop = ipv6_sock_mc_drop,
        .ipv6_dst_lookup = ip6_dst_lookup,
        .udpv6_encap_enable = udpv6_encap_enable,
+       .ndisc_send_na = ndisc_send_na,
        .nd_tbl = &nd_tbl,
 };
 
index bb6fd95a5cd164c1ea0cf3b503222b913fe80cdb..14bd2f9d9dbbbcc9653c6bfb43a4c70827c04741 100644 (file)
@@ -461,10 +461,10 @@ static void ndisc_send_skb(struct sk_buff *skb,
        rcu_read_unlock();
 }
 
-static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
-                         const struct in6_addr *daddr,
-                         const struct in6_addr *solicited_addr,
-                         bool router, bool solicited, bool override, bool inc_opt)
+void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
+                  const struct in6_addr *daddr,
+                  const struct in6_addr *solicited_addr,
+                  bool router, bool solicited, bool override, bool inc_opt)
 {
        struct sk_buff *skb;
        struct in6_addr tmpaddr;