aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/mcast.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/mcast.c')
-rw-r--r--net/ipv6/mcast.c40
1 files changed, 31 insertions, 9 deletions
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 5ce107c8aab3..e4955d019734 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -132,7 +132,7 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
132 return iv > 0 ? iv : 1; 132 return iv > 0 ? iv : 1;
133} 133}
134 134
135int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) 135int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
136{ 136{
137 struct net_device *dev = NULL; 137 struct net_device *dev = NULL;
138 struct ipv6_mc_socklist *mc_lst; 138 struct ipv6_mc_socklist *mc_lst;
@@ -140,6 +140,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
140 struct net *net = sock_net(sk); 140 struct net *net = sock_net(sk);
141 int err; 141 int err;
142 142
143 ASSERT_RTNL();
144
143 if (!ipv6_addr_is_multicast(addr)) 145 if (!ipv6_addr_is_multicast(addr))
144 return -EINVAL; 146 return -EINVAL;
145 147
@@ -161,7 +163,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
161 mc_lst->next = NULL; 163 mc_lst->next = NULL;
162 mc_lst->addr = *addr; 164 mc_lst->addr = *addr;
163 165
164 rtnl_lock();
165 if (ifindex == 0) { 166 if (ifindex == 0) {
166 struct rt6_info *rt; 167 struct rt6_info *rt;
167 rt = rt6_lookup(net, addr, NULL, 0, 0); 168 rt = rt6_lookup(net, addr, NULL, 0, 0);
@@ -173,7 +174,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
173 dev = __dev_get_by_index(net, ifindex); 174 dev = __dev_get_by_index(net, ifindex);
174 175
175 if (dev == NULL) { 176 if (dev == NULL) {
176 rtnl_unlock();
177 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 177 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
178 return -ENODEV; 178 return -ENODEV;
179 } 179 }
@@ -190,7 +190,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
190 err = ipv6_dev_mc_inc(dev, addr); 190 err = ipv6_dev_mc_inc(dev, addr);
191 191
192 if (err) { 192 if (err) {
193 rtnl_unlock();
194 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 193 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
195 return err; 194 return err;
196 } 195 }
@@ -198,25 +197,37 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
198 mc_lst->next = np->ipv6_mc_list; 197 mc_lst->next = np->ipv6_mc_list;
199 rcu_assign_pointer(np->ipv6_mc_list, mc_lst); 198 rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
200 199
200 return 0;
201}
202EXPORT_SYMBOL(__ipv6_sock_mc_join);
203
204int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
205{
206 int ret;
207
208 rtnl_lock();
209 ret = __ipv6_sock_mc_join(sk, ifindex, addr);
201 rtnl_unlock(); 210 rtnl_unlock();
202 211
203 return 0; 212 return ret;
204} 213}
214EXPORT_SYMBOL(ipv6_sock_mc_join);
205 215
206/* 216/*
207 * socket leave on multicast group 217 * socket leave on multicast group
208 */ 218 */
209int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) 219int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
210{ 220{
211 struct ipv6_pinfo *np = inet6_sk(sk); 221 struct ipv6_pinfo *np = inet6_sk(sk);
212 struct ipv6_mc_socklist *mc_lst; 222 struct ipv6_mc_socklist *mc_lst;
213 struct ipv6_mc_socklist __rcu **lnk; 223 struct ipv6_mc_socklist __rcu **lnk;
214 struct net *net = sock_net(sk); 224 struct net *net = sock_net(sk);
215 225
226 ASSERT_RTNL();
227
216 if (!ipv6_addr_is_multicast(addr)) 228 if (!ipv6_addr_is_multicast(addr))
217 return -EINVAL; 229 return -EINVAL;
218 230
219 rtnl_lock();
220 for (lnk = &np->ipv6_mc_list; 231 for (lnk = &np->ipv6_mc_list;
221 (mc_lst = rtnl_dereference(*lnk)) != NULL; 232 (mc_lst = rtnl_dereference(*lnk)) != NULL;
222 lnk = &mc_lst->next) { 233 lnk = &mc_lst->next) {
@@ -235,17 +246,28 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
235 __ipv6_dev_mc_dec(idev, &mc_lst->addr); 246 __ipv6_dev_mc_dec(idev, &mc_lst->addr);
236 } else 247 } else
237 (void) ip6_mc_leave_src(sk, mc_lst, NULL); 248 (void) ip6_mc_leave_src(sk, mc_lst, NULL);
238 rtnl_unlock();
239 249
240 atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); 250 atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
241 kfree_rcu(mc_lst, rcu); 251 kfree_rcu(mc_lst, rcu);
242 return 0; 252 return 0;
243 } 253 }
244 } 254 }
245 rtnl_unlock();
246 255
247 return -EADDRNOTAVAIL; 256 return -EADDRNOTAVAIL;
248} 257}
258EXPORT_SYMBOL(__ipv6_sock_mc_drop);
259
260int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
261{
262 int ret;
263
264 rtnl_lock();
265 ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
266 rtnl_unlock();
267
268 return ret;
269}
270EXPORT_SYMBOL(ipv6_sock_mc_drop);
249 271
250/* called with rcu_read_lock() */ 272/* called with rcu_read_lock() */
251static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, 273static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,