]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/vxlan.c
vxlan: add ipv6 proxy support
[karo-tx-linux.git] / drivers / net / vxlan.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;