diff options
author | Dmitry Mishin <dim@openvz.org> | 2006-03-20 22:45:21 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-03-20 22:45:21 -0800 |
commit | 3fdadf7d27e3fbcf72930941884387d1f4936f04 (patch) | |
tree | 167072cf1e60b6b307610563614b435ff0caa52d /net | |
parent | c750360938b403e6cc193d293cfbcb099dd6c60e (diff) |
[NET]: {get|set}sockopt compatibility layer
This patch extends {get|set}sockopt compatibility layer in order to
move protocol specific parts to their place and avoid huge universal
net/compat.c file in the future.
Signed-off-by: Dmitry Mishin <dim@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/compat.c | 95 | ||||
-rw-r--r-- | net/core/sock.c | 28 | ||||
-rw-r--r-- | net/dccp/dccp.h | 8 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 12 | ||||
-rw-r--r-- | net/dccp/ipv6.c | 16 | ||||
-rw-r--r-- | net/dccp/proto.c | 67 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 12 | ||||
-rw-r--r-- | net/ipv4/ip_sockglue.c | 142 | ||||
-rw-r--r-- | net/ipv4/raw.c | 50 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 77 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 8 | ||||
-rw-r--r-- | net/ipv4/udp.c | 51 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 12 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 163 | ||||
-rw-r--r-- | net/ipv6/ipv6_syms.c | 4 | ||||
-rw-r--r-- | net/ipv6/raw.c | 112 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 12 | ||||
-rw-r--r-- | net/ipv6/udp.c | 52 | ||||
-rw-r--r-- | net/netfilter/nf_sockopt.c | 69 | ||||
-rw-r--r-- | net/sctp/ipv6.c | 8 | ||||
-rw-r--r-- | net/sctp/protocol.c | 8 |
21 files changed, 867 insertions, 139 deletions
diff --git a/net/compat.c b/net/compat.c index e593dace2fd..13177a1a4b3 100644 --- a/net/compat.c +++ b/net/compat.c @@ -416,7 +416,7 @@ struct compat_sock_fprog { compat_uptr_t filter; /* struct sock_filter * */ }; -static int do_set_attach_filter(int fd, int level, int optname, +static int do_set_attach_filter(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; @@ -432,11 +432,12 @@ static int do_set_attach_filter(int fd, int level, int optname, __put_user(compat_ptr(ptr), &kfprog->filter)) return -EFAULT; - return sys_setsockopt(fd, level, optname, (char __user *)kfprog, + return sock_setsockopt(sock, level, optname, (char __user *)kfprog, sizeof(struct sock_fprog)); } -static int do_set_sock_timeout(int fd, int level, int optname, char __user *optval, int optlen) +static int do_set_sock_timeout(struct socket *sock, int level, + int optname, char __user *optval, int optlen) { struct compat_timeval __user *up = (struct compat_timeval __user *) optval; struct timeval ktime; @@ -451,30 +452,61 @@ static int do_set_sock_timeout(int fd, int level, int optname, char __user *optv return -EFAULT; old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_setsockopt(fd, level, optname, (char *) &ktime, sizeof(ktime)); + err = sock_setsockopt(sock, level, optname, (char *) &ktime, sizeof(ktime)); set_fs(old_fs); return err; } +static int compat_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int optlen) +{ + if (optname == SO_ATTACH_FILTER) + return do_set_attach_filter(sock, level, optname, + optval, optlen); + if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) + return do_set_sock_timeout(sock, level, optname, optval, optlen); + + return sock_setsockopt(sock, level, optname, optval, optlen); +} + asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen) { + int err; + struct socket *sock; + /* SO_SET_REPLACE seems to be the same in all levels */ if (optname == IPT_SO_SET_REPLACE) return do_netfilter_replace(fd, level, optname, optval, optlen); - if (level == SOL_SOCKET && optname == SO_ATTACH_FILTER) - return do_set_attach_filter(fd, level, optname, - optval, optlen); - if (level == SOL_SOCKET && - (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) - return do_set_sock_timeout(fd, level, optname, optval, optlen); - return sys_setsockopt(fd, level, optname, optval, optlen); + if (optlen < 0) + return -EINVAL; + + if ((sock = sockfd_lookup(fd, &err))!=NULL) + { + err = security_socket_setsockopt(sock,level,optname); + if (err) { + sockfd_put(sock); + return err; + } + + if (level == SOL_SOCKET) + err = compat_sock_setsockopt(sock, level, + optname, optval, optlen); + else if (sock->ops->compat_setsockopt) + err = sock->ops->compat_setsockopt(sock, level, + optname, optval, optlen); + else + err = sock->ops->setsockopt(sock, level, + optname, optval, optlen); + sockfd_put(sock); + } + return err; } -static int do_get_sock_timeout(int fd, int level, int optname, +static int do_get_sock_timeout(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct compat_timeval __user *up; @@ -490,7 +522,7 @@ static int do_get_sock_timeout(int fd, int level, int optname, len = sizeof(ktime); old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_getsockopt(fd, level, optname, (char *) &ktime, &len); + err = sock_getsockopt(sock, level, optname, (char *) &ktime, &len); set_fs(old_fs); if (!err) { @@ -503,15 +535,42 @@ static int do_get_sock_timeout(int fd, int level, int optname, return err; } -asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, +static int compat_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_SOCKET && - (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) - return do_get_sock_timeout(fd, level, optname, optval, optlen); - return sys_getsockopt(fd, level, optname, optval, optlen); + if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) + return do_get_sock_timeout(sock, level, optname, optval, optlen); + return sock_getsockopt(sock, level, optname, optval, optlen); } +asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, + char __user *optval, int __user *optlen) +{ + int err; + struct socket *sock; + + if ((sock = sockfd_lookup(fd, &err))!=NULL) + { + err = security_socket_getsockopt(sock, level, + optname); + if (err) { + sockfd_put(sock); + return err; + } + + if (level == SOL_SOCKET) + err = compat_sock_getsockopt(sock, level, + optname, optval, optlen); + else if (sock->ops->compat_getsockopt) + err = sock->ops->compat_getsockopt(sock, level, + optname, optval, optlen); + else + err = sock->ops->getsockopt(sock, level, + optname, optval, optlen); + sockfd_put(sock); + } + return err; +} /* Argument list sizes for compat_sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), diff --git a/net/core/sock.c b/net/core/sock.c index 5038a5a7bd8..dd63cdea3fe 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1385,6 +1385,20 @@ int sock_common_getsockopt(struct socket *sock, int level, int optname, EXPORT_SYMBOL(sock_common_getsockopt); +#ifdef CONFIG_COMPAT +int compat_sock_common_getsockopt(struct socket *sock, int level, + int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + + if (sk->sk_prot->compat_setsockopt) + return sk->sk_prot->compat_getsockopt(sk, level, + optname, optval, optlen); + return sk->sk_prot->getsockopt(sk, level, optname, optval, optlen); +} +EXPORT_SYMBOL(compat_sock_common_getsockopt); +#endif + int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { @@ -1414,6 +1428,20 @@ int sock_common_setsockopt(struct socket *sock, int level, int optname, EXPORT_SYMBOL(sock_common_setsockopt); +#ifdef CONFIG_COMPAT +int compat_sock_common_setsockopt(struct socket *sock, + int level, int optname, char __user *optval, int optlen) +{ + struct sock *sk = sock->sk; + + if (sk->sk_prot->compat_setsockopt) + return sk->sk_prot->compat_setsockopt(sk, level, + optname, optval, optlen); + return sk->sk_prot->setsockopt(sk, level, optname, optval, optlen); +} +EXPORT_SYMBOL(compat_sock_common_setsockopt); +#endif + void sk_common_release(struct sock *sk) { if (sk->sk_prot->destroy) diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 34e70fb89d4..47de17208d7 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -192,6 +192,14 @@ extern int dccp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); extern int dccp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen); +#ifdef CONFIG_COMPAT +extern int compat_dccp_getsockopt(struct sock *sk, + int level, int optname, + char __user *optval, int __user *optlen); +extern int compat_dccp_setsockopt(struct sock *sk, + int level, int optname, + char __user *optval, int optlen); +#endif extern int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t size); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 80d450ba621..8a33c8498d9 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -994,6 +994,10 @@ static struct inet_connection_sock_af_ops dccp_ipv4_af_ops = { .net_header_len = sizeof(struct iphdr), .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ip_setsockopt, + .compat_getsockopt = compat_ip_getsockopt, +#endif .addr2sockaddr = inet_csk_addr2sockaddr, .sockaddr_len = sizeof(struct sockaddr_in), }; @@ -1040,6 +1044,10 @@ static struct proto dccp_v4_prot = { .init = dccp_v4_init_sock, .setsockopt = dccp_setsockopt, .getsockopt = dccp_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_dccp_setsockopt, + .compat_getsockopt = compat_dccp_getsockopt, +#endif .sendmsg = dccp_sendmsg, .recvmsg = dccp_recvmsg, .backlog_rcv = dccp_v4_do_rcv, @@ -1079,6 +1087,10 @@ static const struct proto_ops inet_dccp_ops = { .shutdown = inet_shutdown, .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif .sendmsg = inet_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 7c8233f6d3c..89106c7d324 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -1114,6 +1114,10 @@ static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { .net_header_len = sizeof(struct ipv6hdr), .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ipv6_setsockopt, + .compat_getsockopt = compat_ipv6_getsockopt, +#endif .addr2sockaddr = inet6_csk_addr2sockaddr, .sockaddr_len = sizeof(struct sockaddr_in6) }; @@ -1130,6 +1134,10 @@ static struct inet_connection_sock_af_ops dccp_ipv6_mapped = { .net_header_len = sizeof(struct iphdr), .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ipv6_setsockopt, + .compat_getsockopt = compat_ipv6_getsockopt, +#endif .addr2sockaddr = inet6_csk_addr2sockaddr, .sockaddr_len = sizeof(struct sockaddr_in6) }; @@ -1167,6 +1175,10 @@ static struct proto dccp_v6_prot = { .init = dccp_v6_init_sock, .setsockopt = dccp_setsockopt, .getsockopt = dccp_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_dccp_setsockopt, + .compat_getsockopt = compat_dccp_getsockopt, +#endif .sendmsg = dccp_sendmsg, .recvmsg = dccp_recvmsg, .backlog_rcv = dccp_v6_do_rcv, @@ -1204,6 +1216,10 @@ static struct proto_ops inet6_dccp_ops = { .shutdown = inet_shutdown, .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif .sendmsg = inet_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, diff --git a/net/dccp/proto.c b/net/dccp/proto.c index baccaf35ffb..59b214995f2 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -455,18 +455,13 @@ out_free_val: goto out; } -int dccp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, int optlen) +static int do_dccp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) { struct dccp_sock *dp; int err; int val; - if (level != SOL_DCCP) - return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, - optname, optval, - optlen); - if (optlen < sizeof(int)) return -EINVAL; @@ -512,8 +507,34 @@ int dccp_setsockopt(struct sock *sk, int level, int optname, return err; } +int dccp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) +{ + if (level != SOL_DCCP) + return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, + optname, optval, + optlen); + return do_dccp_setsockopt(sk, level, optname, optval, optlen); +} EXPORT_SYMBOL_GPL(dccp_setsockopt); +#ifdef CONFIG_COMPAT +int compat_dccp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) +{ + if (level != SOL_DCCP) { + if (inet_csk(sk)->icsk_af_ops->compat_setsockopt) + return inet_csk(sk)->icsk_af_ops->compat_setsockopt(sk, + level, optname, optval, optlen); + else + return inet_csk(sk)->icsk_af_ops->setsockopt(sk, + level, optname, optval, optlen); + } + return do_dccp_setsockopt(sk, level, optname, optval, optlen); +} +EXPORT_SYMBOL_GPL(compat_dccp_setsockopt); +#endif + static int dccp_getsockopt_service(struct sock *sk, int len, __be32 __user *optval, int __user *optlen) @@ -545,16 +566,12 @@ out: return err; } -int dccp_getsockopt(struct sock *sk, int level, int optname, +static int do_dccp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { struct dccp_sock *dp; int val, len; - if (level != SOL_DCCP) - return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level, - optname, optval, - optlen); if (get_user(len, optlen)) return -EFAULT; @@ -587,8 +604,34 @@ int dccp_getsockopt(struct sock *sk, int level, int optname, return 0; } +int dccp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + if (level != SOL_DCCP) + return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level, + optname, optval, + optlen); + return do_dccp_getsockopt(sk, level, optname, optval, optlen); +} EXPORT_SYMBOL_GPL(dccp_getsockopt); +#ifdef CONFIG_COMPAT +int compat_dccp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + if (level != SOL_DCCP) { + if (inet_csk(sk)->icsk_af_ops->compat_setsockopt) + return inet_csk(sk)->icsk_af_ops->compat_getsockopt(sk, + level, optname, optval, optlen); + else + return inet_csk(sk)->icsk_af_ops->getsockopt(sk, + level, optname, optval, optlen); + } + return do_dccp_getsockopt(sk, level, optname, optval, optlen); +} +EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); +#endif + int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 97c276f95b3..454e523b506 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -802,6 +802,10 @@ const struct proto_ops inet_stream_ops = { .shutdown = inet_shutdown, .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif .sendmsg = inet_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, @@ -823,6 +827,10 @@ const struct proto_ops inet_dgram_ops = { .shutdown = inet_shutdown, .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif .sendmsg = inet_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, @@ -848,6 +856,10 @@ static const struct proto_ops inet_sockraw_ops = { .shutdown = inet_shutdown, .setsockopt = sock_common_setsockopt, .getsockopt = sock_common_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif .sendmsg = inet_sendmsg, .recvmsg = sock_common_recvmsg, .mmap = sock_no_mmap, diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index b5c4f61518e..49ff1cd4e1c 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -399,14 +399,12 @@ out: * an IP socket. */ -int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) +static int do_ip_setsockopt(struct sock *sk, int level, + int optname, char __user *optval, int optlen) { struct inet_sock *inet = inet_sk(sk); int val=0,err; - if (level != SOL_IP) - return -ENOPROTOOPT; - if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | (1<<IP_RETOPTS) | (1<<IP_TOS) | @@ -875,12 +873,7 @@ mc_msf_out: break; default: -#ifdef CONFIG_NETFILTER - err = nf_setsockopt(sk, PF_INET, optname, optval, - optlen); -#else err = -ENOPROTOOPT; -#endif break; } release_sock(sk); @@ -891,12 +884,66 @@ e_inval: return -EINVAL; } +int ip_setsockopt(struct sock *sk, int level, + int optname, char __user *optval, int optlen) +{ + int err; + + if (level != SOL_IP) + return -ENOPROTOOPT; + + err = do_ip_setsockopt(sk, level, optname, optval, optlen); +#ifdef CONFIG_NETFILTER + /* we need to exclude all possible ENOPROTOOPTs except default case */ + if (err == -ENOPROTOOPT && optname != IP_HDRINCL && + optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY +#ifdef CONFIG_IP_MROUTE + && (optname < MRT_BASE || optname > (MRT_BASE + 10)) +#endif + ) { + lock_sock(sk); + err = nf_setsockopt(sk, PF_INET, optname, optval, optlen); + release_sock(sk); + } +#endif + return err; +} + +#ifdef CONFIG_COMPAT +int compat_ip_setsockopt(struct sock *sk, int level, + int optname, char __user *optval, int optlen) +{ + int err; + + if (level != SOL_IP) + return -ENOPROTOOPT; + + err = do_ip_setsockopt(sk, level, optname, optval, optlen); +#ifdef CONFIG_NETFILTER + /* we need to exclude all possible ENOPROTOOPTs except default case */ + if (err == -ENOPROTOOPT && optname != IP_HDRINCL && + optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY +#ifdef CONFIG_IP_MROUTE + && (optname < MRT_BASE || optname > (MRT_BASE + 10)) +#endif + ) { + lock_sock(sk); + err = compat_nf_setsockopt(sk, PF_INET, + optname, optval, optlen); + release_sock(sk); + } +#endif + return err; +} +#endif + /* * Get the options. Note for future reference. The GET of IP options gets the * _received_ ones. The set sets the _sent_ ones. */ -int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) +static int do_ip_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) { struct inet_sock *inet = inet_sk(sk); int val; @@ -1080,17 +1127,8 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, val = inet->freebind; break; default: -#ifdef CONFIG_NETFILTER - val = nf_getsockopt(sk, PF_INET, optname, optval, - &len); - release_sock(sk); - if (val >= 0) - val = put_user(len, optlen); - return val; -#else release_sock(sk); return -ENOPROTOOPT; -#endif } release_sock(sk); @@ -1111,7 +1149,73 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, return 0; } +int ip_getsockopt(struct sock *sk, int level, + int optname, char __user *optval, int __user *optlen) +{ + int err; + + err = do_ip_getsockopt(sk, level, optname, optval, optlen); +#ifdef CONFIG_NETFILTER + /* we need to exclude all possible ENOPROTOOPTs except default case */ + if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS +#ifdef CONFIG_IP_MROUTE + && (optname < MRT_BASE || optname > MRT_BASE+10) +#endif + ) { + int len; + + if(get_user(len,optlen)) + return -EFAULT; + + lock_sock(sk); + err = nf_getsockopt(sk, PF_INET, optname, optval, + &len); + release_sock(sk); + if (err >= 0) + err = put_user(len, optlen); + return err; + } +#endif + return err; +} + +#ifdef CONFIG_COMPAT +int compat_ip_getsockopt(struct sock *sk, int level, + int optname, char __user *optval, int __user *optlen) +{ + int err; + + err = do_ip_getsockopt(sk, level, optname, optval, optlen); +#ifdef CONFIG_NETFILTER + /* we need to exclude all possible ENOPROTOOPTs except default case */ + if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS +#ifdef CONFIG_IP_MROUTE + && (optname < MRT_BASE || optname > MRT_BASE+10) +#endif + ) { + int len; + + if(get_user(len,optlen)) + return -EFAULT; + + lock_sock(sk); + err = compat_nf_getsockopt(sk, PF_INET, + optname, optval, &len); + release_sock(sk); + if (err >= 0) + err = put_user(len, optlen); + return err; + } +#endif + return err; +} +#endif + EXPORT_SYMBOL(ip_cmsg_recv); EXPORT_SYMBOL(ip_getsockopt); EXPORT_SYMBOL(ip_setsockopt); +#ifdef CONFIG_COMPAT +EXPORT_SYMBOL(compat_ip_getsockopt); +EXPORT_SYMBOL(compat_ip_setsockopt); +#endif diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index f29a12da510..f1b02b34fc0 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -660,12 +660,9 @@ static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *o out: return ret; } -static int raw_setsockopt(struct sock *sk, int level, int optname, +static int do_raw_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) { - if (level != SOL_RAW) - return ip_setsockopt(sk, level, optname, optval, optlen); - if (optname == ICMP_FILTER) { if (inet_sk(sk)->num != IPPROTO_ICMP) return -EOPNOTSUPP; @@ -675,12 +672,28 @@ static int raw_setsockopt(struct sock *sk, int level, int optname, return -ENOPROTOOPT; } -static int raw_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) +static int raw_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) { if (level != SOL_RAW) - return ip_getsockopt(sk, level, optname, optval, optlen); + return ip_setsockopt(sk, level, optname, optval, optlen); + return do_raw_setsockopt(sk, level, optname, optval, optlen); +} +#ifdef CONFIG_COMPAT +static int compat_raw_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) +{ + if (level != SOL_RAW) + return compat_ip_setsockopt(sk, level, + optname, optval, optlen); + return do_raw_setsockopt(sk, level, optname, optval, optlen); +} +#endif + +static int do_raw_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ if (optname == ICMP_FILTER) { if (inet_sk(sk)->num != IPPROTO_ICMP) return -EOPNOTSUPP; @@ -690,6 +703,25 @@ static int raw_getsockopt(struct sock *sk, int level, int optname, return -ENOPROTOOPT; } +static int raw_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + if (level != SOL_RAW) + return ip_getsockopt(sk, level, optname, optval, optlen); + return do_raw_getsockopt(sk, level, optname, optval, optlen); +} + +#ifdef CONFIG_COMPAT +static int compat_raw_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + if (level != SOL_RAW) + return compat_ip_getsockopt(sk, level, + optname, optval, optlen); + return do_raw_getsockopt(sk, level, optname, optval, optlen); +} +#endif + static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) { switch (cmd) { @@ -728,6 +760,10 @@ struct proto raw_prot = { .init = raw_init, .setsockopt = raw_setsockopt, .getsockopt = raw_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_raw_setsockopt, + .compat_getsockopt = compat_raw_getsockopt, +#endif .sendmsg = raw_sendmsg, .recvmsg = raw_recvmsg, .bind = raw_bind, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 00aa80e9324..31b0123a969 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1687,18 +1687,14 @@ int tcp_disconnect(struct sock *sk, int flags) /* * Socket option code for TCP. */ -int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, - int optlen) +static int do_tcp_setsockopt(struct sock *sk, int level, + int optname, char __user *optval, int optlen) { struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); int val; int err = 0; - if (level != SOL_TCP) - return icsk->icsk_af_ops->setsockopt(sk, level, optname, - optval, optlen); - /* This is a string value all the others are int's */ if (optname == TCP_CONGESTION) { char name[TCP_CA_NAME_MAX]; @@ -1871,6 +1867,35 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, return err; } +int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, + int optlen) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + if (level != SOL_TCP) + return icsk->icsk_af_ops->setsockopt(sk, level, optname, + optval, optlen); + return do_tcp_setsockopt(sk, level, optname, optval, optlen); +} + +#ifdef CONFIG_COMPAT +int compat_tcp_setsockopt(struct sock *sk, int level, + int optname, char __user *optval, int optlen) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + if (level != SOL_TCP) { + if (icsk->icsk_af_ops->compat_setsockopt) + return icsk->icsk_af_ops->compat_setsockopt(sk, + level, optname, optval, optlen); + else + return icsk->icsk_af_ops->setsockopt(sk, + level, optname, optval, optlen); + } + return do_tcp_setsockopt(sk, level, optname, optval, optlen); +} +#endif + /* Return information about state of tcp endpoint in API format. */ void tcp_get_info(struct sock *sk, struct tcp_info *info) { @@ -1931,17 +1956,13 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) EXPORT_SYMBOL_GPL(tcp_get_info); -int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, - int __user *optlen) +static int do_tcp_getsockopt(struct sock *sk, int level, + int optname, char __user *optval, int __user *optlen) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); int val, len; - if (level != SOL_TCP) - return icsk->icsk_af_ops->getsockopt(sk, level, optname, - optval, optlen); - if (get_user(len, optlen)) return -EFAULT; @@ -2025,6 +2046,34 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, return 0; } +int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, + int __user *optlen) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + if (level != SOL_TCP) + return icsk->icsk_af_ops->getsockopt(sk, level, optname, + optval, optlen); + return do_tcp_getsockopt(sk, level, optname, optval, optlen); +} + +#ifdef CONFIG_COMPAT +int compat_tcp_getsockopt(struct sock *sk, int level, + int optname, char __user *optval, int __user *optlen) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + if (level != SOL_TCP) { + if (icsk->icsk_af_ops->compat_getsockopt) + return icsk->icsk_af_ops->compat_getsockopt(sk, + level, optname, optval, optlen); + else + return icsk->icsk_af_ops->getsockopt(sk, + level, optname, optval, optlen); + } + return do_tcp_getsockopt(sk, level, optname, optval, optlen); +} +#endif extern void __skb_cb_too_small_for_tcp(int, int); extern struct tcp_congestion_ops tcp_reno; @@ -2142,3 +2191,7 @@ EXPORT_SYMBOL(tcp_sendpage); EXPORT_SYMBOL(tcp_setsockopt); EXPORT_SYMBOL(tcp_shutdown); EXPORT_SYMBOL(tcp_statistics); +#ifdef CONFIG_COMPAT +EXPORT_SYMBOL(compat_tcp_setsockopt); +EXPORT_SYMBOL(compat_tcp_getsockopt); +#endif diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4eb903db1b1..249ef6c8895 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1226,6 +1226,10 @@ struct inet_connection_sock_af_ops ipv4_specific = { .net_header_len = sizeof(struct iphdr), .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ip_setsockopt, + .compat_getsockopt = compat_ip_getsockopt, +#endif .addr2sockaddr = inet_csk_addr2sockaddr, .sockaddr_len = sizeof(struct sockaddr_in), }; @@ -1808,6 +1812,10 @@ struct proto tcp_prot = { .shutdown = tcp_shutdown, .setsockopt = tcp_setsockopt, .getsockopt = tcp_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_tcp_setsockopt, + .compat_getsockopt = compat_tcp_getsockopt, +#endif .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, .backlog_rcv = tcp_v4_do_rcv, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 00840474a44..0b0721bd45c 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1207,16 +1207,13 @@ static int udp_destroy_sock(struct sock *sk) /* * Socket option code for UDP */ -static int udp_setsockopt(struct sock *sk, int level, int optname, +static int do_udp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) { struct udp_sock *up = udp_sk(sk); int val; int err = 0; - if (level != SOL_UDP) - return ip_setsockopt(sk, level, optname, optval, optlen); - if(optlen<sizeof(int)) return -EINVAL; @@ -1256,15 +1253,31 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, return err; } -static int udp_getsockopt(struct sock *sk, int level, int optname, +static int udp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, int optlen) |