diff options
author | David L Stevens <dlstevens@us.ibm.com> | 2008-04-29 06:23:22 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-04-29 06:23:22 -0400 |
commit | 42908c69f61f75dd70e424263ab89ee52040382b (patch) | |
tree | 7c4333d2159d19cb33ae18f718259df258d7d137 | |
parent | be666e0a1345ed80f29cb30c73da0ec2ea5c5863 (diff) |
net: Add compat support for getsockopt (MCAST_MSFILTER)
This patch adds support for getsockopt for MCAST_MSFILTER for
both IPv4 and IPv6. It depends on the previous setsockopt patch,
and uses the same method.
Signed-off-by: David L Stevens <dlstevens@us.ibm.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/compat.h | 3 | ||||
-rw-r--r-- | net/compat.c | 79 | ||||
-rw-r--r-- | net/ipv4/ip_sockglue.c | 9 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 4 |
4 files changed, 94 insertions, 1 deletions
diff --git a/include/net/compat.h b/include/net/compat.h index 05fa5d0254ab..164cb682e220 100644 --- a/include/net/compat.h +++ b/include/net/compat.h | |||
@@ -42,5 +42,8 @@ extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsi | |||
42 | 42 | ||
43 | extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int, | 43 | extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int, |
44 | int (*)(struct sock *, int, int, char __user *, int)); | 44 | int (*)(struct sock *, int, int, char __user *, int)); |
45 | extern int compat_mc_getsockopt(struct sock *, int, int, char __user *, | ||
46 | int __user *, int (*)(struct sock *, int, int, char __user *, | ||
47 | int __user *)); | ||
45 | 48 | ||
46 | #endif /* NET_COMPAT_H */ | 49 | #endif /* NET_COMPAT_H */ |
diff --git a/net/compat.c b/net/compat.c index 8146f654391c..c823f6f290cb 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -640,6 +640,85 @@ int compat_mc_setsockopt(struct sock *sock, int level, int optname, | |||
640 | 640 | ||
641 | EXPORT_SYMBOL(compat_mc_setsockopt); | 641 | EXPORT_SYMBOL(compat_mc_setsockopt); |
642 | 642 | ||
643 | int compat_mc_getsockopt(struct sock *sock, int level, int optname, | ||
644 | char __user *optval, int __user *optlen, | ||
645 | int (*getsockopt)(struct sock *,int,int,char __user *,int __user *)) | ||
646 | { | ||
647 | struct compat_group_filter __user *gf32 = (void *)optval; | ||
648 | struct group_filter __user *kgf; | ||
649 | int __user *koptlen; | ||
650 | u32 interface, fmode, numsrc; | ||
651 | int klen, ulen, err; | ||
652 | |||
653 | if (optname != MCAST_MSFILTER) | ||
654 | return getsockopt(sock, level, optname, optval, optlen); | ||
655 | |||
656 | koptlen = compat_alloc_user_space(sizeof(*koptlen)); | ||
657 | if (!access_ok(VERIFY_READ, optlen, sizeof(*optlen)) || | ||
658 | __get_user(ulen, optlen)) | ||
659 | return -EFAULT; | ||
660 | |||
661 | /* adjust len for pad */ | ||
662 | klen = ulen + sizeof(*kgf) - sizeof(*gf32); | ||
663 | |||
664 | if (klen < GROUP_FILTER_SIZE(0)) | ||
665 | return -EINVAL; | ||
666 | |||
667 | if (!access_ok(VERIFY_WRITE, koptlen, sizeof(*koptlen)) || | ||
668 | __put_user(klen, koptlen)) | ||
669 | return -EFAULT; | ||
670 | |||
671 | /* have to allow space for previous compat_alloc_user_space, too */ | ||
672 | kgf = compat_alloc_user_space(klen+sizeof(*optlen)); | ||
673 | |||
674 | if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) || | ||
675 | __get_user(interface, &gf32->gf_interface) || | ||
676 | __get_user(fmode, &gf32->gf_fmode) || | ||
677 | __get_user(numsrc, &gf32->gf_numsrc) || | ||
678 | __put_user(interface, &kgf->gf_interface) || | ||
679 | __put_user(fmode, &kgf->gf_fmode) || | ||
680 | __put_user(numsrc, &kgf->gf_numsrc) || | ||
681 | copy_in_user(&kgf->gf_group,&gf32->gf_group,sizeof(kgf->gf_group))) | ||
682 | return -EFAULT; | ||
683 | |||
684 | err = getsockopt(sock, level, optname, (char __user *)kgf, koptlen); | ||
685 | if (err) | ||
686 | return err; | ||
687 | |||
688 | if (!access_ok(VERIFY_READ, koptlen, sizeof(*koptlen)) || | ||
689 | __get_user(klen, koptlen)) | ||
690 | return -EFAULT; | ||
691 | |||
692 | ulen = klen - (sizeof(*kgf)-sizeof(*gf32)); | ||
693 | |||
694 | if (!access_ok(VERIFY_WRITE, optlen, sizeof(*optlen)) || | ||
695 | __put_user(ulen, optlen)) | ||
696 | return -EFAULT; | ||
697 | |||
698 | if (!access_ok(VERIFY_READ, kgf, klen) || | ||
699 | !access_ok(VERIFY_WRITE, gf32, ulen) || | ||
700 | __get_user(interface, &kgf->gf_interface) || | ||
701 | __get_user(fmode, &kgf->gf_fmode) || | ||
702 | __get_user(numsrc, &kgf->gf_numsrc) || | ||
703 | __put_user(interface, &gf32->gf_interface) || | ||
704 | __put_user(fmode, &gf32->gf_fmode) || | ||
705 | __put_user(numsrc, &gf32->gf_numsrc)) | ||
706 | return -EFAULT; | ||
707 | if (numsrc) { | ||
708 | int copylen; | ||
709 | |||
710 | klen -= GROUP_FILTER_SIZE(0); | ||
711 | copylen = numsrc * sizeof(gf32->gf_slist[0]); | ||
712 | if (copylen > klen) | ||
713 | copylen = klen; | ||
714 | if (copy_in_user(gf32->gf_slist, kgf->gf_slist, copylen)) | ||
715 | return -EFAULT; | ||
716 | } | ||
717 | return err; | ||
718 | } | ||
719 | |||
720 | EXPORT_SYMBOL(compat_mc_getsockopt); | ||
721 | |||
643 | 722 | ||
644 | /* Argument list sizes for compat_sys_socketcall */ | 723 | /* Argument list sizes for compat_sys_socketcall */ |
645 | #define AL(x) ((x) * sizeof(u32)) | 724 | #define AL(x) ((x) * sizeof(u32)) |
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 4d8d95404f45..e0514e82308e 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -1186,7 +1186,14 @@ int ip_getsockopt(struct sock *sk, int level, | |||
1186 | int compat_ip_getsockopt(struct sock *sk, int level, int optname, | 1186 | int compat_ip_getsockopt(struct sock *sk, int level, int optname, |
1187 | char __user *optval, int __user *optlen) | 1187 | char __user *optval, int __user *optlen) |
1188 | { | 1188 | { |
1189 | int err = do_ip_getsockopt(sk, level, optname, optval, optlen); | 1189 | int err; |
1190 | |||
1191 | if (optname == MCAST_MSFILTER) | ||
1192 | return compat_mc_getsockopt(sk, level, optname, optval, optlen, | ||
1193 | ip_getsockopt); | ||
1194 | |||
1195 | err = do_ip_getsockopt(sk, level, optname, optval, optlen); | ||
1196 | |||
1190 | #ifdef CONFIG_NETFILTER | 1197 | #ifdef CONFIG_NETFILTER |
1191 | /* we need to exclude all possible ENOPROTOOPTs except default case */ | 1198 | /* we need to exclude all possible ENOPROTOOPTs except default case */ |
1192 | if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && | 1199 | if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index db6fdc1498aa..b4a26f2505f8 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -1089,6 +1089,10 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1089 | if(level != SOL_IPV6) | 1089 | if(level != SOL_IPV6) |
1090 | return -ENOPROTOOPT; | 1090 | return -ENOPROTOOPT; |
1091 | 1091 | ||
1092 | if (optname == MCAST_MSFILTER) | ||
1093 | return compat_mc_getsockopt(sk, level, optname, optval, optlen, | ||
1094 | ipv6_getsockopt); | ||
1095 | |||
1092 | err = do_ipv6_getsockopt(sk, level, optname, optval, optlen); | 1096 | err = do_ipv6_getsockopt(sk, level, optname, optval, optlen); |
1093 | #ifdef CONFIG_NETFILTER | 1097 | #ifdef CONFIG_NETFILTER |
1094 | /* we need to exclude all possible ENOPROTOOPTs except default case */ | 1098 | /* we need to exclude all possible ENOPROTOOPTs except default case */ |