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, imr: &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, imr: &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 | static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex, |
86 | union vxlan_addr *rip, int rifindex) |
87 | { |
88 | if (!vxlan_addr_multicast(ipa: rip)) |
89 | return false; |
90 | |
91 | if (!vxlan_addr_equal(a: rip, b: ip)) |
92 | return false; |
93 | |
94 | if (rifindex != ifindex) |
95 | return false; |
96 | |
97 | return true; |
98 | } |
99 | |
100 | static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan, |
101 | union vxlan_addr *ip, int ifindex) |
102 | { |
103 | struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); |
104 | struct vxlan_vni_node *v, *tmp; |
105 | |
106 | if (vxlan_group_used_match(ip, ifindex, |
107 | rip: &vxlan->default_dst.remote_ip, |
108 | rifindex: vxlan->default_dst.remote_ifindex)) |
109 | return true; |
110 | |
111 | list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { |
112 | if (!vxlan_addr_multicast(ipa: &v->remote_ip)) |
113 | continue; |
114 | |
115 | if (vxlan_group_used_match(ip, ifindex, |
116 | rip: &v->remote_ip, |
117 | rifindex: vxlan->default_dst.remote_ifindex)) |
118 | return true; |
119 | } |
120 | |
121 | return false; |
122 | } |
123 | |
124 | /* See if multicast group is already in use by other ID */ |
125 | bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, |
126 | __be32 vni, union vxlan_addr *rip, int rifindex) |
127 | { |
128 | union vxlan_addr *ip = (rip ? : &dev->default_dst.remote_ip); |
129 | int ifindex = (rifindex ? : dev->default_dst.remote_ifindex); |
130 | struct vxlan_dev *vxlan; |
131 | struct vxlan_sock *sock4; |
132 | #if IS_ENABLED(CONFIG_IPV6) |
133 | struct vxlan_sock *sock6; |
134 | #endif |
135 | unsigned short family = dev->default_dst.remote_ip.sa.sa_family; |
136 | |
137 | sock4 = rtnl_dereference(dev->vn4_sock); |
138 | |
139 | /* The vxlan_sock is only used by dev, leaving group has |
140 | * no effect on other vxlan devices. |
141 | */ |
142 | if (family == AF_INET && sock4 && refcount_read(r: &sock4->refcnt) == 1) |
143 | return false; |
144 | |
145 | #if IS_ENABLED(CONFIG_IPV6) |
146 | sock6 = rtnl_dereference(dev->vn6_sock); |
147 | if (family == AF_INET6 && sock6 && refcount_read(r: &sock6->refcnt) == 1) |
148 | return false; |
149 | #endif |
150 | |
151 | list_for_each_entry(vxlan, &vn->vxlan_list, next) { |
152 | if (!netif_running(dev: vxlan->dev) || vxlan == dev) |
153 | continue; |
154 | |
155 | if (family == AF_INET && |
156 | rtnl_dereference(vxlan->vn4_sock) != sock4) |
157 | continue; |
158 | #if IS_ENABLED(CONFIG_IPV6) |
159 | if (family == AF_INET6 && |
160 | rtnl_dereference(vxlan->vn6_sock) != sock6) |
161 | continue; |
162 | #endif |
163 | if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { |
164 | if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex)) |
165 | continue; |
166 | } else { |
167 | if (!vxlan_group_used_match(ip, ifindex, |
168 | rip: &vxlan->default_dst.remote_ip, |
169 | rifindex: vxlan->default_dst.remote_ifindex)) |
170 | continue; |
171 | } |
172 | |
173 | return true; |
174 | } |
175 | |
176 | return false; |
177 | } |
178 | |
179 | static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan) |
180 | { |
181 | struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); |
182 | struct vxlan_vni_node *v, *tmp, *vgood = NULL; |
183 | int ret = 0; |
184 | |
185 | list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { |
186 | if (!vxlan_addr_multicast(ipa: &v->remote_ip)) |
187 | continue; |
188 | /* skip if address is same as default address */ |
189 | if (vxlan_addr_equal(a: &v->remote_ip, |
190 | b: &vxlan->default_dst.remote_ip)) |
191 | continue; |
192 | ret = vxlan_igmp_join(vxlan, rip: &v->remote_ip, rifindex: 0); |
193 | if (ret == -EADDRINUSE) |
194 | ret = 0; |
195 | if (ret) |
196 | goto out; |
197 | vgood = v; |
198 | } |
199 | out: |
200 | if (ret) { |
201 | list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { |
202 | if (!vxlan_addr_multicast(ipa: &v->remote_ip)) |
203 | continue; |
204 | if (vxlan_addr_equal(a: &v->remote_ip, |
205 | b: &vxlan->default_dst.remote_ip)) |
206 | continue; |
207 | vxlan_igmp_leave(vxlan, rip: &v->remote_ip, rifindex: 0); |
208 | if (v == vgood) |
209 | break; |
210 | } |
211 | } |
212 | |
213 | return ret; |
214 | } |
215 | |
216 | static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan) |
217 | { |
218 | struct vxlan_net *vn = net_generic(net: vxlan->net, id: vxlan_net_id); |
219 | struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); |
220 | struct vxlan_vni_node *v, *tmp; |
221 | int last_err = 0, ret; |
222 | |
223 | list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { |
224 | if (vxlan_addr_multicast(ipa: &v->remote_ip) && |
225 | !vxlan_group_used(vn, dev: vxlan, vni: v->vni, rip: &v->remote_ip, |
226 | rifindex: 0)) { |
227 | ret = vxlan_igmp_leave(vxlan, rip: &v->remote_ip, rifindex: 0); |
228 | if (ret) |
229 | last_err = ret; |
230 | } |
231 | } |
232 | |
233 | return last_err; |
234 | } |
235 | |
236 | int vxlan_multicast_join(struct vxlan_dev *vxlan) |
237 | { |
238 | int ret = 0; |
239 | |
240 | if (vxlan_addr_multicast(ipa: &vxlan->default_dst.remote_ip)) { |
241 | ret = vxlan_igmp_join(vxlan, rip: &vxlan->default_dst.remote_ip, |
242 | rifindex: vxlan->default_dst.remote_ifindex); |
243 | if (ret == -EADDRINUSE) |
244 | ret = 0; |
245 | if (ret) |
246 | return ret; |
247 | } |
248 | |
249 | if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) |
250 | return vxlan_multicast_join_vnigrp(vxlan); |
251 | |
252 | return 0; |
253 | } |
254 | |
255 | int vxlan_multicast_leave(struct vxlan_dev *vxlan) |
256 | { |
257 | struct vxlan_net *vn = net_generic(net: vxlan->net, id: vxlan_net_id); |
258 | int ret = 0; |
259 | |
260 | if (vxlan_addr_multicast(ipa: &vxlan->default_dst.remote_ip) && |
261 | !vxlan_group_used(vn, dev: vxlan, vni: 0, NULL, rifindex: 0)) { |
262 | ret = vxlan_igmp_leave(vxlan, rip: &vxlan->default_dst.remote_ip, |
263 | rifindex: vxlan->default_dst.remote_ifindex); |
264 | if (ret) |
265 | return ret; |
266 | } |
267 | |
268 | if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) |
269 | return vxlan_multicast_leave_vnigrp(vxlan); |
270 | |
271 | return 0; |
272 | } |
273 | |