handle the group_source_req options directly
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 27 Apr 2020 15:37:02 +0000 (11:37 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 21 May 2020 00:31:32 +0000 (20:31 -0400)
Native ->setsockopt() handling of these options (MCAST_..._SOURCE_GROUP
and MCAST_{,UN}BLOCK_SOURCE) consists of copyin + call of a helper that
does the actual work.  The only change needed for ->compat_setsockopt()
is a slightly different copyin - the helpers can be reused as-is.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
net/ipv4/ip_sockglue.c
net/ipv6/ipv6_sockglue.c

index 7f065a6..a2469bc 100644 (file)
@@ -1322,8 +1322,27 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname,
        case MCAST_LEAVE_SOURCE_GROUP:
        case MCAST_BLOCK_SOURCE:
        case MCAST_UNBLOCK_SOURCE:
-               return compat_mc_setsockopt(sk, level, optname, optval, optlen,
-                       ip_setsockopt);
+       {
+               struct compat_group_source_req __user *gsr32 = (void __user *)optval;
+               struct group_source_req greqs;
+
+               if (optlen != sizeof(struct compat_group_source_req))
+                       return -EINVAL;
+
+               if (get_user(greqs.gsr_interface, &gsr32->gsr_interface) ||
+                   copy_from_user(&greqs.gsr_group, &gsr32->gsr_group,
+                               sizeof(greqs.gsr_group)) ||
+                   copy_from_user(&greqs.gsr_source, &gsr32->gsr_source,
+                               sizeof(greqs.gsr_source)))
+                       return -EFAULT;
+
+               rtnl_lock();
+               lock_sock(sk);
+               err = do_mcast_group_source(sk, optname, &greqs);
+               release_sock(sk);
+               rtnl_unlock();
+               return err;
+       }
        case MCAST_MSFILTER:
        {
                const int size0 = offsetof(struct compat_group_filter, gf_slist);
index bb049fe..e10258c 100644 (file)
@@ -1015,8 +1015,27 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
        case MCAST_LEAVE_SOURCE_GROUP:
        case MCAST_BLOCK_SOURCE:
        case MCAST_UNBLOCK_SOURCE:
-               return compat_mc_setsockopt(sk, level, optname, optval, optlen,
-                       ipv6_setsockopt);
+       {
+               struct compat_group_source_req __user *gsr32 = (void __user *)optval;
+               struct group_source_req greqs;
+
+               if (optlen < sizeof(struct compat_group_source_req))
+                       return -EINVAL;
+
+               if (get_user(greqs.gsr_interface, &gsr32->gsr_interface) ||
+                   copy_from_user(&greqs.gsr_group, &gsr32->gsr_group,
+                               sizeof(greqs.gsr_group)) ||
+                   copy_from_user(&greqs.gsr_source, &gsr32->gsr_source,
+                               sizeof(greqs.gsr_source)))
+                       return -EFAULT;
+
+               rtnl_lock();
+               lock_sock(sk);
+               err = do_ipv6_mcast_group_source(sk, optname, &greqs);
+               release_sock(sk);
+               rtnl_unlock();
+               return err;
+       }
        case MCAST_MSFILTER:
        {
                const int size0 = offsetof(struct compat_group_filter, gf_slist);