summaryrefslogtreecommitdiff
path: root/sys/compat/linux/linux_socket.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1999-02-10 08:04:05 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1999-02-10 08:04:05 +0000
commit8f00a358005dd7b96cf6bc5db0e7b8f72dafa9a6 (patch)
treef19cd44441ff601eddff1e2a705898f0c4609885 /sys/compat/linux/linux_socket.c
parent62eda4da35a809978f6c57cd987aff2f88268af5 (diff)
IP_HDRINCL support, will need tweaking; freebsd
Diffstat (limited to 'sys/compat/linux/linux_socket.c')
-rw-r--r--sys/compat/linux/linux_socket.c202
1 files changed, 201 insertions, 1 deletions
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index 2e8ab290631..68c6d8cbdcc 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: linux_socket.c,v 1.15 1998/07/19 15:32:24 deraadt Exp $ */
+/* $OpenBSD: linux_socket.c,v 1.16 1999/02/10 08:04:04 deraadt Exp $ */
/* $NetBSD: linux_socket.c,v 1.14 1996/04/05 00:01:50 christos Exp $ */
/*
@@ -48,6 +48,8 @@
#include <net/if_types.h>
#include <net/if_dl.h>
#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/mount.h>
#include <sys/proc.h>
@@ -103,6 +105,14 @@ int linux_setsockopt __P((struct proc *, struct linux_setsockopt_args *,
register_t *));
int linux_getsockopt __P((struct proc *, struct linux_getsockopt_args *,
register_t *));
+int linux_recvmsg __P((struct proc *, struct linux_recvmsg_args *,
+ register_t *));
+int linux_sendmsg __P((struct proc *, struct linux_sendmsg_args *,
+ register_t *));
+
+int linux_check_hdrincl __P((struct proc *, int, register_t *));
+int linux_sendto_hdrincl __P((struct proc *, struct sys_sendto_args *,
+ register_t *));
/*
* Convert between Linux and BSD socket domain values
@@ -418,6 +428,119 @@ linux_recv(p, uap, retval)
}
int
+linux_check_hdrincl(p, fd, retval)
+ struct proc *p;
+ int fd;
+ register_t *retval;
+{
+ struct sys_getsockopt_args /* {
+ int s;
+ int level;
+ int name;
+ caddr_t val;
+ int *avalsize;
+ } */ gsa;
+ int error;
+ caddr_t sg, val;
+ int *valsize;
+ int size_val = sizeof val;
+ int optval;
+
+ sg = stackgap_init(p->p_emul);
+ val = stackgap_alloc(&sg, sizeof(optval));
+ valsize = stackgap_alloc(&sg, sizeof(size_val));
+
+ if ((error = copyout(&size_val, valsize, sizeof(size_val))))
+ return (error);
+ SCARG(&gsa, s) = fd;
+ SCARG(&gsa, level) = IPPROTO_IP;
+ SCARG(&gsa, name) = IP_HDRINCL;
+ SCARG(&gsa, val) = val;
+ SCARG(&gsa, avalsize) = valsize;
+
+ if ((error = sys_getsockopt(p, &gsa, retval)))
+ return (error);
+ if ((error = copyin(val, &optval, sizeof(optval))))
+ return (error);
+ return (optval == 0);
+}
+
+/*
+ * linux_ip_copysize defines how many bytes we should copy
+ * from the beginning of the IP packet before we customize it for BSD.
+ * It should include all the fields we modify (ip_len and ip_off)
+ * and be as small as possible to minimize copying overhead.
+ */
+#define linux_ip_copysize 8
+
+int
+linux_sendto_hdrincl(p, bsa, retval)
+ struct proc *p;
+ struct sys_sendto_args *bsa;
+ register_t *retval;
+{
+ caddr_t sg;
+ struct sys_sendmsg_args ssa;
+ struct ip *packet, rpacket;
+ struct msghdr *msg, rmsg;
+ struct iovec *iov, riov[2];
+ int error;
+
+ /* Check the packet isn't too small before we mess with it */
+ if (SCARG(bsa, 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().
+ */
+ sg = stackgap_init(p->p_emul);
+ 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(SCARG(bsa, buf), (caddr_t)&rpacket,
+ linux_ip_copysize)))
+ return error;
+
+ /* Convert fields from Linux to BSD raw IP socket format */
+ rpacket.ip_len = SCARG(bsa, len);
+ error = copyout(&rpacket, packet, sizeof(packet));
+ if (error)
+ return (error);
+
+ riov[0].iov_base = (char *)packet;
+ riov[0].iov_len = linux_ip_copysize;
+ riov[1].iov_base = (caddr_t)SCARG(bsa, buf) + linux_ip_copysize;
+ riov[1].iov_len = SCARG(bsa, len) - linux_ip_copysize;
+
+ error = copyout(&riov[0], iov, sizeof(riov));
+ if (error)
+ return (error);
+
+ /* Prepare the msghdr and iovec structures describing the new packet */
+ rmsg.msg_name = (void *)SCARG(bsa, to);
+ rmsg.msg_namelen = SCARG(bsa, tolen);
+ rmsg.msg_iov = iov;
+ rmsg.msg_iovlen = 2;
+ rmsg.msg_control = NULL;
+ rmsg.msg_controllen = 0;
+ rmsg.msg_flags = 0;
+
+ error = copyout(&riov[0], iov, sizeof(riov));
+ if (error)
+ return (error);
+
+ SCARG(&ssa, s) = SCARG(bsa, s);
+ SCARG(&ssa, msg) = msg;
+ SCARG(&ssa, flags) = SCARG(bsa, flags);
+ return sys_sendmsg(p, &ssa, retval);
+}
+
+int
linux_sendto(p, uap, retval)
struct proc *p;
struct linux_sendto_args /* {
@@ -444,6 +567,8 @@ linux_sendto(p, uap, retval)
SCARG(&bsa, to) = (void *) lsa.to;
SCARG(&bsa, tolen) = lsa.tolen;
+ if (linux_check_hdrincl(p, lsa.s, retval) == 0)
+ return linux_sendto_hdrincl(p, &bsa, retval);
return sys_sendto(p, &bsa, retval);
}
@@ -727,6 +852,77 @@ linux_getsockopt(p, uap, retval)
return sys_getsockopt(p, &bga, retval);
}
+int
+linux_recvmsg(p, uap, retval)
+ struct proc *p;
+ struct linux_recvmsg_args /* {
+ syscallarg(int) s;
+ syscallarg(caddr_t) msg;
+ syscallarg(int) flags;
+ } */ *uap;
+ register_t *retval;
+{
+ struct linux_recvmsg_args lla;
+ struct sys_recvmsg_args bla;
+ int error;
+
+ if ((error = copyin((caddr_t) uap, (caddr_t) &lla, sizeof lla)))
+ return error;
+
+ SCARG(&bla, s) = lla.s;
+ SCARG(&bla, msg) = (struct msghdr *)lla.msg;
+ SCARG(&bla, flags) = lla.flags;
+
+ return sys_recvmsg(p, &bla, retval);
+}
+
+int
+linux_sendmsg(p, uap, retval)
+ struct proc *p;
+ struct linux_sendmsg_args /* {
+ syscallarg(int) s;
+ syscallarg(struct msghdr *) msg;
+ syscallarg(int) flags;
+ } */ *uap;
+ register_t *retval;
+{
+ struct linux_sendmsg_args lla;
+ struct sys_sendmsg_args bla;
+ int error;
+ caddr_t control;
+ int level;
+
+ if ((error = copyin((caddr_t) uap, (caddr_t) &lla, sizeof lla)))
+ return error;
+ SCARG(&bla, s) = lla.s;
+ SCARG(&bla, msg) = lla.msg;
+ SCARG(&bla, flags) = lla.flags;
+
+ error = copyin(lla.msg->msg_control, &control, sizeof(caddr_t));
+ if (error)
+ return error;
+ if (control == NULL)
+ goto done;
+ error = copyin(&((struct cmsghdr *)control)->cmsg_level,
+ &level, sizeof(int));
+ if (error)
+ return error;
+ if (level == 1) {
+ /*
+ * Linux thinks that SOL_SOCKET is 1; we know that it's really
+ * 0xffff, of course.
+ */
+ level = SOL_SOCKET;
+ /* XXX should use stack gap! */
+ error = copyout(&level, &((struct cmsghdr *)control)->
+ cmsg_level, sizeof(int));
+ if (error)
+ return error;
+ }
+done:
+ return sys_sendmsg(p, &bla, retval);
+}
+
/*
* Entry point to all Linux socket calls. Just check which call to
* make and take appropriate action.
@@ -773,6 +969,10 @@ linux_sys_socketcall(p, v, retval)
return linux_setsockopt(p, SCARG(uap, args), retval);
case LINUX_SYS_getsockopt:
return linux_getsockopt(p, SCARG(uap, args), retval);
+ case LINUX_SYS_sendmsg:
+ return linux_sendmsg(p, SCARG(uap, args), retval);
+ case LINUX_SYS_recvmsg:
+ return linux_recvmsg(p, SCARG(uap, args), retval);
default:
return ENOSYS;
}