Add a kern_setsockopt and kern_getsockopt which can read the option

values from either user land or from the kernel. Use them for
[gs]etsockopt and to clean up some calls to [gs]etsockopt in the
Linux emulation code that uses the stackgap.
This commit is contained in:
David Malone 2004-07-17 21:06:36 +00:00
parent 44f4ffe202
commit e140eb430c
3 changed files with 109 additions and 98 deletions

@ -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;
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;
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;
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);
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;

@ -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);
}
error = kern_getsockopt(td, uap->s, uap->level, uap->name,
uap->val, UIO_USERSPACE, &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 = 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 = sogetopt(so, &sopt);
if (error == 0) {
valsize = sopt.sopt_valsize;
error = copyout(&valsize, uap->avalsize, sizeof (valsize));
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);
}

@ -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,