diff options
-rw-r--r-- | sys/compat/linux/linux_socket.c | 84 | ||||
-rw-r--r-- | sys/kern/uipc_syscalls.c | 120 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 5 |
3 files changed, 110 insertions, 99 deletions
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index c73a57a..85c71a6 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -402,35 +402,14 @@ bad: /* Return 0 if IP_HDRINCL is set for the given socket. */ static int -linux_check_hdrincl(struct thread *td, caddr_t *sg, int s) +linux_check_hdrincl(struct thread *td, int s) { - struct getsockopt_args /* { - int s; - int level; - int name; - void * __restrict val; - socklen_t * __restrict avalsize; - } */ bsd_args; - void * __restrict val; - socklen_t * __restrict valsize; int error, optval, size_val; - val = stackgap_alloc(sg, sizeof(size_val)); - valsize = stackgap_alloc(sg, sizeof(socklen_t)); - - size_val = sizeof(val); - if ((error = copyout(&size_val, valsize, sizeof(size_val)))) - return (error); - - bsd_args.s = s; - bsd_args.level = IPPROTO_IP; - bsd_args.name = IP_HDRINCL; - bsd_args.val = val; - bsd_args.avalsize = valsize; - if ((error = getsockopt(td, &bsd_args))) - return (error); - - if ((error = copyin(val, &optval, sizeof(optval)))) + size_val = sizeof(optval); + error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL, + &optval, UIO_SYSSPACE, &size_val); + if (error) return (error); return (optval == 0); @@ -461,6 +440,7 @@ linux_sendto_hdrincl(struct thread *td, caddr_t *sg, struct linux_sendto_args *l #define linux_ip_copysize 8 struct ip *packet; + caddr_t sg = stackgap_init(); struct msghdr msg; struct iovec aiov[2]; int error; @@ -515,13 +495,6 @@ linux_socket(struct thread *td, struct linux_socket_args *args) int type; int protocol; } */ bsd_args; - struct setsockopt_args /* { - int s; - int level; - int name; - caddr_t val; - int valsize; - } */ bsd_setsockopt_args; int error; int retval_socket; @@ -540,21 +513,12 @@ linux_socket(struct thread *td, struct linux_socket_args *args) && bsd_args.domain == AF_INET && retval_socket >= 0) { /* It's a raw IP socket: set the IP_HDRINCL option. */ - caddr_t sg; - int *hdrincl; - - sg = stackgap_init(); - hdrincl = (int *)stackgap_alloc(&sg, sizeof(*hdrincl)); - *hdrincl = 1; - bsd_setsockopt_args.s = td->td_retval[0]; - bsd_setsockopt_args.level = IPPROTO_IP; - bsd_setsockopt_args.name = IP_HDRINCL; - bsd_setsockopt_args.val = (caddr_t)hdrincl; - bsd_setsockopt_args.valsize = sizeof(*hdrincl); - /* We ignore any error returned by setsockopt() */ - setsockopt(td, &bsd_setsockopt_args); - /* Copy back the return value from socket() */ - td->td_retval[0] = bsd_setsockopt_args.s; + int hdrincl; + + hdrincl = 1; + /* We ignore any error returned by kern_setsockopt() */ + kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL, + &hdrincl, UIO_SYSSPACE, sizeof(hdrincl)); } #ifdef INET6 /* @@ -571,21 +535,12 @@ linux_socket(struct thread *td, struct linux_socket_args *args) && ip6_v6only #endif ) { - caddr_t sg; - int *v6only; - - sg = stackgap_init(); - v6only = (int *)stackgap_alloc(&sg, sizeof(*v6only)); - *v6only = 0; - bsd_setsockopt_args.s = td->td_retval[0]; - bsd_setsockopt_args.level = IPPROTO_IPV6; - bsd_setsockopt_args.name = IPV6_V6ONLY; - bsd_setsockopt_args.val = (caddr_t)v6only; - bsd_setsockopt_args.valsize = sizeof(*v6only); + int v6only; + + v6only = 0; /* We ignore any error returned by setsockopt() */ - setsockopt(td, &bsd_setsockopt_args); - /* Copy back the return value from socket() */ - td->td_retval[0] = bsd_setsockopt_args.s; + kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY, + &v6only, UIO_SYSSPACE, sizeof(v6only)); } #endif @@ -916,15 +871,14 @@ linux_sendto(struct thread *td, struct linux_sendto_args *args) struct linux_sendto_args linux_args; struct msghdr msg; struct iovec aiov; - caddr_t sg = stackgap_init(); int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); - if (linux_check_hdrincl(td, &sg, linux_args.s) == 0) + if (linux_check_hdrincl(td, linux_args.s) == 0) /* IP_HDRINCL set, tweak the packet before sending */ - return (linux_sendto_hdrincl(td, &sg, &linux_args)); + return (linux_sendto_hdrincl(td, &linux_args)); msg.msg_name = linux_args.to; msg.msg_namelen = linux_args.tolen; diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index eeff60d..238dee4 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -1242,23 +1242,48 @@ setsockopt(td, uap) int valsize; } */ *uap; { + + return (kern_setsockopt(td, uap->s, uap->level, uap->name, + uap->val, UIO_USERSPACE, uap->valsize)); +} + +int +kern_setsockopt(td, s, level, name, val, valseg, valsize) + struct thread *td; + int s; + int level; + int name; + void *val; + enum uio_seg valseg; + socklen_t valsize; +{ + int error; struct socket *so; struct sockopt sopt; - int error; - if (uap->val == 0 && uap->valsize != 0) + if (val == NULL && valsize != 0) return (EFAULT); - if (uap->valsize < 0) + if (valsize < 0) return (EINVAL); - NET_LOCK_GIANT(); - if ((error = fgetsock(td, uap->s, &so, NULL)) == 0) { - sopt.sopt_dir = SOPT_SET; - sopt.sopt_level = uap->level; - sopt.sopt_name = uap->name; - sopt.sopt_val = uap->val; - sopt.sopt_valsize = uap->valsize; + sopt.sopt_dir = SOPT_SET; + sopt.sopt_level = level; + sopt.sopt_name = name; + sopt.sopt_val = val; + sopt.sopt_valsize = valsize; + switch (valseg) { + case UIO_USERSPACE: sopt.sopt_td = td; + break; + case UIO_SYSSPACE: + sopt.sopt_td = NULL; + break; + default: + panic("kern_setsockopt called with bad valseg"); + } + + NET_LOCK_GIANT(); + if ((error = fgetsock(td, s, &so, NULL)) == 0) { error = sosetopt(so, &sopt); fputsock(so); } @@ -1283,39 +1308,66 @@ getsockopt(td, uap) { socklen_t valsize; int error; - struct socket *so; - struct sockopt sopt; - NET_LOCK_GIANT(); - if ((error = fgetsock(td, uap->s, &so, NULL)) != 0) - goto done2; if (uap->val) { error = copyin(uap->avalsize, &valsize, sizeof (valsize)); if (error) - goto done1; - if (valsize < 0) { - error = EINVAL; - goto done1; - } - } else { - valsize = 0; + return (error); } - sopt.sopt_dir = SOPT_GET; - sopt.sopt_level = uap->level; - sopt.sopt_name = uap->name; - sopt.sopt_val = uap->val; - sopt.sopt_valsize = (size_t)valsize; /* checked non-negative above */ - sopt.sopt_td = td; + error = kern_getsockopt(td, uap->s, uap->level, uap->name, + uap->val, UIO_USERSPACE, &valsize); - error = sogetopt(so, &sopt); - if (error == 0) { - valsize = sopt.sopt_valsize; + if (error == 0) error = copyout(&valsize, uap->avalsize, sizeof (valsize)); + return (error); +} + +/* + * Kernel version of getsockopt. + * optval can be a userland or userspace. optlen is always a kernel pointer. + */ +int +kern_getsockopt(td, s, level, name, val, valseg, valsize) + struct thread *td; + int s; + int level; + int name; + void *val; + enum uio_seg valseg; + socklen_t *valsize; +{ + int error; + struct socket *so; + struct sockopt sopt; + + if (val == NULL) + *valsize = 0; + if (*valsize < 0) + return (EINVAL); + + sopt.sopt_dir = SOPT_GET; + sopt.sopt_level = level; + sopt.sopt_name = name; + sopt.sopt_val = val; + sopt.sopt_valsize = (size_t)*valsize; /* checked non-negative above */ + switch (valseg) { + case UIO_USERSPACE: + sopt.sopt_td = td; + break; + case UIO_SYSSPACE: + sopt.sopt_td = NULL; + break; + default: + panic("kern_getsockopt called with bad valseg"); + } + + NET_LOCK_GIANT(); + if ((error = fgetsock(td, s, &so, NULL)) == 0) { + error = sogetopt(so, &sopt); + *valsize = sopt.sopt_valsize; + fputsock(so); } -done1: - fputsock(so); -done2: NET_UNLOCK_GIANT(); return (error); } diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 8c52027..82ca801 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -30,6 +30,7 @@ #include <sys/signal.h> #include <sys/uio.h> +#include <sys/socket.h> struct sockaddr; struct msghdr; @@ -49,6 +50,8 @@ int kern_connect(struct thread *td, int fd, struct sockaddr *sa); int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg); int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg); +int kern_getsockopt(struct thread *td, int s, int level, int name, + void *optval, enum uio_seg valseg, socklen_t *valsize); int kern_lchown(struct thread *td, char *path, enum uio_seg pathseg, int uid, int gid); int kern_link(struct thread *td, char *path, char *link, @@ -74,6 +77,8 @@ int kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, fd_set *fd_ex, struct timeval *tvp); int kern_sendit(struct thread *td, int s, struct msghdr *mp, int flags, struct mbuf *control); +int kern_setsockopt(struct thread *td, int s, int level, int name, + void *optval, enum uio_seg valseg, socklen_t valsize); int kern_shmat(struct thread *td, int shmid, const void *shmaddr, int shmflg); int kern_shmctl(struct thread *td, int shmid, int cmd, void *buf, |