summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authordwmalone <dwmalone@FreeBSD.org>2004-07-17 21:06:36 +0000
committerdwmalone <dwmalone@FreeBSD.org>2004-07-17 21:06:36 +0000
commitc8c1b8f415a3f8e57219cf3587707d7d24b8d582 (patch)
treedca356ba37d0e4e58bfdf5b38929557fe4c9839d /sys
parent7beb6d42ceb4bcb8294f42ff6a0a292e434bcc88 (diff)
downloadFreeBSD-src-c8c1b8f415a3f8e57219cf3587707d7d24b8d582.zip
FreeBSD-src-c8c1b8f415a3f8e57219cf3587707d7d24b8d582.tar.gz
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.
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/linux/linux_socket.c84
-rw-r--r--sys/kern/uipc_syscalls.c120
-rw-r--r--sys/sys/syscallsubr.h5
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,
OpenPOWER on IntegriCloud