diff options
Diffstat (limited to 'net/ipv6/mcast.c')
-rw-r--r-- | net/ipv6/mcast.c | 40 |
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 | ||
135 | int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | 135 | int __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 | } | ||
202 | EXPORT_SYMBOL(__ipv6_sock_mc_join); | ||
203 | |||
204 | int 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 | } |
214 | EXPORT_SYMBOL(ipv6_sock_mc_join); | ||
205 | 215 | ||
206 | /* | 216 | /* |
207 | * socket leave on multicast group | 217 | * socket leave on multicast group |
208 | */ | 218 | */ |
209 | int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | 219 | int __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 | } |
258 | EXPORT_SYMBOL(__ipv6_sock_mc_drop); | ||
259 | |||
260 | int 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 | } | ||
270 | EXPORT_SYMBOL(ipv6_sock_mc_drop); | ||
249 | 271 | ||
250 | /* called with rcu_read_lock() */ | 272 | /* called with rcu_read_lock() */ |
251 | static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, | 273 | static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, |