Commit d5541e85 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David S. Miller
Browse files

net/ipv6: factor out MCAST_MSFILTER getsockopt helpers



Factor out one helper each for getting the native and compat
version of the MCAST_MSFILTER option.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b6238c04
Loading
Loading
Loading
Loading
+74 −65
Original line number Diff line number Diff line
@@ -1071,33 +1071,12 @@ static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
	return len;
}

static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
		    char __user *optval, int __user *optlen, unsigned int flags)
static int ipv6_get_msfilter(struct sock *sk, void __user *optval,
		int __user *optlen, int len)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	int len;
	int val;

	if (ip6_mroute_opt(optname))
		return ip6_mroute_getsockopt(sk, optname, optval, optlen);

	if (get_user(len, optlen))
		return -EFAULT;
	switch (optname) {
	case IPV6_ADDRFORM:
		if (sk->sk_protocol != IPPROTO_UDP &&
		    sk->sk_protocol != IPPROTO_UDPLITE &&
		    sk->sk_protocol != IPPROTO_TCP)
			return -ENOPROTOOPT;
		if (sk->sk_state != TCP_ESTABLISHED)
			return -ENOTCONN;
		val = sk->sk_family;
		break;
	case MCAST_MSFILTER:
	{
		struct group_filter __user *p = (void __user *)optval;
		struct group_filter gsf;
	const int size0 = offsetof(struct group_filter, gf_slist);
	struct group_filter __user *p = optval;
	struct group_filter gsf;
	int num;
	int err;

@@ -1121,6 +1100,72 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
	return err;
}

#ifdef CONFIG_COMPAT
static int compat_ipv6_get_msfilter(struct sock *sk, void __user *optval,
		int __user *optlen)
{
	const int size0 = offsetof(struct compat_group_filter, gf_slist);
	struct compat_group_filter __user *p = optval;
	struct compat_group_filter gf32;
	struct group_filter gf;
	int len, err;
	int num;

	if (get_user(len, optlen))
		return -EFAULT;
	if (len < size0)
		return -EINVAL;

	if (copy_from_user(&gf32, p, size0))
		return -EFAULT;
	gf.gf_interface = gf32.gf_interface;
	gf.gf_fmode = gf32.gf_fmode;
	num = gf.gf_numsrc = gf32.gf_numsrc;
	gf.gf_group = gf32.gf_group;

	if (gf.gf_group.ss_family != AF_INET6)
		return -EADDRNOTAVAIL;

	lock_sock(sk);
	err = ip6_mc_msfget(sk, &gf, p->gf_slist);
	release_sock(sk);
	if (err)
		return err;
	if (num > gf.gf_numsrc)
		num = gf.gf_numsrc;
	len = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
	if (put_user(len, optlen) ||
	    put_user(gf.gf_fmode, &p->gf_fmode) ||
	    put_user(gf.gf_numsrc, &p->gf_numsrc))
		return -EFAULT;
	return 0;
}
#endif

static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
		    char __user *optval, int __user *optlen, unsigned int flags)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	int len;
	int val;

	if (ip6_mroute_opt(optname))
		return ip6_mroute_getsockopt(sk, optname, optval, optlen);

	if (get_user(len, optlen))
		return -EFAULT;
	switch (optname) {
	case IPV6_ADDRFORM:
		if (sk->sk_protocol != IPPROTO_UDP &&
		    sk->sk_protocol != IPPROTO_UDPLITE &&
		    sk->sk_protocol != IPPROTO_TCP)
			return -ENOPROTOOPT;
		if (sk->sk_state != TCP_ESTABLISHED)
			return -ENOTCONN;
		val = sk->sk_family;
		break;
	case MCAST_MSFILTER:
		return ipv6_get_msfilter(sk, optval, optlen, len);
	case IPV6_2292PKTOPTIONS:
	{
		struct msghdr msg;
@@ -1481,44 +1526,8 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
	if (level != SOL_IPV6)
		return -ENOPROTOOPT;

	if (optname == MCAST_MSFILTER) {
		const int size0 = offsetof(struct compat_group_filter, gf_slist);
		struct compat_group_filter __user *p = (void __user *)optval;
		struct compat_group_filter gf32;
		struct group_filter gf;
		int ulen, err;
		int num;

		if (get_user(ulen, optlen))
			return -EFAULT;

		if (ulen < size0)
			return -EINVAL;

		if (copy_from_user(&gf32, p, size0))
			return -EFAULT;

		gf.gf_interface = gf32.gf_interface;
		gf.gf_fmode = gf32.gf_fmode;
		num = gf.gf_numsrc = gf32.gf_numsrc;
		gf.gf_group = gf32.gf_group;

		if (gf.gf_group.ss_family != AF_INET6)
			return -EADDRNOTAVAIL;
		lock_sock(sk);
		err = ip6_mc_msfget(sk, &gf, p->gf_slist);
		release_sock(sk);
		if (err)
			return err;
		if (num > gf.gf_numsrc)
			num = gf.gf_numsrc;
		ulen = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
		if (put_user(ulen, optlen) ||
		    put_user(gf.gf_fmode, &p->gf_fmode) ||
		    put_user(gf.gf_numsrc, &p->gf_numsrc))
			return -EFAULT;
		return 0;
	}
	if (optname == MCAST_MSFILTER)
		return compat_ipv6_get_msfilter(sk, optval, optlen);

	err = do_ipv6_getsockopt(sk, level, optname, optval, optlen,
				 MSG_CMSG_COMPAT);