igmp v6: add __ipv6_sock_mc_join and __ipv6_sock_mc_drop
[cascardo/linux.git] / net / ipv6 / mcast.c
index 5ce107c..e4955d0 100644 (file)
@@ -132,7 +132,7 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
        return iv > 0 ? iv : 1;
 }
 
-int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
+int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
 {
        struct net_device *dev = NULL;
        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)
        struct net *net = sock_net(sk);
        int err;
 
+       ASSERT_RTNL();
+
        if (!ipv6_addr_is_multicast(addr))
                return -EINVAL;
 
@@ -161,7 +163,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
        mc_lst->next = NULL;
        mc_lst->addr = *addr;
 
-       rtnl_lock();
        if (ifindex == 0) {
                struct rt6_info *rt;
                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)
                dev = __dev_get_by_index(net, ifindex);
 
        if (dev == NULL) {
-               rtnl_unlock();
                sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
                return -ENODEV;
        }
@@ -190,7 +190,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
        err = ipv6_dev_mc_inc(dev, addr);
 
        if (err) {
-               rtnl_unlock();
                sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
                return err;
        }
@@ -198,25 +197,37 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
        mc_lst->next = np->ipv6_mc_list;
        rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
 
+       return 0;
+}
+EXPORT_SYMBOL(__ipv6_sock_mc_join);
+
+int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
+{
+       int ret;
+
+       rtnl_lock();
+       ret = __ipv6_sock_mc_join(sk, ifindex, addr);
        rtnl_unlock();
 
-       return 0;
+       return ret;
 }
+EXPORT_SYMBOL(ipv6_sock_mc_join);
 
 /*
  *     socket leave on multicast group
  */
-int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
+int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct ipv6_mc_socklist *mc_lst;
        struct ipv6_mc_socklist __rcu **lnk;
        struct net *net = sock_net(sk);
 
+       ASSERT_RTNL();
+
        if (!ipv6_addr_is_multicast(addr))
                return -EINVAL;
 
-       rtnl_lock();
        for (lnk = &np->ipv6_mc_list;
             (mc_lst = rtnl_dereference(*lnk)) != NULL;
              lnk = &mc_lst->next) {
@@ -235,17 +246,28 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
                                        __ipv6_dev_mc_dec(idev, &mc_lst->addr);
                        } else
                                (void) ip6_mc_leave_src(sk, mc_lst, NULL);
-                       rtnl_unlock();
 
                        atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
                        kfree_rcu(mc_lst, rcu);
                        return 0;
                }
        }
-       rtnl_unlock();
 
        return -EADDRNOTAVAIL;
 }
+EXPORT_SYMBOL(__ipv6_sock_mc_drop);
+
+int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
+{
+       int ret;
+
+       rtnl_lock();
+       ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
+       rtnl_unlock();
+
+       return ret;
+}
+EXPORT_SYMBOL(ipv6_sock_mc_drop);
 
 /* called with rcu_read_lock() */
 static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,