summaryrefslogtreecommitdiffstats
path: root/sys/compat/linux/linux_socket.c
diff options
context:
space:
mode:
authordwmalone <dwmalone@FreeBSD.org>2003-11-09 17:04:04 +0000
committerdwmalone <dwmalone@FreeBSD.org>2003-11-09 17:04:04 +0000
commit3e991a3b59b5351609ea5c3ca9defdbd375cf5f4 (patch)
tree8ff92d9ae0e7c1635b5df0b4f2934df0ed63d0ba /sys/compat/linux/linux_socket.c
parent81bc6553dec2d89470833cd8e710c597fe8c317c (diff)
downloadFreeBSD-src-3e991a3b59b5351609ea5c3ca9defdbd375cf5f4.zip
FreeBSD-src-3e991a3b59b5351609ea5c3ca9defdbd375cf5f4.tar.gz
Use kern_sendit rather than sendit for the Linux send* syscalls.
This means we can avoid using the stack gap for most send* syscalls now (it is still used in the IP_HDRINCL case).
Diffstat (limited to 'sys/compat/linux/linux_socket.c')
-rw-r--r--sys/compat/linux/linux_socket.c249
1 files changed, 105 insertions, 144 deletions
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index bf9d217..2bf1c7e 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/file.h>
#include <sys/limits.h>
#include <sys/malloc.h>
+#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syscallsubr.h>
@@ -333,39 +334,6 @@ linux_to_bsd_msg_flags(int flags)
return ret_flags;
}
-/*
- * Allocate stackgap and put the converted sockaddr structure
- * there, address on stackgap returned in sap.
- */
-static int
-linux_sa_get(caddr_t *sgp, struct sockaddr **sap,
- const struct osockaddr *osa, int *osalen)
-{
- struct sockaddr *sa, *usa;
- int alloclen, error;
-
- alloclen = *osalen;
- error = do_sa_get(&sa, osa, &alloclen, M_TEMP);
- if (error)
- return (error);
-
- usa = (struct sockaddr *) stackgap_alloc(sgp, alloclen);
- if (!usa) {
- error = ENOMEM;
- goto out;
- }
-
- if ((error = copyout(sa, usa, alloclen)))
- goto out;
-
- *sap = usa;
- *osalen = alloclen;
-
-out:
- FREE(sa, M_TEMP);
- return (error);
-}
-
static int
linux_sa_put(struct osockaddr *osa)
{
@@ -392,6 +360,46 @@ linux_sa_put(struct osockaddr *osa)
return (0);
}
+static int
+linux_sendit(struct thread *td, int s, struct msghdr *mp, int flags)
+{
+ struct mbuf *control;
+ struct sockaddr *to;
+ int error;
+
+ if (mp->msg_name != NULL) {
+ error = linux_getsockaddr(&to, mp->msg_name, mp->msg_namelen);
+ if (error)
+ return (error);
+ mp->msg_name = to;
+ } else
+ to = NULL;
+
+ if (mp->msg_control != NULL) {
+ struct cmsghdr *cmsg;
+
+ if (mp->msg_controllen < sizeof(struct cmsghdr)) {
+ error = EINVAL;
+ goto bad;
+ }
+ error = sockargs(&control, mp->msg_control,
+ mp->msg_controllen, MT_CONTROL);
+ if (error)
+ goto bad;
+
+ cmsg = mtod(control, struct cmsghdr *);
+ cmsg->cmsg_level = linux_to_bsd_sockopt_level(cmsg->cmsg_level);
+ } else
+ control = NULL;
+
+ error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control);
+
+bad:
+ if (to)
+ FREE(to, M_SONAME);
+ return (error);
+}
+
/* Return 0 if IP_HDRINCL is set for the given socket. */
static int
linux_check_hdrincl(struct thread *td, caddr_t *sg, int s)
@@ -428,12 +436,21 @@ linux_check_hdrincl(struct thread *td, caddr_t *sg, int s)
return (optval == 0);
}
+struct linux_sendto_args {
+ int s;
+ void *msg;
+ int len;
+ int flags;
+ caddr_t to;
+ int tolen;
+};
+
/*
* Updated sendto() when IP_HDRINCL is set:
* tweak endian-dependent fields in the IP packet.
*/
static int
-linux_sendto_hdrincl(struct thread *td, caddr_t *sg, struct sendto_args *bsd_args)
+linux_sendto_hdrincl(struct thread *td, caddr_t *sg, struct linux_sendto_args *linux_args)
{
/*
* linux_ip_copysize defines how many bytes we should copy
@@ -444,55 +461,43 @@ linux_sendto_hdrincl(struct thread *td, caddr_t *sg, struct sendto_args *bsd_arg
#define linux_ip_copysize 8
struct ip *packet;
- struct msghdr *msg;
- struct iovec *iov;
-
+ struct msghdr msg;
+ struct iovec aiov[2];
int error;
- struct sendmsg_args /* {
- int s;
- caddr_t msg;
- int flags;
- } */ sendmsg_args;
/* Check the packet isn't too small before we mess with it */
- if (bsd_args->len < linux_ip_copysize)
+ if (linux_args->len < linux_ip_copysize)
return (EINVAL);
/*
* Tweaking the user buffer in place would be bad manners.
* We create a corrected IP header with just the needed length,
* then use an iovec to glue it to the rest of the user packet
- * when calling sendmsg().
+ * when calling sendit().
*/
packet = (struct ip *)stackgap_alloc(sg, linux_ip_copysize);
- msg = (struct msghdr *)stackgap_alloc(sg, sizeof(*msg));
- iov = (struct iovec *)stackgap_alloc(sg, sizeof(*iov)*2);
/* Make a copy of the beginning of the packet to be sent */
- if ((error = copyin(bsd_args->buf, packet, linux_ip_copysize)))
+ if ((error = copyin(linux_args->msg, packet, linux_ip_copysize)))
return (error);
/* Convert fields from Linux to BSD raw IP socket format */
- packet->ip_len = bsd_args->len;
+ packet->ip_len = linux_args->len;
packet->ip_off = ntohs(packet->ip_off);
/* Prepare the msghdr and iovec structures describing the new packet */
- msg->msg_name = bsd_args->to;
- msg->msg_namelen = bsd_args->tolen;
- msg->msg_iov = iov;
- msg->msg_iovlen = 2;
- msg->msg_control = NULL;
- msg->msg_controllen = 0;
- msg->msg_flags = 0;
- iov[0].iov_base = (char *)packet;
- iov[0].iov_len = linux_ip_copysize;
- iov[1].iov_base = (char *)(bsd_args->buf) + linux_ip_copysize;
- iov[1].iov_len = bsd_args->len - linux_ip_copysize;
-
- sendmsg_args.s = bsd_args->s;
- sendmsg_args.msg = (caddr_t)msg;
- sendmsg_args.flags = bsd_args->flags;
- return (sendmsg(td, &sendmsg_args));
+ msg.msg_name = linux_args->to;
+ msg.msg_namelen = linux_args->tolen;
+ msg.msg_iov = aiov;
+ msg.msg_iovlen = 2;
+ msg.msg_control = NULL;
+ msg.msg_flags = 0;
+ aiov[0].iov_base = (char *)packet;
+ aiov[0].iov_len = linux_ip_copysize;
+ aiov[1].iov_base = (char *)(linux_args->msg) + linux_ip_copysize;
+ aiov[1].iov_len = linux_args->len - linux_ip_copysize;
+ error = linux_sendit(td, linux_args->s, &msg, linux_args->flags);
+ return (error);
}
struct linux_socket_args {
@@ -895,55 +900,32 @@ linux_recv(struct thread *td, struct linux_recv_args *args)
return (orecv(td, &bsd_args));
}
-struct linux_sendto_args {
- int s;
- void *msg;
- int len;
- int flags;
- caddr_t to;
- int tolen;
-};
-
static int
linux_sendto(struct thread *td, struct linux_sendto_args *args)
{
struct linux_sendto_args linux_args;
- struct sendto_args /* {
- int s;
- caddr_t buf;
- size_t len;
- int flags;
- caddr_t to;
- int tolen;
- } */ bsd_args;
+ struct msghdr msg;
+ struct iovec aiov;
caddr_t sg = stackgap_init();
- struct sockaddr *to;
- int tolen, error;
+ int error;
if ((error = copyin(args, &linux_args, sizeof(linux_args))))
return (error);
- tolen = linux_args.tolen;
- if (linux_args.to) {
- error = linux_sa_get(&sg, &to,
- (struct osockaddr *) linux_args.to, &tolen);
- if (error)
- return (error);
- } else
- to = NULL;
-
- bsd_args.s = linux_args.s;
- bsd_args.buf = linux_args.msg;
- bsd_args.len = linux_args.len;
- bsd_args.flags = linux_args.flags;
- bsd_args.to = (caddr_t) to;
- bsd_args.tolen = (unsigned int) tolen;
-
if (linux_check_hdrincl(td, &sg, linux_args.s) == 0)
/* IP_HDRINCL set, tweak the packet before sending */
- return (linux_sendto_hdrincl(td, &sg, &bsd_args));
-
- return (sendto(td, &bsd_args));
+ return (linux_sendto_hdrincl(td, &sg, &linux_args));
+
+ msg.msg_name = linux_args.to;
+ msg.msg_namelen = linux_args.tolen;
+ msg.msg_iov = &aiov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_flags = 0;
+ aiov.iov_base = linux_args.msg;
+ aiov.iov_len = linux_args.len;
+ error = linux_sendit(td, linux_args.s, &msg, linux_args.flags);
+ return (error);
}
struct linux_recvfrom_args {
@@ -999,16 +981,9 @@ static int
linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
{
struct linux_sendmsg_args linux_args;
- struct sendmsg_args /* {
- int s;
- const struct msghdr *msg;
- int flags;
- } */ bsd_args;
struct msghdr msg;
- struct msghdr *nmsg = NULL;
- struct cmsghdr *cmsg;
+ struct iovec aiov[UIO_SMALLIOV], *iov;
int error;
- caddr_t sg;
if ((error = copyin(args, &linux_args, sizeof(linux_args))))
return (error);
@@ -1016,41 +991,27 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
error = copyin(linux_args.msg, &msg, sizeof(msg));
if (error)
return (error);
-
- sg = stackgap_init();
- nmsg = (struct msghdr *)stackgap_alloc(&sg, sizeof(struct msghdr));
- if (nmsg == NULL)
- return (ENOMEM);
-
- bcopy(&msg, nmsg, sizeof(struct msghdr));
-
- if (msg.msg_name != NULL) {
- struct sockaddr *sa;
-
- error = linux_sa_get(&sg, &sa,
- (struct osockaddr *) msg.msg_name, &msg.msg_namelen);
- if (error)
- return (error);
-
- nmsg->msg_name = sa;
+ if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
+ if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
+ return (EMSGSIZE);
+ MALLOC(iov, struct iovec *,
+ sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
+ M_WAITOK);
+ } else {
+ iov = aiov;
}
-
- if (msg.msg_control != NULL) {
- nmsg->msg_control = (struct cmsghdr *)stackgap_alloc(&sg,
- msg.msg_controllen);
- if (nmsg->msg_control == NULL)
- return (ENOMEM);
-
- bcopy(msg.msg_control, nmsg->msg_control, msg.msg_controllen);
- cmsg = (struct cmsghdr*)nmsg->msg_control;
-
- cmsg->cmsg_level = linux_to_bsd_sockopt_level(cmsg->cmsg_level);
- }
-
- bsd_args.s = linux_args.s;
- bsd_args.msg = (caddr_t)nmsg;
- bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags);
- return (sendmsg(td, &bsd_args));
+ if (msg.msg_iovlen &&
+ (error = copyin(msg.msg_iov, iov,
+ (unsigned)(msg.msg_iovlen * sizeof (struct iovec)))))
+ goto done;
+ msg.msg_iov = iov;
+ msg.msg_flags = 0;
+
+ error = linux_sendit(td, linux_args.s, &msg, linux_args.flags);
+done:
+ if (iov != aiov)
+ FREE(iov, M_IOV);
+ return (error);
}
struct linux_recvmsg_args {
OpenPOWER on IntegriCloud