b1f5505e7370fc0488cbf7d20677ed2231e4aa20
[linux-2.6-microblaze.git] / drivers / net / vxlan / vxlan_multicast.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *      Vxlan multicast group handling
4  *
5  */
6 #include <linux/kernel.h>
7 #include <net/net_namespace.h>
8 #include <net/sock.h>
9 #include <linux/igmp.h>
10 #include <net/vxlan.h>
11
12 #include "vxlan_private.h"
13
14 /* Update multicast group membership when first VNI on
15  * multicast address is brought up
16  */
17 int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,
18                     int rifindex)
19 {
20         union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip);
21         int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex);
22         int ret = -EINVAL;
23         struct sock *sk;
24
25         if (ip->sa.sa_family == AF_INET) {
26                 struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
27                 struct ip_mreqn mreq = {
28                         .imr_multiaddr.s_addr   = ip->sin.sin_addr.s_addr,
29                         .imr_ifindex            = ifindex,
30                 };
31
32                 sk = sock4->sock->sk;
33                 lock_sock(sk);
34                 ret = ip_mc_join_group(sk, &mreq);
35                 release_sock(sk);
36 #if IS_ENABLED(CONFIG_IPV6)
37         } else {
38                 struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
39
40                 sk = sock6->sock->sk;
41                 lock_sock(sk);
42                 ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex,
43                                                    &ip->sin6.sin6_addr);
44                 release_sock(sk);
45 #endif
46         }
47
48         return ret;
49 }
50
51 int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
52                      int rifindex)
53 {
54         union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip);
55         int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex);
56         int ret = -EINVAL;
57         struct sock *sk;
58
59         if (ip->sa.sa_family == AF_INET) {
60                 struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
61                 struct ip_mreqn mreq = {
62                         .imr_multiaddr.s_addr   = ip->sin.sin_addr.s_addr,
63                         .imr_ifindex            = ifindex,
64                 };
65
66                 sk = sock4->sock->sk;
67                 lock_sock(sk);
68                 ret = ip_mc_leave_group(sk, &mreq);
69                 release_sock(sk);
70 #if IS_ENABLED(CONFIG_IPV6)
71         } else {
72                 struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
73
74                 sk = sock6->sock->sk;
75                 lock_sock(sk);
76                 ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex,
77                                                    &ip->sin6.sin6_addr);
78                 release_sock(sk);
79 #endif
80         }
81
82         return ret;
83 }
84
85 /* See if multicast group is already in use by other ID */
86 bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
87                       union vxlan_addr *rip, int rifindex)
88 {
89         union vxlan_addr *ip = (rip ? : &dev->default_dst.remote_ip);
90         int ifindex = (rifindex ? : dev->default_dst.remote_ifindex);
91         struct vxlan_dev *vxlan;
92         struct vxlan_sock *sock4;
93 #if IS_ENABLED(CONFIG_IPV6)
94         struct vxlan_sock *sock6;
95 #endif
96         unsigned short family = dev->default_dst.remote_ip.sa.sa_family;
97
98         sock4 = rtnl_dereference(dev->vn4_sock);
99
100         /* The vxlan_sock is only used by dev, leaving group has
101          * no effect on other vxlan devices.
102          */
103         if (family == AF_INET && sock4 && refcount_read(&sock4->refcnt) == 1)
104                 return false;
105
106 #if IS_ENABLED(CONFIG_IPV6)
107         sock6 = rtnl_dereference(dev->vn6_sock);
108         if (family == AF_INET6 && sock6 && refcount_read(&sock6->refcnt) == 1)
109                 return false;
110 #endif
111
112         list_for_each_entry(vxlan, &vn->vxlan_list, next) {
113                 if (!netif_running(vxlan->dev) || vxlan == dev)
114                         continue;
115
116                 if (family == AF_INET &&
117                     rtnl_dereference(vxlan->vn4_sock) != sock4)
118                         continue;
119 #if IS_ENABLED(CONFIG_IPV6)
120                 if (family == AF_INET6 &&
121                     rtnl_dereference(vxlan->vn6_sock) != sock6)
122                         continue;
123 #endif
124                 if (!vxlan_addr_equal(&vxlan->default_dst.remote_ip, ip))
125                         continue;
126
127                 if (vxlan->default_dst.remote_ifindex != ifindex)
128                         continue;
129
130                 return true;
131         }
132
133         return false;
134 }