diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-02-10 08:04:05 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-02-10 08:04:05 +0000 |
commit | 8f00a358005dd7b96cf6bc5db0e7b8f72dafa9a6 (patch) | |
tree | f19cd44441ff601eddff1e2a705898f0c4609885 | |
parent | 62eda4da35a809978f6c57cd987aff2f88268af5 (diff) |
IP_HDRINCL support, will need tweaking; freebsd
-rw-r--r-- | sys/compat/linux/linux_socket.c | 202 |
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; } |