mirror of https://gitee.com/openkylin/linux.git
Merge branch 'uaccess.net' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Al Viro says: ==================== uaccess-related stuff in net/* Assorted uaccess-related work in net/*. First, there's getting rid of compat_alloc_user_space() mess in MCAST_... [gs]etsockopt() - no need to play with copying to/from temporary object on userland stack, etc., when ->compat_[sg]etsockopt() instances in question can easly do everything without that. That's the first 13 patches. Then there's a trivial bit in net/batman-adv (completely unrelated to everything else) and finally getting the atm compat ioctls into simpler shape. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
c536fc74b4
|
@ -123,7 +123,7 @@ extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex);
|
|||
extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
|
||||
struct ip_msfilter __user *optval, int __user *optlen);
|
||||
extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
|
||||
struct group_filter __user *optval, int __user *optlen);
|
||||
struct sockaddr_storage __user *p);
|
||||
extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt,
|
||||
int dif, int sdif);
|
||||
extern void ip_mc_init_dev(struct in_device *);
|
||||
|
|
|
@ -67,11 +67,28 @@ int put_cmsg_compat(struct msghdr*, int, int, int, void *);
|
|||
int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *,
|
||||
unsigned char *, int);
|
||||
|
||||
int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int,
|
||||
int (*)(struct sock *, int, int, char __user *,
|
||||
unsigned int));
|
||||
int compat_mc_getsockopt(struct sock *, int, int, char __user *, int __user *,
|
||||
int (*)(struct sock *, int, int, char __user *,
|
||||
int __user *));
|
||||
struct compat_group_req {
|
||||
__u32 gr_interface;
|
||||
struct __kernel_sockaddr_storage gr_group
|
||||
__aligned(4);
|
||||
} __packed;
|
||||
|
||||
struct compat_group_source_req {
|
||||
__u32 gsr_interface;
|
||||
struct __kernel_sockaddr_storage gsr_group
|
||||
__aligned(4);
|
||||
struct __kernel_sockaddr_storage gsr_source
|
||||
__aligned(4);
|
||||
} __packed;
|
||||
|
||||
struct compat_group_filter {
|
||||
__u32 gf_interface;
|
||||
struct __kernel_sockaddr_storage gf_group
|
||||
__aligned(4);
|
||||
__u32 gf_fmode;
|
||||
__u32 gf_numsrc;
|
||||
struct __kernel_sockaddr_storage gf_slist[1]
|
||||
__aligned(4);
|
||||
} __packed;
|
||||
|
||||
#endif /* NET_COMPAT_H */
|
||||
|
|
|
@ -1136,9 +1136,10 @@ struct group_filter;
|
|||
|
||||
int ip6_mc_source(int add, int omode, struct sock *sk,
|
||||
struct group_source_req *pgsr);
|
||||
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
|
||||
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
|
||||
struct sockaddr_storage *list);
|
||||
int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
|
||||
struct group_filter __user *optval, int __user *optlen);
|
||||
struct sockaddr_storage __user *p);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int ac6_proc_init(struct net *net);
|
||||
|
|
|
@ -56,6 +56,8 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd,
|
|||
int error;
|
||||
struct list_head *pos;
|
||||
void __user *argp = (void __user *)arg;
|
||||
void __user *buf;
|
||||
int __user *len;
|
||||
|
||||
vcc = ATM_SD(sock);
|
||||
switch (cmd) {
|
||||
|
@ -162,7 +164,49 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd,
|
|||
if (error != -ENOIOCTLCMD)
|
||||
goto done;
|
||||
|
||||
error = atm_dev_ioctl(cmd, argp, compat);
|
||||
if (cmd == ATM_GETNAMES) {
|
||||
if (IS_ENABLED(CONFIG_COMPAT) && compat) {
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_atm_iobuf __user *ciobuf = argp;
|
||||
compat_uptr_t cbuf;
|
||||
len = &ciobuf->length;
|
||||
if (get_user(cbuf, &ciobuf->buffer))
|
||||
return -EFAULT;
|
||||
buf = compat_ptr(cbuf);
|
||||
#endif
|
||||
} else {
|
||||
struct atm_iobuf __user *iobuf = argp;
|
||||
len = &iobuf->length;
|
||||
if (get_user(buf, &iobuf->buffer))
|
||||
return -EFAULT;
|
||||
}
|
||||
error = atm_getnames(buf, len);
|
||||
} else {
|
||||
int number;
|
||||
|
||||
if (IS_ENABLED(CONFIG_COMPAT) && compat) {
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_atmif_sioc __user *csioc = argp;
|
||||
compat_uptr_t carg;
|
||||
|
||||
len = &csioc->length;
|
||||
if (get_user(carg, &csioc->arg))
|
||||
return -EFAULT;
|
||||
buf = compat_ptr(carg);
|
||||
if (get_user(number, &csioc->number))
|
||||
return -EFAULT;
|
||||
#endif
|
||||
} else {
|
||||
struct atmif_sioc __user *sioc = argp;
|
||||
|
||||
len = &sioc->length;
|
||||
if (get_user(buf, &sioc->arg))
|
||||
return -EFAULT;
|
||||
if (get_user(number, &sioc->number))
|
||||
return -EFAULT;
|
||||
}
|
||||
error = atm_dev_ioctl(cmd, buf, len, number, compat);
|
||||
}
|
||||
|
||||
done:
|
||||
return error;
|
||||
|
@ -230,61 +274,25 @@ static struct {
|
|||
static int do_atm_iobuf(struct socket *sock, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct atm_iobuf __user *iobuf;
|
||||
struct compat_atm_iobuf __user *iobuf32;
|
||||
struct compat_atm_iobuf __user *iobuf32 = compat_ptr(arg);
|
||||
u32 data;
|
||||
void __user *datap;
|
||||
int len, err;
|
||||
|
||||
iobuf = compat_alloc_user_space(sizeof(*iobuf));
|
||||
iobuf32 = compat_ptr(arg);
|
||||
|
||||
if (get_user(len, &iobuf32->length) ||
|
||||
get_user(data, &iobuf32->buffer))
|
||||
return -EFAULT;
|
||||
datap = compat_ptr(data);
|
||||
if (put_user(len, &iobuf->length) ||
|
||||
put_user(datap, &iobuf->buffer))
|
||||
if (get_user(data, &iobuf32->buffer))
|
||||
return -EFAULT;
|
||||
|
||||
err = do_vcc_ioctl(sock, cmd, (unsigned long) iobuf, 0);
|
||||
|
||||
if (!err) {
|
||||
if (copy_in_user(&iobuf32->length, &iobuf->length,
|
||||
sizeof(int)))
|
||||
err = -EFAULT;
|
||||
}
|
||||
|
||||
return err;
|
||||
return atm_getnames(&iobuf32->length, compat_ptr(data));
|
||||
}
|
||||
|
||||
static int do_atmif_sioc(struct socket *sock, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct atmif_sioc __user *sioc;
|
||||
struct compat_atmif_sioc __user *sioc32;
|
||||
struct compat_atmif_sioc __user *sioc32 = compat_ptr(arg);
|
||||
int number;
|
||||
u32 data;
|
||||
void __user *datap;
|
||||
int err;
|
||||
|
||||
sioc = compat_alloc_user_space(sizeof(*sioc));
|
||||
sioc32 = compat_ptr(arg);
|
||||
|
||||
if (copy_in_user(&sioc->number, &sioc32->number, 2 * sizeof(int)) ||
|
||||
get_user(data, &sioc32->arg))
|
||||
if (get_user(data, &sioc32->arg) || get_user(number, &sioc32->number))
|
||||
return -EFAULT;
|
||||
datap = compat_ptr(data);
|
||||
if (put_user(datap, &sioc->arg))
|
||||
return -EFAULT;
|
||||
|
||||
err = do_vcc_ioctl(sock, cmd, (unsigned long) sioc, 0);
|
||||
|
||||
if (!err) {
|
||||
if (copy_in_user(&sioc32->length, &sioc->length,
|
||||
sizeof(int)))
|
||||
err = -EFAULT;
|
||||
}
|
||||
return err;
|
||||
return atm_dev_ioctl(cmd, compat_ptr(data), &sioc32->length, number, 0);
|
||||
}
|
||||
|
||||
static int do_atm_ioctl(struct socket *sock, unsigned int cmd32,
|
||||
|
|
|
@ -193,88 +193,48 @@ static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg,
|
|||
return error ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat)
|
||||
int atm_getnames(void __user *buf, int __user *iobuf_len)
|
||||
{
|
||||
void __user *buf;
|
||||
int error, len, number, size = 0;
|
||||
int error, len, size = 0;
|
||||
struct atm_dev *dev;
|
||||
struct list_head *p;
|
||||
int *tmp_buf, *tmp_p;
|
||||
int __user *sioc_len;
|
||||
int __user *iobuf_len;
|
||||
|
||||
switch (cmd) {
|
||||
case ATM_GETNAMES:
|
||||
if (IS_ENABLED(CONFIG_COMPAT) && compat) {
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_atm_iobuf __user *ciobuf = arg;
|
||||
compat_uptr_t cbuf;
|
||||
iobuf_len = &ciobuf->length;
|
||||
if (get_user(cbuf, &ciobuf->buffer))
|
||||
return -EFAULT;
|
||||
buf = compat_ptr(cbuf);
|
||||
#endif
|
||||
} else {
|
||||
struct atm_iobuf __user *iobuf = arg;
|
||||
iobuf_len = &iobuf->length;
|
||||
if (get_user(buf, &iobuf->buffer))
|
||||
return -EFAULT;
|
||||
}
|
||||
if (get_user(len, iobuf_len))
|
||||
return -EFAULT;
|
||||
mutex_lock(&atm_dev_mutex);
|
||||
list_for_each(p, &atm_devs)
|
||||
size += sizeof(int);
|
||||
if (size > len) {
|
||||
mutex_unlock(&atm_dev_mutex);
|
||||
return -E2BIG;
|
||||
}
|
||||
tmp_buf = kmalloc(size, GFP_ATOMIC);
|
||||
if (!tmp_buf) {
|
||||
mutex_unlock(&atm_dev_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
tmp_p = tmp_buf;
|
||||
list_for_each(p, &atm_devs) {
|
||||
dev = list_entry(p, struct atm_dev, dev_list);
|
||||
*tmp_p++ = dev->number;
|
||||
}
|
||||
if (get_user(len, iobuf_len))
|
||||
return -EFAULT;
|
||||
mutex_lock(&atm_dev_mutex);
|
||||
list_for_each(p, &atm_devs)
|
||||
size += sizeof(int);
|
||||
if (size > len) {
|
||||
mutex_unlock(&atm_dev_mutex);
|
||||
error = ((copy_to_user(buf, tmp_buf, size)) ||
|
||||
put_user(size, iobuf_len))
|
||||
? -EFAULT : 0;
|
||||
kfree(tmp_buf);
|
||||
return error;
|
||||
default:
|
||||
break;
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_COMPAT) && compat) {
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_atmif_sioc __user *csioc = arg;
|
||||
compat_uptr_t carg;
|
||||
|
||||
sioc_len = &csioc->length;
|
||||
if (get_user(carg, &csioc->arg))
|
||||
return -EFAULT;
|
||||
buf = compat_ptr(carg);
|
||||
|
||||
if (get_user(len, &csioc->length))
|
||||
return -EFAULT;
|
||||
if (get_user(number, &csioc->number))
|
||||
return -EFAULT;
|
||||
#endif
|
||||
} else {
|
||||
struct atmif_sioc __user *sioc = arg;
|
||||
|
||||
sioc_len = &sioc->length;
|
||||
if (get_user(buf, &sioc->arg))
|
||||
return -EFAULT;
|
||||
if (get_user(len, &sioc->length))
|
||||
return -EFAULT;
|
||||
if (get_user(number, &sioc->number))
|
||||
return -EFAULT;
|
||||
tmp_buf = kmalloc(size, GFP_ATOMIC);
|
||||
if (!tmp_buf) {
|
||||
mutex_unlock(&atm_dev_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
tmp_p = tmp_buf;
|
||||
list_for_each(p, &atm_devs) {
|
||||
dev = list_entry(p, struct atm_dev, dev_list);
|
||||
*tmp_p++ = dev->number;
|
||||
}
|
||||
mutex_unlock(&atm_dev_mutex);
|
||||
error = ((copy_to_user(buf, tmp_buf, size)) ||
|
||||
put_user(size, iobuf_len))
|
||||
? -EFAULT : 0;
|
||||
kfree(tmp_buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
|
||||
int number, int compat)
|
||||
{
|
||||
int error, len, size = 0;
|
||||
struct atm_dev *dev;
|
||||
|
||||
if (get_user(len, sioc_len))
|
||||
return -EFAULT;
|
||||
|
||||
dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d",
|
||||
number);
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
extern struct list_head atm_devs;
|
||||
extern struct mutex atm_dev_mutex;
|
||||
|
||||
int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat);
|
||||
|
||||
int atm_getnames(void __user *buf, int __user *iobuf_len);
|
||||
int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
|
||||
int number, int compat);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
|
|
|
@ -135,9 +135,6 @@ static ssize_t batadv_socket_read(struct file *file, char __user *buf,
|
|||
if (!buf || count < sizeof(struct batadv_icmp_packet))
|
||||
return -EINVAL;
|
||||
|
||||
if (!access_ok(buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
error = wait_event_interruptible(socket_client->queue_wait,
|
||||
socket_client->queue_len);
|
||||
|
||||
|
|
194
net/compat.c
194
net/compat.c
|
@ -448,200 +448,6 @@ COMPAT_SYSCALL_DEFINE5(getsockopt, int, fd, int, level, int, optname,
|
|||
return __compat_sys_getsockopt(fd, level, optname, optval, optlen);
|
||||
}
|
||||
|
||||
struct compat_group_req {
|
||||
__u32 gr_interface;
|
||||
struct __kernel_sockaddr_storage gr_group
|
||||
__aligned(4);
|
||||
} __packed;
|
||||
|
||||
struct compat_group_source_req {
|
||||
__u32 gsr_interface;
|
||||
struct __kernel_sockaddr_storage gsr_group
|
||||
__aligned(4);
|
||||
struct __kernel_sockaddr_storage gsr_source
|
||||
__aligned(4);
|
||||
} __packed;
|
||||
|
||||
struct compat_group_filter {
|
||||
__u32 gf_interface;
|
||||
struct __kernel_sockaddr_storage gf_group
|
||||
__aligned(4);
|
||||
__u32 gf_fmode;
|
||||
__u32 gf_numsrc;
|
||||
struct __kernel_sockaddr_storage gf_slist[1]
|
||||
__aligned(4);
|
||||
} __packed;
|
||||
|
||||
#define __COMPAT_GF0_SIZE (sizeof(struct compat_group_filter) - \
|
||||
sizeof(struct __kernel_sockaddr_storage))
|
||||
|
||||
|
||||
int compat_mc_setsockopt(struct sock *sock, int level, int optname,
|
||||
char __user *optval, unsigned int optlen,
|
||||
int (*setsockopt)(struct sock *, int, int, char __user *, unsigned int))
|
||||
{
|
||||
char __user *koptval = optval;
|
||||
int koptlen = optlen;
|
||||
|
||||
switch (optname) {
|
||||
case MCAST_JOIN_GROUP:
|
||||
case MCAST_LEAVE_GROUP:
|
||||
{
|
||||
struct compat_group_req __user *gr32 = (void __user *)optval;
|
||||
struct group_req __user *kgr =
|
||||
compat_alloc_user_space(sizeof(struct group_req));
|
||||
u32 interface;
|
||||
|
||||
if (!access_ok(gr32, sizeof(*gr32)) ||
|
||||
!access_ok(kgr, sizeof(struct group_req)) ||
|
||||
__get_user(interface, &gr32->gr_interface) ||
|
||||
__put_user(interface, &kgr->gr_interface) ||
|
||||
copy_in_user(&kgr->gr_group, &gr32->gr_group,
|
||||
sizeof(kgr->gr_group)))
|
||||
return -EFAULT;
|
||||
koptval = (char __user *)kgr;
|
||||
koptlen = sizeof(struct group_req);
|
||||
break;
|
||||
}
|
||||
case MCAST_JOIN_SOURCE_GROUP:
|
||||
case MCAST_LEAVE_SOURCE_GROUP:
|
||||
case MCAST_BLOCK_SOURCE:
|
||||
case MCAST_UNBLOCK_SOURCE:
|
||||
{
|
||||
struct compat_group_source_req __user *gsr32 = (void __user *)optval;
|
||||
struct group_source_req __user *kgsr = compat_alloc_user_space(
|
||||
sizeof(struct group_source_req));
|
||||
u32 interface;
|
||||
|
||||
if (!access_ok(gsr32, sizeof(*gsr32)) ||
|
||||
!access_ok(kgsr,
|
||||
sizeof(struct group_source_req)) ||
|
||||
__get_user(interface, &gsr32->gsr_interface) ||
|
||||
__put_user(interface, &kgsr->gsr_interface) ||
|
||||
copy_in_user(&kgsr->gsr_group, &gsr32->gsr_group,
|
||||
sizeof(kgsr->gsr_group)) ||
|
||||
copy_in_user(&kgsr->gsr_source, &gsr32->gsr_source,
|
||||
sizeof(kgsr->gsr_source)))
|
||||
return -EFAULT;
|
||||
koptval = (char __user *)kgsr;
|
||||
koptlen = sizeof(struct group_source_req);
|
||||
break;
|
||||
}
|
||||
case MCAST_MSFILTER:
|
||||
{
|
||||
struct compat_group_filter __user *gf32 = (void __user *)optval;
|
||||
struct group_filter __user *kgf;
|
||||
u32 interface, fmode, numsrc;
|
||||
|
||||
if (!access_ok(gf32, __COMPAT_GF0_SIZE) ||
|
||||
__get_user(interface, &gf32->gf_interface) ||
|
||||
__get_user(fmode, &gf32->gf_fmode) ||
|
||||
__get_user(numsrc, &gf32->gf_numsrc))
|
||||
return -EFAULT;
|
||||
koptlen = optlen + sizeof(struct group_filter) -
|
||||
sizeof(struct compat_group_filter);
|
||||
if (koptlen < GROUP_FILTER_SIZE(numsrc))
|
||||
return -EINVAL;
|
||||
kgf = compat_alloc_user_space(koptlen);
|
||||
if (!access_ok(kgf, koptlen) ||
|
||||
__put_user(interface, &kgf->gf_interface) ||
|
||||
__put_user(fmode, &kgf->gf_fmode) ||
|
||||
__put_user(numsrc, &kgf->gf_numsrc) ||
|
||||
copy_in_user(&kgf->gf_group, &gf32->gf_group,
|
||||
sizeof(kgf->gf_group)) ||
|
||||
(numsrc && copy_in_user(kgf->gf_slist, gf32->gf_slist,
|
||||
numsrc * sizeof(kgf->gf_slist[0]))))
|
||||
return -EFAULT;
|
||||
koptval = (char __user *)kgf;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return setsockopt(sock, level, optname, koptval, koptlen);
|
||||
}
|
||||
EXPORT_SYMBOL(compat_mc_setsockopt);
|
||||
|
||||
int compat_mc_getsockopt(struct sock *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen,
|
||||
int (*getsockopt)(struct sock *, int, int, char __user *, int __user *))
|
||||
{
|
||||
struct compat_group_filter __user *gf32 = (void __user *)optval;
|
||||
struct group_filter __user *kgf;
|
||||
int __user *koptlen;
|
||||
u32 interface, fmode, numsrc;
|
||||
int klen, ulen, err;
|
||||
|
||||
if (optname != MCAST_MSFILTER)
|
||||
return getsockopt(sock, level, optname, optval, optlen);
|
||||
|
||||
koptlen = compat_alloc_user_space(sizeof(*koptlen));
|
||||
if (!access_ok(optlen, sizeof(*optlen)) ||
|
||||
__get_user(ulen, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
/* adjust len for pad */
|
||||
klen = ulen + sizeof(*kgf) - sizeof(*gf32);
|
||||
|
||||
if (klen < GROUP_FILTER_SIZE(0))
|
||||
return -EINVAL;
|
||||
|
||||
if (!access_ok(koptlen, sizeof(*koptlen)) ||
|
||||
__put_user(klen, koptlen))
|
||||
return -EFAULT;
|
||||
|
||||
/* have to allow space for previous compat_alloc_user_space, too */
|
||||
kgf = compat_alloc_user_space(klen+sizeof(*optlen));
|
||||
|
||||
if (!access_ok(gf32, __COMPAT_GF0_SIZE) ||
|
||||
__get_user(interface, &gf32->gf_interface) ||
|
||||
__get_user(fmode, &gf32->gf_fmode) ||
|
||||
__get_user(numsrc, &gf32->gf_numsrc) ||
|
||||
__put_user(interface, &kgf->gf_interface) ||
|
||||
__put_user(fmode, &kgf->gf_fmode) ||
|
||||
__put_user(numsrc, &kgf->gf_numsrc) ||
|
||||
copy_in_user(&kgf->gf_group, &gf32->gf_group, sizeof(kgf->gf_group)))
|
||||
return -EFAULT;
|
||||
|
||||
err = getsockopt(sock, level, optname, (char __user *)kgf, koptlen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!access_ok(koptlen, sizeof(*koptlen)) ||
|
||||
__get_user(klen, koptlen))
|
||||
return -EFAULT;
|
||||
|
||||
ulen = klen - (sizeof(*kgf)-sizeof(*gf32));
|
||||
|
||||
if (!access_ok(optlen, sizeof(*optlen)) ||
|
||||
__put_user(ulen, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
if (!access_ok(kgf, klen) ||
|
||||
!access_ok(gf32, ulen) ||
|
||||
__get_user(interface, &kgf->gf_interface) ||
|
||||
__get_user(fmode, &kgf->gf_fmode) ||
|
||||
__get_user(numsrc, &kgf->gf_numsrc) ||
|
||||
__put_user(interface, &gf32->gf_interface) ||
|
||||
__put_user(fmode, &gf32->gf_fmode) ||
|
||||
__put_user(numsrc, &gf32->gf_numsrc))
|
||||
return -EFAULT;
|
||||
if (numsrc) {
|
||||
int copylen;
|
||||
|
||||
klen -= GROUP_FILTER_SIZE(0);
|
||||
copylen = numsrc * sizeof(gf32->gf_slist[0]);
|
||||
if (copylen > klen)
|
||||
copylen = klen;
|
||||
if (copy_in_user(gf32->gf_slist, kgf->gf_slist, copylen))
|
||||
return -EFAULT;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(compat_mc_getsockopt);
|
||||
|
||||
|
||||
/* Argument list sizes for compat_sys_socketcall */
|
||||
#define AL(x) ((x) * sizeof(u32))
|
||||
static unsigned char nas[21] = {
|
||||
|
|
|
@ -2565,9 +2565,9 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
|
|||
}
|
||||
|
||||
int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
|
||||
struct group_filter __user *optval, int __user *optlen)
|
||||
struct sockaddr_storage __user *p)
|
||||
{
|
||||
int err, i, count, copycount;
|
||||
int i, count, copycount;
|
||||
struct sockaddr_in *psin;
|
||||
__be32 addr;
|
||||
struct ip_mc_socklist *pmc;
|
||||
|
@ -2583,37 +2583,29 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
|
|||
if (!ipv4_is_multicast(addr))
|
||||
return -EINVAL;
|
||||
|
||||
err = -EADDRNOTAVAIL;
|
||||
|
||||
for_each_pmc_rtnl(inet, pmc) {
|
||||
if (pmc->multi.imr_multiaddr.s_addr == addr &&
|
||||
pmc->multi.imr_ifindex == gsf->gf_interface)
|
||||
break;
|
||||
}
|
||||
if (!pmc) /* must have a prior join */
|
||||
goto done;
|
||||
return -EADDRNOTAVAIL;
|
||||
gsf->gf_fmode = pmc->sfmode;
|
||||
psl = rtnl_dereference(pmc->sflist);
|
||||
count = psl ? psl->sl_count : 0;
|
||||
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
|
||||
gsf->gf_numsrc = count;
|
||||
if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
|
||||
copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
for (i = 0; i < copycount; i++) {
|
||||
for (i = 0; i < copycount; i++, p++) {
|
||||
struct sockaddr_storage ss;
|
||||
|
||||
psin = (struct sockaddr_in *)&ss;
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
psin->sin_family = AF_INET;
|
||||
psin->sin_addr.s_addr = psl->sl_addr[i];
|
||||
if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
|
||||
if (copy_to_user(p, &ss, sizeof(ss)))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -587,6 +587,86 @@ static bool setsockopt_needs_rtnl(int optname)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int set_mcast_msfilter(struct sock *sk, int ifindex,
|
||||
int numsrc, int fmode,
|
||||
struct sockaddr_storage *group,
|
||||
struct sockaddr_storage *list)
|
||||
{
|
||||
int msize = IP_MSFILTER_SIZE(numsrc);
|
||||
struct ip_msfilter *msf;
|
||||
struct sockaddr_in *psin;
|
||||
int err, i;
|
||||
|
||||
msf = kmalloc(msize, GFP_KERNEL);
|
||||
if (!msf)
|
||||
return -ENOBUFS;
|
||||
|
||||
psin = (struct sockaddr_in *)group;
|
||||
if (psin->sin_family != AF_INET)
|
||||
goto Eaddrnotavail;
|
||||
msf->imsf_multiaddr = psin->sin_addr.s_addr;
|
||||
msf->imsf_interface = 0;
|
||||
msf->imsf_fmode = fmode;
|
||||
msf->imsf_numsrc = numsrc;
|
||||
for (i = 0; i < numsrc; ++i) {
|
||||
psin = (struct sockaddr_in *)&list[i];
|
||||
|
||||
if (psin->sin_family != AF_INET)
|
||||
goto Eaddrnotavail;
|
||||
msf->imsf_slist[i] = psin->sin_addr.s_addr;
|
||||
}
|
||||
err = ip_mc_msfilter(sk, msf, ifindex);
|
||||
kfree(msf);
|
||||
return err;
|
||||
|
||||
Eaddrnotavail:
|
||||
kfree(msf);
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
static int do_mcast_group_source(struct sock *sk, int optname,
|
||||
struct group_source_req *greqs)
|
||||
{
|
||||
struct ip_mreq_source mreqs;
|
||||
struct sockaddr_in *psin;
|
||||
int omode, add, err;
|
||||
|
||||
if (greqs->gsr_group.ss_family != AF_INET ||
|
||||
greqs->gsr_source.ss_family != AF_INET)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
psin = (struct sockaddr_in *)&greqs->gsr_group;
|
||||
mreqs.imr_multiaddr = psin->sin_addr.s_addr;
|
||||
psin = (struct sockaddr_in *)&greqs->gsr_source;
|
||||
mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
|
||||
mreqs.imr_interface = 0; /* use index for mc_source */
|
||||
|
||||
if (optname == MCAST_BLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 1;
|
||||
} else if (optname == MCAST_UNBLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 0;
|
||||
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
|
||||
struct ip_mreqn mreq;
|
||||
|
||||
psin = (struct sockaddr_in *)&greqs->gsr_group;
|
||||
mreq.imr_multiaddr = psin->sin_addr;
|
||||
mreq.imr_address.s_addr = 0;
|
||||
mreq.imr_ifindex = greqs->gsr_interface;
|
||||
err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
|
||||
if (err && err != -EADDRINUSE)
|
||||
return err;
|
||||
greqs->gsr_interface = mreq.imr_ifindex;
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 1;
|
||||
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 0;
|
||||
}
|
||||
return ip_mc_source(add, omode, sk, &mreqs, greqs->gsr_interface);
|
||||
}
|
||||
|
||||
static int do_ip_setsockopt(struct sock *sk, int level,
|
||||
int optname, char __user *optval, unsigned int optlen)
|
||||
{
|
||||
|
@ -1029,9 +1109,6 @@ static int do_ip_setsockopt(struct sock *sk, int level,
|
|||
case MCAST_UNBLOCK_SOURCE:
|
||||
{
|
||||
struct group_source_req greqs;
|
||||
struct ip_mreq_source mreqs;
|
||||
struct sockaddr_in *psin;
|
||||
int omode, add;
|
||||
|
||||
if (optlen != sizeof(struct group_source_req))
|
||||
goto e_inval;
|
||||
|
@ -1039,50 +1116,12 @@ static int do_ip_setsockopt(struct sock *sk, int level,
|
|||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (greqs.gsr_group.ss_family != AF_INET ||
|
||||
greqs.gsr_source.ss_family != AF_INET) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
break;
|
||||
}
|
||||
psin = (struct sockaddr_in *)&greqs.gsr_group;
|
||||
mreqs.imr_multiaddr = psin->sin_addr.s_addr;
|
||||
psin = (struct sockaddr_in *)&greqs.gsr_source;
|
||||
mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
|
||||
mreqs.imr_interface = 0; /* use index for mc_source */
|
||||
|
||||
if (optname == MCAST_BLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 1;
|
||||
} else if (optname == MCAST_UNBLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 0;
|
||||
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
|
||||
struct ip_mreqn mreq;
|
||||
|
||||
psin = (struct sockaddr_in *)&greqs.gsr_group;
|
||||
mreq.imr_multiaddr = psin->sin_addr;
|
||||
mreq.imr_address.s_addr = 0;
|
||||
mreq.imr_ifindex = greqs.gsr_interface;
|
||||
err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
|
||||
if (err && err != -EADDRINUSE)
|
||||
break;
|
||||
greqs.gsr_interface = mreq.imr_ifindex;
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 1;
|
||||
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 0;
|
||||
}
|
||||
err = ip_mc_source(add, omode, sk, &mreqs,
|
||||
greqs.gsr_interface);
|
||||
err = do_mcast_group_source(sk, optname, &greqs);
|
||||
break;
|
||||
}
|
||||
case MCAST_MSFILTER:
|
||||
{
|
||||
struct sockaddr_in *psin;
|
||||
struct ip_msfilter *msf = NULL;
|
||||
struct group_filter *gsf = NULL;
|
||||
int msize, i, ifindex;
|
||||
|
||||
if (optlen < GROUP_FILTER_SIZE(0))
|
||||
goto e_inval;
|
||||
|
@ -1095,7 +1134,6 @@ static int do_ip_setsockopt(struct sock *sk, int level,
|
|||
err = PTR_ERR(gsf);
|
||||
break;
|
||||
}
|
||||
|
||||
/* numsrc >= (4G-140)/128 overflow in 32 bits */
|
||||
if (gsf->gf_numsrc >= 0x1ffffff ||
|
||||
gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
|
||||
|
@ -1106,36 +1144,10 @@ static int do_ip_setsockopt(struct sock *sk, int level,
|
|||
err = -EINVAL;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
|
||||
msf = kmalloc(msize, GFP_KERNEL);
|
||||
if (!msf) {
|
||||
err = -ENOBUFS;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
ifindex = gsf->gf_interface;
|
||||
psin = (struct sockaddr_in *)&gsf->gf_group;
|
||||
if (psin->sin_family != AF_INET) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
msf->imsf_multiaddr = psin->sin_addr.s_addr;
|
||||
msf->imsf_interface = 0;
|
||||
msf->imsf_fmode = gsf->gf_fmode;
|
||||
msf->imsf_numsrc = gsf->gf_numsrc;
|
||||
err = -EADDRNOTAVAIL;
|
||||
for (i = 0; i < gsf->gf_numsrc; ++i) {
|
||||
psin = (struct sockaddr_in *)&gsf->gf_slist[i];
|
||||
|
||||
if (psin->sin_family != AF_INET)
|
||||
goto mc_msf_out;
|
||||
msf->imsf_slist[i] = psin->sin_addr.s_addr;
|
||||
}
|
||||
kfree(gsf);
|
||||
gsf = NULL;
|
||||
|
||||
err = ip_mc_msfilter(sk, msf, ifindex);
|
||||
err = set_mcast_msfilter(sk, gsf->gf_interface,
|
||||
gsf->gf_numsrc, gsf->gf_fmode,
|
||||
&gsf->gf_group, gsf->gf_slist);
|
||||
mc_msf_out:
|
||||
kfree(msf);
|
||||
kfree(gsf);
|
||||
break;
|
||||
}
|
||||
|
@ -1272,9 +1284,113 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname,
|
|||
if (level != SOL_IP)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
|
||||
return compat_mc_setsockopt(sk, level, optname, optval, optlen,
|
||||
ip_setsockopt);
|
||||
switch (optname) {
|
||||
case MCAST_JOIN_GROUP:
|
||||
case MCAST_LEAVE_GROUP:
|
||||
{
|
||||
struct compat_group_req __user *gr32 = (void __user *)optval;
|
||||
struct group_req greq;
|
||||
struct sockaddr_in *psin = (struct sockaddr_in *)&greq.gr_group;
|
||||
struct ip_mreqn mreq;
|
||||
|
||||
if (optlen < sizeof(struct compat_group_req))
|
||||
return -EINVAL;
|
||||
|
||||
if (get_user(greq.gr_interface, &gr32->gr_interface) ||
|
||||
copy_from_user(&greq.gr_group, &gr32->gr_group,
|
||||
sizeof(greq.gr_group)))
|
||||
return -EFAULT;
|
||||
|
||||
if (psin->sin_family != AF_INET)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
mreq.imr_multiaddr = psin->sin_addr;
|
||||
mreq.imr_ifindex = greq.gr_interface;
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
if (optname == MCAST_JOIN_GROUP)
|
||||
err = ip_mc_join_group(sk, &mreq);
|
||||
else
|
||||
err = ip_mc_leave_group(sk, &mreq);
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
}
|
||||
case MCAST_JOIN_SOURCE_GROUP:
|
||||
case MCAST_LEAVE_SOURCE_GROUP:
|
||||
case MCAST_BLOCK_SOURCE:
|
||||
case MCAST_UNBLOCK_SOURCE:
|
||||
{
|
||||
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);
|
||||
struct compat_group_filter *gf32;
|
||||
void *p;
|
||||
int n;
|
||||
|
||||
if (optlen < size0)
|
||||
return -EINVAL;
|
||||
if (optlen > sysctl_optmem_max - 4)
|
||||
return -ENOBUFS;
|
||||
|
||||
p = kmalloc(optlen + 4, GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */
|
||||
if (copy_from_user(gf32, optval, optlen)) {
|
||||
err = -EFAULT;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
|
||||
n = gf32->gf_numsrc;
|
||||
/* numsrc >= (4G-140)/128 overflow in 32 bits */
|
||||
if (n >= 0x1ffffff) {
|
||||
err = -ENOBUFS;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) {
|
||||
err = -EINVAL;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
/* numsrc >= (4G-140)/128 overflow in 32 bits */
|
||||
if (n > sock_net(sk)->ipv4.sysctl_igmp_max_msf)
|
||||
err = -ENOBUFS;
|
||||
else
|
||||
err = set_mcast_msfilter(sk, gf32->gf_interface,
|
||||
n, gf32->gf_fmode,
|
||||
&gf32->gf_group, gf32->gf_slist);
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
mc_msf_out:
|
||||
kfree(p);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = do_ip_setsockopt(sk, level, optname, optval, optlen);
|
||||
#ifdef CONFIG_NETFILTER
|
||||
|
@ -1465,19 +1581,28 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
|
|||
}
|
||||
case MCAST_MSFILTER:
|
||||
{
|
||||
struct group_filter __user *p = (void __user *)optval;
|
||||
struct group_filter gsf;
|
||||
const int size0 = offsetof(struct group_filter, gf_slist);
|
||||
int num;
|
||||
|
||||
if (len < GROUP_FILTER_SIZE(0)) {
|
||||
if (len < size0) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
|
||||
if (copy_from_user(&gsf, p, size0)) {
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
err = ip_mc_gsfget(sk, &gsf,
|
||||
(struct group_filter __user *)optval,
|
||||
optlen);
|
||||
num = gsf.gf_numsrc;
|
||||
err = ip_mc_gsfget(sk, &gsf, p->gf_slist);
|
||||
if (err)
|
||||
goto out;
|
||||
if (gsf.gf_numsrc < num)
|
||||
num = gsf.gf_numsrc;
|
||||
if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
|
||||
copy_to_user(p, &gsf, size0))
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
case IP_MULTICAST_ALL:
|
||||
|
@ -1590,9 +1715,47 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname,
|
|||
{
|
||||
int err;
|
||||
|
||||
if (optname == MCAST_MSFILTER)
|
||||
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
||||
ip_getsockopt);
|
||||
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 (level != SOL_IP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
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;
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
err = ip_mc_gsfget(sk, &gf, p->gf_slist);
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
if (err)
|
||||
return err;
|
||||
if (gf.gf_numsrc < num)
|
||||
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;
|
||||
}
|
||||
|
||||
err = do_ip_getsockopt(sk, level, optname, optval, optlen,
|
||||
MSG_CMSG_COMPAT);
|
||||
|
|
|
@ -136,6 +136,41 @@ static bool setsockopt_needs_rtnl(int optname)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int do_ipv6_mcast_group_source(struct sock *sk, int optname,
|
||||
struct group_source_req *greqs)
|
||||
{
|
||||
int omode, add;
|
||||
|
||||
if (greqs->gsr_group.ss_family != AF_INET6 ||
|
||||
greqs->gsr_source.ss_family != AF_INET6)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
if (optname == MCAST_BLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 1;
|
||||
} else if (optname == MCAST_UNBLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 0;
|
||||
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
|
||||
struct sockaddr_in6 *psin6;
|
||||
int retv;
|
||||
|
||||
psin6 = (struct sockaddr_in6 *)&greqs->gsr_group;
|
||||
retv = ipv6_sock_mc_join_ssm(sk, greqs->gsr_interface,
|
||||
&psin6->sin6_addr,
|
||||
MCAST_INCLUDE);
|
||||
/* prior join w/ different source is ok */
|
||||
if (retv && retv != -EADDRINUSE)
|
||||
return retv;
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 1;
|
||||
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 0;
|
||||
}
|
||||
return ip6_mc_source(add, omode, sk, greqs);
|
||||
}
|
||||
|
||||
static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
|
@ -715,7 +750,6 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
case MCAST_UNBLOCK_SOURCE:
|
||||
{
|
||||
struct group_source_req greqs;
|
||||
int omode, add;
|
||||
|
||||
if (optlen < sizeof(struct group_source_req))
|
||||
goto e_inval;
|
||||
|
@ -723,34 +757,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
retv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (greqs.gsr_group.ss_family != AF_INET6 ||
|
||||
greqs.gsr_source.ss_family != AF_INET6) {
|
||||
retv = -EADDRNOTAVAIL;
|
||||
break;
|
||||
}
|
||||
if (optname == MCAST_BLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 1;
|
||||
} else if (optname == MCAST_UNBLOCK_SOURCE) {
|
||||
omode = MCAST_EXCLUDE;
|
||||
add = 0;
|
||||
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
|
||||
struct sockaddr_in6 *psin6;
|
||||
|
||||
psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
|
||||
retv = ipv6_sock_mc_join_ssm(sk, greqs.gsr_interface,
|
||||
&psin6->sin6_addr,
|
||||
MCAST_INCLUDE);
|
||||
/* prior join w/ different source is ok */
|
||||
if (retv && retv != -EADDRINUSE)
|
||||
break;
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 1;
|
||||
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
|
||||
omode = MCAST_INCLUDE;
|
||||
add = 0;
|
||||
}
|
||||
retv = ip6_mc_source(add, omode, sk, &greqs);
|
||||
retv = do_ipv6_mcast_group_source(sk, optname, &greqs);
|
||||
break;
|
||||
}
|
||||
case MCAST_MSFILTER:
|
||||
|
@ -780,7 +787,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
retv = -EINVAL;
|
||||
break;
|
||||
}
|
||||
retv = ip6_mc_msfilter(sk, gsf);
|
||||
retv = ip6_mc_msfilter(sk, gsf, gsf->gf_slist);
|
||||
kfree(gsf);
|
||||
|
||||
break;
|
||||
|
@ -973,9 +980,110 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
if (level != SOL_IPV6)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
|
||||
return compat_mc_setsockopt(sk, level, optname, optval, optlen,
|
||||
ipv6_setsockopt);
|
||||
switch (optname) {
|
||||
case MCAST_JOIN_GROUP:
|
||||
case MCAST_LEAVE_GROUP:
|
||||
{
|
||||
struct compat_group_req __user *gr32 = (void __user *)optval;
|
||||
struct group_req greq;
|
||||
struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)&greq.gr_group;
|
||||
|
||||
if (optlen < sizeof(struct compat_group_req))
|
||||
return -EINVAL;
|
||||
|
||||
if (get_user(greq.gr_interface, &gr32->gr_interface) ||
|
||||
copy_from_user(&greq.gr_group, &gr32->gr_group,
|
||||
sizeof(greq.gr_group)))
|
||||
return -EFAULT;
|
||||
|
||||
if (greq.gr_group.ss_family != AF_INET6)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
if (optname == MCAST_JOIN_GROUP)
|
||||
err = ipv6_sock_mc_join(sk, greq.gr_interface,
|
||||
&psin6->sin6_addr);
|
||||
else
|
||||
err = ipv6_sock_mc_drop(sk, greq.gr_interface,
|
||||
&psin6->sin6_addr);
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
}
|
||||
case MCAST_JOIN_SOURCE_GROUP:
|
||||
case MCAST_LEAVE_SOURCE_GROUP:
|
||||
case MCAST_BLOCK_SOURCE:
|
||||
case MCAST_UNBLOCK_SOURCE:
|
||||
{
|
||||
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);
|
||||
struct compat_group_filter *gf32;
|
||||
void *p;
|
||||
int n;
|
||||
|
||||
if (optlen < size0)
|
||||
return -EINVAL;
|
||||
if (optlen > sysctl_optmem_max - 4)
|
||||
return -ENOBUFS;
|
||||
|
||||
p = kmalloc(optlen + 4, GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */
|
||||
if (copy_from_user(gf32, optval, optlen)) {
|
||||
err = -EFAULT;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
|
||||
n = gf32->gf_numsrc;
|
||||
/* numsrc >= (4G-140)/128 overflow in 32 bits */
|
||||
if (n >= 0x1ffffffU ||
|
||||
n > sysctl_mld_max_msf) {
|
||||
err = -ENOBUFS;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen) {
|
||||
err = -EINVAL;
|
||||
goto mc_msf_out;
|
||||
}
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
err = ip6_mc_msfilter(sk, &(struct group_filter){
|
||||
.gf_interface = gf32->gf_interface,
|
||||
.gf_group = gf32->gf_group,
|
||||
.gf_fmode = gf32->gf_fmode,
|
||||
.gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist);
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
mc_msf_out:
|
||||
kfree(p);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
|
||||
#ifdef CONFIG_NETFILTER
|
||||
|
@ -1048,18 +1156,28 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
|||
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);
|
||||
int num;
|
||||
int err;
|
||||
|
||||
if (len < GROUP_FILTER_SIZE(0))
|
||||
if (len < size0)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
|
||||
if (copy_from_user(&gsf, p, size0))
|
||||
return -EFAULT;
|
||||
if (gsf.gf_group.ss_family != AF_INET6)
|
||||
return -EADDRNOTAVAIL;
|
||||
num = gsf.gf_numsrc;
|
||||
lock_sock(sk);
|
||||
err = ip6_mc_msfget(sk, &gsf,
|
||||
(struct group_filter __user *)optval, optlen);
|
||||
err = ip6_mc_msfget(sk, &gsf, p->gf_slist);
|
||||
if (!err) {
|
||||
if (num > gsf.gf_numsrc)
|
||||
num = gsf.gf_numsrc;
|
||||
if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
|
||||
copy_to_user(p, &gsf, size0))
|
||||
err = -EFAULT;
|
||||
}
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
@ -1428,9 +1546,44 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
|||
if (level != SOL_IPV6)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (optname == MCAST_MSFILTER)
|
||||
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
||||
ipv6_getsockopt);
|
||||
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;
|
||||
}
|
||||
|
||||
err = do_ipv6_getsockopt(sk, level, optname, optval, optlen,
|
||||
MSG_CMSG_COMPAT);
|
||||
|
|
|
@ -457,7 +457,8 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
|
|||
return err;
|
||||
}
|
||||
|
||||
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
|
||||
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
|
||||
struct sockaddr_storage *list)
|
||||
{
|
||||
const struct in6_addr *group;
|
||||
struct ipv6_mc_socklist *pmc;
|
||||
|
@ -509,10 +510,10 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
|
|||
goto done;
|
||||
}
|
||||
newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc;
|
||||
for (i = 0; i < newpsl->sl_count; ++i) {
|
||||
for (i = 0; i < newpsl->sl_count; ++i, ++list) {
|
||||
struct sockaddr_in6 *psin6;
|
||||
|
||||
psin6 = (struct sockaddr_in6 *)&gsf->gf_slist[i];
|
||||
psin6 = (struct sockaddr_in6 *)list;
|
||||
newpsl->sl_addr[i] = psin6->sin6_addr;
|
||||
}
|
||||
err = ip6_mc_add_src(idev, group, gsf->gf_fmode,
|
||||
|
@ -547,7 +548,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
|
|||
}
|
||||
|
||||
int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
|
||||
struct group_filter __user *optval, int __user *optlen)
|
||||
struct sockaddr_storage *p)
|
||||
{
|
||||
int err, i, count, copycount;
|
||||
const struct in6_addr *group;
|
||||
|
@ -592,14 +593,10 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
|
|||
|
||||
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
|
||||
gsf->gf_numsrc = count;
|
||||
if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
|
||||
copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
/* changes to psl require the socket lock, and a write lock
|
||||
* on pmc->sflock. We have the socket lock so reading here is safe.
|
||||
*/
|
||||
for (i = 0; i < copycount; i++) {
|
||||
for (i = 0; i < copycount; i++, p++) {
|
||||
struct sockaddr_in6 *psin6;
|
||||
struct sockaddr_storage ss;
|
||||
|
||||
|
@ -607,7 +604,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
|
|||
memset(&ss, 0, sizeof(ss));
|
||||
psin6->sin6_family = AF_INET6;
|
||||
psin6->sin6_addr = psl->sl_addr[i];
|
||||
if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
|
||||
if (copy_to_user(p, &ss, sizeof(ss)))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue