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:
parent
44f4ffe202
commit
e140eb430c
sys
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user