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;

	for (lnk = &np->ipv6_mc_list;
	     (mc_lst = sock_dereference(*lnk, sk)) != NULL;
	      lnk = &mc_lst->next) {
		if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
		    ipv6_addr_equal(&mc_lst->addr, addr)) {
			struct net_device *dev;

			*lnk = mc_lst->next;

			dev = __dev_get_by_index(net, mc_lst->ifindex);
			if (dev) {
				struct inet6_dev *idev = __in6_dev_get(dev);

				ip6_mc_leave_src(sk, mc_lst, idev);
				if (idev)
					__ipv6_dev_mc_dec(idev, &mc_lst->addr);
			} else {
				ip6_mc_leave_src(sk, mc_lst, NULL);
			}

			atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
			kfree_rcu(mc_lst, rcu);
			return 0;
		}
	}

	return -EADDRNOTAVAIL;
}
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;

	for (lnk = &np->ipv6_mc_list;
	     (mc_lst = sock_dereference(*lnk, sk)) != NULL;
	      lnk = &mc_lst->next) {
		if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
		    ipv6_addr_equal(&mc_lst->addr, addr)) {
			struct net_device *dev;

			*lnk = mc_lst->next;

			dev = __dev_get_by_index(net, mc_lst->ifindex);
			if (dev) {
				struct inet6_dev *idev = __in6_dev_get(dev);

				ip6_mc_leave_src(sk, mc_lst, idev);
				if (idev)
					__ipv6_dev_mc_dec(idev, &mc_lst->addr);
			} else {
				ip6_mc_leave_src(sk, mc_lst, NULL);
			}

			atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
			kfree_rcu(mc_lst, rcu);
			return 0;
		}
	}

	return -EADDRNOTAVAIL;
}