]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/vxlan.c
Merge tag 'renesas-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[karo-tx-linux.git] / drivers / net / vxlan.c
index 6b560f373fc373db6fedaba728e2f10095cd3296..bf64b4191dcc379f8d0c3d9ec287bb998efa6a69 100644 (file)
@@ -478,7 +478,7 @@ static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan,
        struct vxlan_fdb *f;
 
        hlist_for_each_entry_rcu(f, head, hlist) {
-               if (compare_ether_addr(mac, f->eth_addr) == 0)
+               if (ether_addr_equal(mac, f->eth_addr))
                        return f;
        }
 
@@ -558,6 +558,40 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
        return 1;
 }
 
+/* Notify netdevs that UDP port started listening */
+static void vxlan_notify_add_rx_port(struct sock *sk)
+{
+       struct net_device *dev;
+       struct net *net = sock_net(sk);
+       sa_family_t sa_family = sk->sk_family;
+       u16 port = htons(inet_sk(sk)->inet_sport);
+
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
+               if (dev->netdev_ops->ndo_add_vxlan_port)
+                       dev->netdev_ops->ndo_add_vxlan_port(dev, sa_family,
+                                                           port);
+       }
+       rcu_read_unlock();
+}
+
+/* Notify netdevs that UDP port is no more listening */
+static void vxlan_notify_del_rx_port(struct sock *sk)
+{
+       struct net_device *dev;
+       struct net *net = sock_net(sk);
+       sa_family_t sa_family = sk->sk_family;
+       u16 port = htons(inet_sk(sk)->inet_sport);
+
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
+               if (dev->netdev_ops->ndo_del_vxlan_port)
+                       dev->netdev_ops->ndo_del_vxlan_port(dev, sa_family,
+                                                           port);
+       }
+       rcu_read_unlock();
+}
+
 /* Add new entry to forwarding table -- assumes lock held */
 static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                            const u8 *mac, union vxlan_addr *ip,
@@ -909,13 +943,18 @@ static void vxlan_sock_hold(struct vxlan_sock *vs)
 
 void vxlan_sock_release(struct vxlan_sock *vs)
 {
-       struct vxlan_net *vn = net_generic(sock_net(vs->sock->sk), vxlan_net_id);
+       struct sock *sk = vs->sock->sk;
+       struct net *net = sock_net(sk);
+       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
 
        if (!atomic_dec_and_test(&vs->refcnt))
                return;
 
        spin_lock(&vn->sock_lock);
        hlist_del_rcu(&vs->hlist);
+       smp_wmb();
+       vs->sock->sk->sk_user_data = NULL;
+       vxlan_notify_del_rx_port(sk);
        spin_unlock(&vn->sock_lock);
 
        queue_work(vxlan_wq, &vs->del_work);
@@ -1009,7 +1048,8 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
        port = inet_sk(sk)->inet_sport;
 
-       vs = vxlan_find_sock(sock_net(sk), port);
+       smp_read_barrier_depends();
+       vs = (struct vxlan_sock *)sk->sk_user_data;
        if (!vs)
                goto drop;
 
@@ -1049,8 +1089,7 @@ static void vxlan_rcv(struct vxlan_sock *vs,
        skb->protocol = eth_type_trans(skb, vxlan->dev);
 
        /* Ignore packet loops (and multicast echo) */
-       if (compare_ether_addr(eth_hdr(skb)->h_source,
-                              vxlan->dev->dev_addr) == 0)
+       if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr))
                goto drop;
 
        /* Re-examine inner Ethernet packet */
@@ -1320,7 +1359,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
        if (n) {
                bool diff;
 
-               diff = compare_ether_addr(eth_hdr(skb)->h_dest, n->ha) != 0;
+               diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha);
                if (diff) {
                        memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
                                dev->addr_len);
