diff options
author | David L Stevens <dlstevens@us.ibm.com> | 2008-04-27 04:06:07 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-04-27 17:26:53 -0400 |
commit | dae50295488f35d2d617b08a5fae43154c947eec (patch) | |
tree | afee67a0e85f4a168e0bb75423f1b703f48baad3 | |
parent | 01a2202c95989a4df48e9a5b5e013cb80c6b2d66 (diff) |
ipv4/ipv6 compat: Fix SSM applications on 64bit kernels.
Add support on 64-bit kernels for seting 32-bit compatible MCAST*
socket options.
Signed-off-by: David L Stevens <dlstevens@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/compat.h | 3 | ||||
-rw-r--r-- | net/compat.c | 117 | ||||
-rw-r--r-- | net/ipv4/ip_sockglue.c | 5 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 5 |
4 files changed, 130 insertions, 0 deletions
diff --git a/include/net/compat.h b/include/net/compat.h index 406db242f73a..05fa5d0254ab 100644 --- a/include/net/compat.h +++ b/include/net/compat.h | |||
@@ -40,4 +40,7 @@ extern int put_cmsg_compat(struct msghdr*, int, int, int, void *); | |||
40 | 40 | ||
41 | extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int); | 41 | extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int); |
42 | 42 | ||
43 | extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, int, | ||
44 | int (*)(struct sock *, int, int, char __user *, int)); | ||
45 | |||
43 | #endif /* NET_COMPAT_H */ | 46 | #endif /* NET_COMPAT_H */ |
diff --git a/net/compat.c b/net/compat.c index 80013fb69a61..01bf95d0832e 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | #include <net/scm.h> | 25 | #include <net/scm.h> |
26 | #include <net/sock.h> | 26 | #include <net/sock.h> |
27 | #include <net/ip.h> | ||
28 | #include <net/ipv6.h> | ||
27 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
28 | #include <net/compat.h> | 30 | #include <net/compat.h> |
29 | 31 | ||
@@ -521,6 +523,121 @@ asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, | |||
521 | } | 523 | } |
522 | return err; | 524 | return err; |
523 | } | 525 | } |
526 | |||
527 | struct compat_group_req { | ||
528 | __u32 gr_interface; | ||
529 | struct __kernel_sockaddr_storage gr_group | ||
530 | __attribute__ ((aligned(4))); | ||
531 | } __attribute__ ((packed)); | ||
532 | |||
533 | struct compat_group_source_req { | ||
534 | __u32 gsr_interface; | ||
535 | struct __kernel_sockaddr_storage gsr_group | ||
536 | __attribute__ ((aligned(4))); | ||
537 | struct __kernel_sockaddr_storage gsr_source | ||
538 | __attribute__ ((aligned(4))); | ||
539 | } __attribute__ ((packed)); | ||
540 | |||
541 | struct compat_group_filter { | ||
542 | __u32 gf_interface; | ||
543 | struct __kernel_sockaddr_storage gf_group | ||
544 | __attribute__ ((aligned(4))); | ||
545 | __u32 gf_fmode; | ||
546 | __u32 gf_numsrc; | ||
547 | struct __kernel_sockaddr_storage gf_slist[1] | ||
548 | __attribute__ ((aligned(4))); | ||
549 | } __attribute__ ((packed)); | ||
550 | |||
551 | |||
552 | int compat_mc_setsockopt(struct sock *sock, int level, int optname, | ||
553 | char __user *optval, int optlen, | ||
554 | int (*setsockopt)(struct sock *,int,int,char __user *,int)) | ||
555 | { | ||
556 | char __user *koptval = optval; | ||
557 | int koptlen = optlen; | ||
558 | |||
559 | switch (optname) { | ||
560 | case MCAST_JOIN_GROUP: | ||
561 | case MCAST_LEAVE_GROUP: | ||
562 | { | ||
563 | struct compat_group_req __user *gr32 = (void *)optval; | ||
564 | struct group_req __user *kgr = | ||
565 | compat_alloc_user_space(sizeof(struct group_req)); | ||
566 | u32 interface; | ||
567 | |||
568 | if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) || | ||
569 | !access_ok(VERIFY_WRITE, kgr, sizeof(struct group_req)) || | ||
570 | __get_user(interface, &gr32->gr_interface) || | ||
571 | __put_user(interface, &kgr->gr_interface) || | ||
572 | copy_in_user(&kgr->gr_group, &gr32->gr_group, | ||
573 | sizeof(kgr->gr_group))) | ||
574 | return -EFAULT; | ||
575 | koptval = (char __user *)kgr; | ||
576 | koptlen = sizeof(struct group_req); | ||
577 | break; | ||
578 | } | ||
579 | case MCAST_JOIN_SOURCE_GROUP: | ||
580 | case MCAST_LEAVE_SOURCE_GROUP: | ||
581 | case MCAST_BLOCK_SOURCE: | ||
582 | case MCAST_UNBLOCK_SOURCE: | ||
583 | { | ||
584 | struct compat_group_source_req __user *gsr32 = (void *)optval; | ||
585 | struct group_source_req *kgsr = compat_alloc_user_space( | ||
586 | sizeof(struct group_source_req)); | ||
587 | u32 interface; | ||
588 | |||
589 | if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) || | ||
590 | !access_ok(VERIFY_WRITE, kgsr, | ||
591 | sizeof(struct group_source_req)) || | ||
592 | __get_user(interface, &gsr32->gsr_interface) || | ||
593 | __put_user(interface, &kgsr->gsr_interface) || | ||
594 | copy_in_user(&kgsr->gsr_group, &gsr32->gsr_group, | ||
595 | sizeof(kgsr->gsr_group)) || | ||
596 | copy_in_user(&kgsr->gsr_source, &gsr32->gsr_source, | ||
597 | sizeof(kgsr->gsr_source))) | ||
598 | return -EFAULT; | ||
599 | koptval = (char __user *)kgsr; | ||
600 | koptlen = sizeof(struct group_source_req); | ||
601 | break; | ||
602 | } | ||
603 | case MCAST_MSFILTER: | ||
604 | { | ||
605 | struct compat_group_filter __user *gf32 = (void *)optval; | ||
606 | struct group_filter *kgf; | ||
607 | u32 interface, fmode, numsrc; | ||
608 | |||
609 | if (!access_ok(VERIFY_READ, gf32, sizeof(*gf32)) || | ||
610 | __get_user(interface, &gf32->gf_interface) || | ||
611 | __get_user(fmode, &gf32->gf_fmode) || | ||
612 | __get_user(numsrc, &gf32->gf_numsrc)) | ||
613 | return -EFAULT; | ||
614 | koptlen = optlen + sizeof(struct group_filter) - | ||
615 | sizeof(struct compat_group_filter); | ||
616 | if (koptlen < GROUP_FILTER_SIZE(numsrc)) | ||
617 | return -EINVAL; | ||
618 | kgf = compat_alloc_user_space(koptlen); | ||
619 | if (!access_ok(VERIFY_WRITE, kgf, koptlen) || | ||
620 | __put_user(interface, &kgf->gf_interface) || | ||
621 | __put_user(fmode, &kgf->gf_fmode) || | ||
622 | __put_user(numsrc, &kgf->gf_numsrc) || | ||
623 | copy_in_user(&kgf->gf_group, &gf32->gf_group, | ||
624 | sizeof(kgf->gf_group)) || | ||
625 | (numsrc && copy_in_user(&kgf->gf_slist, &gf32->gf_slist, | ||
626 | numsrc * sizeof(kgf->gf_slist[0])))) | ||
627 | return -EFAULT; | ||
628 | koptval = (char __user *)kgf; | ||
629 | break; | ||
630 | } | ||
631 | |||
632 | default: | ||
633 | break; | ||
634 | } | ||
635 | return setsockopt(sock, level, optname, koptval, koptlen); | ||
636 | } | ||
637 | |||
638 | EXPORT_SYMBOL(compat_mc_setsockopt); | ||
639 | |||
640 | |||
524 | /* Argument list sizes for compat_sys_socketcall */ | 641 | /* Argument list sizes for compat_sys_socketcall */ |
525 | #define AL(x) ((x) * sizeof(u32)) | 642 | #define AL(x) ((x) * sizeof(u32)) |
526 | static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), | 643 | static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), |
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index d8adfd4972e2..4d8d95404f45 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/mroute.h> | 36 | #include <linux/mroute.h> |
37 | #include <net/route.h> | 37 | #include <net/route.h> |
38 | #include <net/xfrm.h> | 38 | #include <net/xfrm.h> |
39 | #include <net/compat.h> | ||
39 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 40 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
40 | #include <net/transp_v6.h> | 41 | #include <net/transp_v6.h> |
41 | #endif | 42 | #endif |
@@ -923,6 +924,10 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname, | |||
923 | if (level != SOL_IP) | 924 | if (level != SOL_IP) |
924 | return -ENOPROTOOPT; | 925 | return -ENOPROTOOPT; |
925 | 926 | ||
927 | if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) | ||
928 | return compat_mc_setsockopt(sk, level, optname, optval, optlen, | ||
929 | ip_setsockopt); | ||
930 | |||
926 | err = do_ip_setsockopt(sk, level, optname, optval, optlen); | 931 | err = do_ip_setsockopt(sk, level, optname, optval, optlen); |
927 | #ifdef CONFIG_NETFILTER | 932 | #ifdef CONFIG_NETFILTER |
928 | /* we need to exclude all possible ENOPROTOOPTs except default case */ | 933 | /* we need to exclude all possible ENOPROTOOPTs except default case */ |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 06de9d0e1f6b..db6fdc1498aa 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -52,6 +52,7 @@ | |||
52 | #include <net/udp.h> | 52 | #include <net/udp.h> |
53 | #include <net/udplite.h> | 53 | #include <net/udplite.h> |
54 | #include <net/xfrm.h> | 54 | #include <net/xfrm.h> |
55 | #include <net/compat.h> | ||
55 | 56 | ||
56 | #include <asm/uaccess.h> | 57 | #include <asm/uaccess.h> |
57 | 58 | ||
@@ -779,6 +780,10 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
779 | if (level != SOL_IPV6) | 780 | if (level != SOL_IPV6) |
780 | return -ENOPROTOOPT; | 781 | return -ENOPROTOOPT; |
781 | 782 | ||
783 | if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER) | ||
784 | return compat_mc_setsockopt(sk, level, optname, optval, optlen, | ||
785 | ipv6_setsockopt); | ||
786 | |||
782 | err = do_ipv6_setsockopt(sk, level, optname, optval, optlen); | 787 | err = do_ipv6_setsockopt(sk, level, optname, optval, optlen); |
783 | #ifdef CONFIG_NETFILTER | 788 | #ifdef CONFIG_NETFILTER |
784 | /* we need to exclude all possible ENOPROTOOPTs except default case */ | 789 | /* we need to exclude all possible ENOPROTOOPTs except default case */ |