@@ -1381,7 +1420,7 @@ static int handle_offloads(struct sk_buff *skb)
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static int vxlan6_xmit_skb(struct net *net, struct vxlan_sock *vs,
+static int vxlan6_xmit_skb(struct vxlan_sock *vs,
                           struct dst_entry *dst, struct sk_buff *skb,
                           struct net_device *dev, struct in6_addr *saddr,
                           struct in6_addr *daddr, __u8 prio, __u8 ttl,
@@ -1398,6 +1437,8 @@ static int vxlan6_xmit_skb(struct net *net, struct vxlan_sock *vs,
                skb->encapsulation = 1;
        }
 
+       skb_scrub_packet(skb, false);
+
        min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
                        + VXLAN_HLEN + sizeof(struct ipv6hdr)
                        + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
@@ -1433,7 +1474,6 @@ static int vxlan6_xmit_skb(struct net *net, struct vxlan_sock *vs,
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
-       skb_dst_drop(skb);
        skb_dst_set(skb, dst);
 
        if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) {
@@ -1476,7 +1516,7 @@ static int vxlan6_xmit_skb(struct net *net, struct vxlan_sock *vs,
 }
 #endif
 
-int vxlan_xmit_skb(struct net *net, struct vxlan_sock *vs,
+int vxlan_xmit_skb(struct vxlan_sock *vs,
                   struct rtable *rt, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
                   __be16 src_port, __be16 dst_port, __be32 vni)
@@ -1529,8 +1569,8 @@ int vxlan_xmit_skb(struct net *net, struct vxlan_sock *vs,
        if (err)
                return err;
 
-       return iptunnel_xmit(net, rt, skb, src, dst,
-                       IPPROTO_UDP, tos, ttl, df);
+       return iptunnel_xmit(rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df,
+                            false);
 }
 EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
 
@@ -1653,7 +1693,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
                ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 
-               err = vxlan_xmit_skb(dev_net(dev), vxlan->vn_sock, rt, skb,
+               err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
                                     fl4.saddr, dst->sin.sin_addr.s_addr,
                                     tos, ttl, df, src_port, dst_port,
                                     htonl(vni << 8));
@@ -1705,7 +1745,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 
                ttl = ttl ? : ip6_dst_hoplimit(ndst);
 
-               err = vxlan6_xmit_skb(dev_net(dev), vxlan->vn_sock, ndst, skb,
+               err = vxlan6_xmit_skb(vxlan->vn_sock, ndst, skb,
                                      dev, &fl6.saddr, &fl6.daddr, 0, ttl,
                                      src_port, dst_port, htonl(vni << 8));
 #endif
@@ -1980,6 +2020,34 @@ static struct device_type vxlan_type = {
        .name = "vxlan",
 };
 
+/* Calls the ndo_add_vxlan_port of the caller in order to
+ * supply the listening VXLAN udp ports.
+ */
+void vxlan_get_rx_port(struct net_device *dev)
+{
+       struct vxlan_sock *vs;
+       struct net *net = dev_net(dev);
+       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+       sa_family_t sa_family;
+       u16 port;
+       int i;
+
+       if (!dev || !dev->netdev_ops || !dev->netdev_ops->ndo_add_vxlan_port)
+               return;
+
+       spin_lock(&vn->sock_lock);
+       for (i = 0; i < PORT_HASH_SIZE; ++i) {
+               hlist_for_each_entry_rcu(vs, vs_head(net, i), hlist) {
+                       port = htons(inet_sk(vs->sock->sk)->inet_sport);
+                       sa_family = vs->sock->sk->sk_family;
+                       dev->netdev_ops->ndo_add_vxlan_port(dev, sa_family,
+                                                           port);
+               }
+       }
+       spin_unlock(&vn->sock_lock);
+}
+EXPORT_SYMBOL_GPL(vxlan_get_rx_port);
+
 /* Initialize the device structure. */
 static void vxlan_setup(struct net_device *dev)
 {
@@ -2236,9 +2304,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        atomic_set(&vs->refcnt, 1);
        vs->rcv = rcv;
        vs->data = data;
+       smp_wmb();
+       vs->sock->sk->sk_user_data = vs;
 
        spin_lock(&vn->sock_lock);
        hlist_add_head_rcu(&vs->hlist, vs_head(net, port));
+       vxlan_notify_add_rx_port(sk);
        spin_unlock(&vn->sock_lock);
 
        /* Mark socket as an encapsulation socket. */