linux_socket.c revision 191548
118316Swollman/*-
218316Swollman * Copyright (c) 1995 S�ren Schmidt
318316Swollman * All rights reserved.
418316Swollman *
518316Swollman * Redistribution and use in source and binary forms, with or without
618316Swollman * modification, are permitted provided that the following conditions
718316Swollman * are met:
818316Swollman * 1. Redistributions of source code must retain the above copyright
918316Swollman *    notice, this list of conditions and the following disclaimer
1018316Swollman *    in this position and unchanged.
1118316Swollman * 2. Redistributions in binary form must reproduce the above copyright
1218316Swollman *    notice, this list of conditions and the following disclaimer in the
1318316Swollman *    documentation and/or other materials provided with the distribution.
1446303Smarkm * 3. The name of the author may not be used to endorse or promote products
1518316Swollman *    derived from this software without specific prior written permission
1618316Swollman *
1718316Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1818316Swollman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1918316Swollman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2018316Swollman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2118316Swollman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2218316Swollman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2318316Swollman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2418316Swollman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2518316Swollman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2618316Swollman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2718316Swollman */
2818316Swollman
2918316Swollman#include <sys/cdefs.h>
3018316Swollman__FBSDID("$FreeBSD: head/sys/compat/linux/linux_socket.c 191548 2009-04-26 22:06:42Z zec $");
3118316Swollman
3246303Smarkm/* XXX we use functions that might not exist. */
3350476Speter#include "opt_compat.h"
3418316Swollman#include "opt_inet6.h"
3518316Swollman
3646303Smarkm#include <sys/param.h>
3718316Swollman#include <sys/proc.h>
3818316Swollman#include <sys/systm.h>
3918316Swollman#include <sys/sysproto.h>
4046303Smarkm#include <sys/fcntl.h>
4118316Swollman#include <sys/file.h>
4246303Smarkm#include <sys/limits.h>
4318316Swollman#include <sys/lock.h>
4418316Swollman#include <sys/malloc.h>
4518316Swollman#include <sys/mutex.h>
4618316Swollman#include <sys/mbuf.h>
4718316Swollman#include <sys/socket.h>
4818316Swollman#include <sys/socketvar.h>
4946303Smarkm#include <sys/syscallsubr.h>
5032502Scharnier#include <sys/uio.h>
5146303Smarkm#include <sys/syslog.h>
5218316Swollman#include <sys/un.h>
5318316Swollman#include <sys/vimage.h>
5418316Swollman
5518316Swollman#include <net/if.h>
5618316Swollman#include <netinet/in.h>
5718316Swollman#include <netinet/in_systm.h>
5818316Swollman#include <netinet/ip.h>
5918316Swollman#ifdef INET6
6046303Smarkm#include <netinet/ip6.h>
6146303Smarkm#include <netinet6/ip6_var.h>
6246303Smarkm#include <netinet6/in6_var.h>
6346303Smarkm#include <netinet6/vinet6.h>
6446303Smarkm#endif
6550476Speter
6646303Smarkm#ifdef COMPAT_LINUX32
6718316Swollman#include <machine/../linux32/linux.h>
6818316Swollman#include <machine/../linux32/linux32_proto.h>
6918316Swollman#else
7018316Swollman#include <machine/../linux/linux.h>
7146303Smarkm#include <machine/../linux/linux_proto.h>
7246303Smarkm#endif
7346303Smarkm#include <compat/linux/linux_socket.h>
7446303Smarkm#include <compat/linux/linux_util.h>
7546303Smarkm
7646303Smarkmstatic int do_sa_get(struct sockaddr **, const struct osockaddr *, int *,
7746303Smarkm    struct malloc_type *);
7846303Smarkmstatic int linux_to_bsd_domain(int);
7946303Smarkm
8046303Smarkm/*
8146303Smarkm * Reads a linux sockaddr and does any necessary translation.
8218316Swollman * Linux sockaddrs don't have a length field, only a family.
8318316Swollman */
8418316Swollmanstatic int
8546303Smarkmlinux_getsockaddr(struct sockaddr **sap, const struct osockaddr *osa, int len)
8618316Swollman{
8746303Smarkm	int osalen = len;
8846303Smarkm
8918316Swollman	return (do_sa_get(sap, osa, &osalen, M_SONAME));
9018316Swollman}
9118316Swollman
9218316Swollman/*
9318316Swollman * Copy the osockaddr structure pointed to by osa to kernel, adjust
9418316Swollman * family and convert to sockaddr.
9518316Swollman */
9618316Swollmanstatic int
9718316Swollmando_sa_get(struct sockaddr **sap, const struct osockaddr *osa, int *osalen,
9818316Swollman    struct malloc_type *mtype)
9918316Swollman{
10018316Swollman	int error=0, bdom;
10118316Swollman	struct sockaddr *sa;
10218316Swollman	struct osockaddr *kosa;
10318316Swollman	int alloclen;
10418316Swollman#ifdef INET6
10518316Swollman	int oldv6size;
10618316Swollman	struct sockaddr_in6 *sin6;
10719880Swollman#endif
10819880Swollman
10919880Swollman	if (*osalen < 2 || *osalen > UCHAR_MAX || !osa)
11019880Swollman		return (EINVAL);
11118316Swollman
11218316Swollman	alloclen = *osalen;
11318316Swollman#ifdef INET6
11446303Smarkm	oldv6size = 0;
11546303Smarkm	/*
11646303Smarkm	 * Check for old (pre-RFC2553) sockaddr_in6. We may accept it
11718316Swollman	 * if it's a v4-mapped address, so reserve the proper space
11846303Smarkm	 * for it.
11946303Smarkm	 */
12046303Smarkm	if (alloclen == sizeof (struct sockaddr_in6) - sizeof (u_int32_t)) {
12118316Swollman		alloclen = sizeof (struct sockaddr_in6);
12218316Swollman		oldv6size = 1;
12346303Smarkm	}
12437908Scharnier#endif
12518316Swollman
12646303Smarkm	kosa = malloc(alloclen, mtype, M_WAITOK);
12737908Scharnier
12818316Swollman	if ((error = copyin(osa, kosa, *osalen)))
12918316Swollman		goto out;
13018316Swollman
13118316Swollman	bdom = linux_to_bsd_domain(kosa->sa_family);
13219880Swollman	if (bdom == -1) {
13346303Smarkm		error = EINVAL;
13418316Swollman		goto out;
13518316Swollman	}
13618316Swollman
13718316Swollman#ifdef INET6
13818316Swollman	/*
13946303Smarkm	 * Older Linux IPv6 code uses obsolete RFC2133 struct sockaddr_in6,
14024359Simp	 * which lacks the scope id compared with RFC2553 one. If we detect
14118316Swollman	 * the situation, reject the address and write a message to system log.
14218316Swollman	 *
14318316Swollman	 * Still accept addresses for which the scope id is not used.
14418316Swollman	 */
14518316Swollman	if (oldv6size && bdom == AF_INET6) {
14618316Swollman		sin6 = (struct sockaddr_in6 *)kosa;
14718316Swollman		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
14818316Swollman		    (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
14918316Swollman		     !IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) &&
15018316Swollman		     !IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) &&
15118316Swollman		     !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
15218316Swollman		     !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) {
15318316Swollman			sin6->sin6_scope_id = 0;
15418316Swollman		} else {
15518316Swollman			log(LOG_DEBUG,
15618316Swollman			    "obsolete pre-RFC2553 sockaddr_in6 rejected\n");
15718316Swollman			error = EINVAL;
15818316Swollman			goto out;
15918316Swollman		}
16018316Swollman	} else
16137908Scharnier#endif
16218316Swollman	if (bdom == AF_INET)
16318316Swollman		alloclen = sizeof(struct sockaddr_in);
16418316Swollman
16518316Swollman	sa = (struct sockaddr *) kosa;
16618316Swollman	sa->sa_family = bdom;
16737908Scharnier	sa->sa_len = alloclen;
16818316Swollman
16918316Swollman	*sap = sa;
17018316Swollman	*osalen = alloclen;
17146303Smarkm	return (0);
17246303Smarkm
17346303Smarkmout:
17446303Smarkm	free(kosa, mtype);
17546303Smarkm	return (error);
17646303Smarkm}
17746303Smarkm
17846303Smarkmstatic int
17918316Swollmanlinux_to_bsd_domain(int domain)
18018316Swollman{
18118316Swollman
18218316Swollman	switch (domain) {
18318316Swollman	case LINUX_AF_UNSPEC:
18418316Swollman		return (AF_UNSPEC);
18518316Swollman	case LINUX_AF_UNIX:
18618316Swollman		return (AF_LOCAL);
18718316Swollman	case LINUX_AF_INET:
18818316Swollman		return (AF_INET);
18946303Smarkm	case LINUX_AF_INET6:
19046303Smarkm		return (AF_INET6);
19146303Smarkm	case LINUX_AF_AX25:
19246303Smarkm		return (AF_CCITT);
19346303Smarkm	case LINUX_AF_IPX:
19446303Smarkm		return (AF_IPX);
19518316Swollman	case LINUX_AF_APPLETALK:
19646303Smarkm		return (AF_APPLETALK);
19718316Swollman	}
19846303Smarkm	return (-1);
19918316Swollman}
20046303Smarkm
20118316Swollmanstatic int
20246303Smarkmbsd_to_linux_domain(int domain)
20318316Swollman{
20418316Swollman
20546303Smarkm	switch (domain) {
20618316Swollman	case AF_UNSPEC:
20718316Swollman		return (LINUX_AF_UNSPEC);
20818316Swollman	case AF_LOCAL:
20918316Swollman		return (LINUX_AF_UNIX);
21018316Swollman	case AF_INET:
21146303Smarkm		return (LINUX_AF_INET);
21246303Smarkm	case AF_INET6:
21318316Swollman		return (LINUX_AF_INET6);
21418316Swollman	case AF_CCITT:
21518316Swollman		return (LINUX_AF_AX25);
21646303Smarkm	case AF_IPX:
21718316Swollman		return (LINUX_AF_IPX);
21818316Swollman	case AF_APPLETALK:
21918316Swollman		return (LINUX_AF_APPLETALK);
22018316Swollman	}
22146303Smarkm	return (-1);
22218316Swollman}
22318316Swollman
22418316Swollmanstatic int
22518316Swollmanlinux_to_bsd_sockopt_level(int level)
22646303Smarkm{
22718316Swollman
22846303Smarkm	switch (level) {
22918316Swollman	case LINUX_SOL_SOCKET:
23018316Swollman		return (SOL_SOCKET);
23137908Scharnier	}
23218316Swollman	return (level);
23346303Smarkm}
23446303Smarkm
23518316Swollmanstatic int
23618316Swollmanbsd_to_linux_sockopt_level(int level)
23718316Swollman{
23819880Swollman
23919880Swollman	switch (level) {
24019880Swollman	case SOL_SOCKET:
24119880Swollman		return (LINUX_SOL_SOCKET);
24237908Scharnier	}
24319880Swollman	return (level);
24419880Swollman}
24519880Swollman
24619880Swollmanstatic int
24719880Swollmanlinux_to_bsd_ip_sockopt(int opt)
24819880Swollman{
24937908Scharnier
25019880Swollman	switch (opt) {
25146303Smarkm	case LINUX_IP_TOS:
25237908Scharnier		return (IP_TOS);
25319880Swollman	case LINUX_IP_TTL:
25419880Swollman		return (IP_TTL);
25519880Swollman	case LINUX_IP_OPTIONS:
25619880Swollman		return (IP_OPTIONS);
25737908Scharnier	case LINUX_IP_MULTICAST_IF:
25819880Swollman		return (IP_MULTICAST_IF);
25937908Scharnier	case LINUX_IP_MULTICAST_TTL:
26019880Swollman		return (IP_MULTICAST_TTL);
26119880Swollman	case LINUX_IP_MULTICAST_LOOP:
26219880Swollman		return (IP_MULTICAST_LOOP);
26318316Swollman	case LINUX_IP_ADD_MEMBERSHIP:
26437908Scharnier		return (IP_ADD_MEMBERSHIP);
26518316Swollman	case LINUX_IP_DROP_MEMBERSHIP:
26618316Swollman		return (IP_DROP_MEMBERSHIP);
26718316Swollman	case LINUX_IP_HDRINCL:
26846303Smarkm		return (IP_HDRINCL);
26937908Scharnier	}
27046303Smarkm	return (-1);
27146303Smarkm}
27246303Smarkm
27346303Smarkmstatic int
27418316Swollmanlinux_to_bsd_so_sockopt(int opt)
27546303Smarkm{
27646303Smarkm
27746524Smarkm	switch (opt) {
27846524Smarkm	case LINUX_SO_DEBUG:
27946303Smarkm		return (SO_DEBUG);
28018316Swollman	case LINUX_SO_REUSEADDR:
28118316Swollman		return (SO_REUSEADDR);
28218316Swollman	case LINUX_SO_TYPE:
28346303Smarkm		return (SO_TYPE);
28418316Swollman	case LINUX_SO_ERROR:
28518316Swollman		return (SO_ERROR);
28618316Swollman	case LINUX_SO_DONTROUTE:
28746524Smarkm		return (SO_DONTROUTE);
28818316Swollman	case LINUX_SO_BROADCAST:
28918316Swollman		return (SO_BROADCAST);
29018316Swollman	case LINUX_SO_SNDBUF:
29118316Swollman		return (SO_SNDBUF);
29218316Swollman	case LINUX_SO_RCVBUF:
29318316Swollman		return (SO_RCVBUF);
29418316Swollman	case LINUX_SO_KEEPALIVE:
29518316Swollman		return (SO_KEEPALIVE);
29618316Swollman	case LINUX_SO_OOBINLINE:
29746303Smarkm		return (SO_OOBINLINE);
29818316Swollman	case LINUX_SO_LINGER:
29918316Swollman		return (SO_LINGER);
30046303Smarkm	case LINUX_SO_PEERCRED:
30137908Scharnier		return (LOCAL_PEERCRED);
30246303Smarkm	case LINUX_SO_RCVLOWAT:
30337908Scharnier		return (SO_RCVLOWAT);
30446303Smarkm	case LINUX_SO_SNDLOWAT:
30546303Smarkm		return (SO_SNDLOWAT);
30646303Smarkm	case LINUX_SO_RCVTIMEO:
30746303Smarkm		return (SO_RCVTIMEO);
30846303Smarkm	case LINUX_SO_SNDTIMEO:
30937908Scharnier		return (SO_SNDTIMEO);
31037908Scharnier	case LINUX_SO_TIMESTAMP:
31118316Swollman		return (SO_TIMESTAMP);
31246303Smarkm	case LINUX_SO_ACCEPTCONN:
31318316Swollman		return (SO_ACCEPTCONN);
31418316Swollman	}
31518316Swollman	return (-1);
31618316Swollman}
31718316Swollman
31818316Swollmanstatic int
31918316Swollmanlinux_to_bsd_msg_flags(int flags)
32018316Swollman{
32146303Smarkm	int ret_flags = 0;
32246303Smarkm
32346303Smarkm	if (flags & LINUX_MSG_OOB)
32446303Smarkm		ret_flags |= MSG_OOB;
32518316Swollman	if (flags & LINUX_MSG_PEEK)
32618316Swollman		ret_flags |= MSG_PEEK;
32718316Swollman	if (flags & LINUX_MSG_DONTROUTE)
32818316Swollman		ret_flags |= MSG_DONTROUTE;
32918316Swollman	if (flags & LINUX_MSG_CTRUNC)
33018316Swollman		ret_flags |= MSG_CTRUNC;
33118316Swollman	if (flags & LINUX_MSG_TRUNC)
33246303Smarkm		ret_flags |= MSG_TRUNC;
33318316Swollman	if (flags & LINUX_MSG_DONTWAIT)
33418316Swollman		ret_flags |= MSG_DONTWAIT;
33518316Swollman	if (flags & LINUX_MSG_EOR)
33618316Swollman		ret_flags |= MSG_EOR;
33718316Swollman	if (flags & LINUX_MSG_WAITALL)
33846303Smarkm		ret_flags |= MSG_WAITALL;
33946303Smarkm	if (flags & LINUX_MSG_NOSIGNAL)
34046303Smarkm		ret_flags |= MSG_NOSIGNAL;
34146524Smarkm#if 0 /* not handled */
34246524Smarkm	if (flags & LINUX_MSG_PROXY)
34346303Smarkm		;
34418316Swollman	if (flags & LINUX_MSG_FIN)
34518316Swollman		;
34618316Swollman	if (flags & LINUX_MSG_SYN)
34718316Swollman		;
34818316Swollman	if (flags & LINUX_MSG_CONFIRM)
34918316Swollman		;
35018316Swollman	if (flags & LINUX_MSG_RST)
35118316Swollman		;
35218316Swollman	if (flags & LINUX_MSG_ERRQUEUE)
35318316Swollman		;
35418316Swollman#endif
35518316Swollman	return ret_flags;
35618316Swollman}
35718316Swollman
35818316Swollman/*
35918316Swollman* If bsd_to_linux_sockaddr() or linux_to_bsd_sockaddr() faults, then the
36018316Swollman* native syscall will fault.  Thus, we don't really need to check the
36119880Swollman* return values for these functions.
36219880Swollman*/
36318316Swollman
36418316Swollmanstatic int
36518316Swollmanbsd_to_linux_sockaddr(struct sockaddr *arg)
36618316Swollman{
36718316Swollman	struct sockaddr sa;
36818316Swollman	size_t sa_len = sizeof(struct sockaddr);
36918316Swollman	int error;
37018316Swollman
37118316Swollman	if ((error = copyin(arg, &sa, sa_len)))
37218316Swollman		return (error);
37319880Swollman
37418316Swollman	*(u_short *)&sa = sa.sa_family;
37518316Swollman
37618316Swollman	error = copyout(&sa, arg, sa_len);
37718316Swollman
37818316Swollman	return (error);
37919880Swollman}
38019880Swollman
38119880Swollmanstatic int
38219880Swollmanlinux_to_bsd_sockaddr(struct sockaddr *arg, int len)
38346303Smarkm{
38419880Swollman	struct sockaddr sa;
38519880Swollman	size_t sa_len = sizeof(struct sockaddr);
38619880Swollman	int error;
38719880Swollman
38819880Swollman	if ((error = copyin(arg, &sa, sa_len)))
38919880Swollman		return (error);
39019880Swollman
39146303Smarkm	sa.sa_family = *(sa_family_t *)&sa;
39219880Swollman	sa.sa_len = len;
39346303Smarkm
39446303Smarkm	error = copyout(&sa, arg, sa_len);
39519880Swollman
39646303Smarkm	return (error);
39719880Swollman}
39846303Smarkm
39946303Smarkm
40046303Smarkmstatic int
40146303Smarkmlinux_sa_put(struct osockaddr *osa)
40219880Swollman{
40319880Swollman	struct osockaddr sa;
40419880Swollman	int error, bdom;
40519880Swollman
40618316Swollman	/*
40718316Swollman	 * Only read/write the osockaddr family part, the rest is
40818316Swollman	 * not changed.
40918316Swollman	 */
41018316Swollman	error = copyin(osa, &sa, sizeof(sa.sa_family));
41118316Swollman	if (error)
41218316Swollman		return (error);
41318316Swollman
41418316Swollman	bdom = bsd_to_linux_domain(sa.sa_family);
41546303Smarkm	if (bdom == -1)
41618316Swollman		return (EINVAL);
41718316Swollman
41818316Swollman	sa.sa_family = bdom;
41918316Swollman	error = copyout(&sa, osa, sizeof(sa.sa_family));
42018316Swollman	if (error)
42146303Smarkm		return (error);
42218316Swollman
42318316Swollman	return (0);
42446303Smarkm}
42518316Swollman
42618316Swollmanstatic int
42746303Smarkmlinux_to_bsd_cmsg_type(int cmsg_type)
42818316Swollman{
42918316Swollman
43046303Smarkm	switch (cmsg_type) {
43146524Smarkm	case LINUX_SCM_RIGHTS:
43246524Smarkm		return (SCM_RIGHTS);
43346303Smarkm	}
43418316Swollman	return (-1);
43518316Swollman}
43618316Swollman
43718316Swollmanstatic int
43818316Swollmanbsd_to_linux_cmsg_type(int cmsg_type)
43918316Swollman{
44018316Swollman
44118316Swollman	switch (cmsg_type) {
44218316Swollman	case SCM_RIGHTS:
44318316Swollman		return (LINUX_SCM_RIGHTS);
44418316Swollman	}
44518316Swollman	return (-1);
44646303Smarkm}
44746303Smarkm
44846303Smarkm
44946303Smarkm
45046303Smarkmstatic int
45118316Swollmanlinux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
45218316Swollman{
45318316Swollman	if (lhdr->msg_controllen > INT_MAX)
45418316Swollman		return (ENOBUFS);
45518316Swollman
45618316Swollman	bhdr->msg_name		= PTRIN(lhdr->msg_name);
45718316Swollman	bhdr->msg_namelen	= lhdr->msg_namelen;
45818316Swollman	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
45918316Swollman	bhdr->msg_iovlen	= lhdr->msg_iovlen;
46018316Swollman	bhdr->msg_control	= PTRIN(lhdr->msg_control);
46118316Swollman	bhdr->msg_controllen	= lhdr->msg_controllen;
46232502Scharnier	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
46318316Swollman	return (0);
46446524Smarkm}
46546524Smarkm
46618316Swollmanstatic int
46718316Swollmanbsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
46818316Swollman{
46918316Swollman	lhdr->msg_name		= PTROUT(bhdr->msg_name);
47018316Swollman	lhdr->msg_namelen	= bhdr->msg_namelen;
47118316Swollman	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
47218316Swollman	lhdr->msg_iovlen	= bhdr->msg_iovlen;
47318316Swollman	lhdr->msg_control	= PTROUT(bhdr->msg_control);
47418316Swollman	lhdr->msg_controllen	= bhdr->msg_controllen;
47518316Swollman	/* msg_flags skipped */
47618316Swollman	return (0);
47718316Swollman}
47818316Swollman
47918316Swollmanstatic int
48018316Swollmanlinux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
48118316Swollman    struct mbuf *control, enum uio_seg segflg)
48246303Smarkm{
48346524Smarkm	struct sockaddr *to;
48446524Smarkm	int error;
48546303Smarkm
48618316Swollman	if (mp->msg_name != NULL) {
48718316Swollman		error = linux_getsockaddr(&to, mp->msg_name, mp->msg_namelen);
48818316Swollman		if (error)
48918316Swollman			return (error);
49018316Swollman		mp->msg_name = to;
49118316Swollman	} else
49218316Swollman		to = NULL;
49318316Swollman
49418316Swollman	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
49519880Swollman	    segflg);
49618316Swollman
49718316Swollman	if (to)
49846303Smarkm		free(to, M_SONAME);
49918316Swollman	return (error);
50018316Swollman}
50118316Swollman
50218316Swollman/* Return 0 if IP_HDRINCL is set for the given socket. */
50318316Swollmanstatic int
50446524Smarkmlinux_check_hdrincl(struct thread *td, int s)
50546524Smarkm{
50618316Swollman	int error, optval, size_val;
50718316Swollman
50846303Smarkm	size_val = sizeof(optval);
50918316Swollman	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
51018316Swollman	    &optval, UIO_SYSSPACE, &size_val);
51118316Swollman	if (error)
51218316Swollman		return (error);
51318316Swollman
51418316Swollman	return (optval == 0);
51518316Swollman}
51618316Swollman
51718316Swollmanstruct linux_sendto_args {
51818316Swollman	int s;
51946303Smarkm	l_uintptr_t msg;
52018316Swollman	int len;
52118316Swollman	int flags;
52218316Swollman	l_uintptr_t to;
52346303Smarkm	int tolen;
52418316Swollman};
52546524Smarkm
52646524Smarkm/*
52718316Swollman * Updated sendto() when IP_HDRINCL is set:
52818316Swollman * tweak endian-dependent fields in the IP packet.
52918316Swollman */
53018316Swollmanstatic int
53118316Swollmanlinux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
53218316Swollman{
53318316Swollman/*
53419880Swollman * linux_ip_copysize defines how many bytes we should copy
53519880Swollman * from the beginning of the IP packet before we customize it for BSD.
53619880Swollman * It should include all the fields we modify (ip_len and ip_off).
53719880Swollman */
53819880Swollman#define linux_ip_copysize	8
53919880Swollman
54019880Swollman	struct ip *packet;
54119880Swollman	struct msghdr msg;
54219880Swollman	struct iovec aiov[1];
54319880Swollman	int error;
54419880Swollman
54519880Swollman	/* Check that the packet isn't too big or too small. */
54619880Swollman	if (linux_args->len < linux_ip_copysize ||
54719880Swollman	    linux_args->len > IP_MAXPACKET)
54819880Swollman		return (EINVAL);
54919880Swollman
55019880Swollman	packet = (struct ip *)malloc(linux_args->len, M_TEMP, M_WAITOK);
55119880Swollman
55219880Swollman	/* Make kernel copy of the packet to be sent */
55319880Swollman	if ((error = copyin(PTRIN(linux_args->msg), packet,
55419880Swollman	    linux_args->len)))
55519880Swollman		goto goout;
55619880Swollman
55719880Swollman	/* Convert fields from Linux to BSD raw IP socket format */
55819880Swollman	packet->ip_len = linux_args->len;
55919880Swollman	packet->ip_off = ntohs(packet->ip_off);
56019880Swollman
56119880Swollman	/* Prepare the msghdr and iovec structures describing the new packet */
56219880Swollman	msg.msg_name = PTRIN(linux_args->to);
56319880Swollman	msg.msg_namelen = linux_args->tolen;
56419880Swollman	msg.msg_iov = aiov;
56519880Swollman	msg.msg_iovlen = 1;
56619880Swollman	msg.msg_control = NULL;
56719880Swollman	msg.msg_flags = 0;
56819880Swollman	aiov[0].iov_base = (char *)packet;
56919880Swollman	aiov[0].iov_len = linux_args->len;
57019880Swollman	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
57119880Swollman	    NULL, UIO_SYSSPACE);
57219880Swollmangoout:
57319880Swollman	free(packet, M_TEMP);
57419880Swollman	return (error);
57519880Swollman}
57619880Swollman
57719880Swollmanstruct linux_socket_args {
57819880Swollman	int domain;
57919880Swollman	int type;
58019880Swollman	int protocol;
58119880Swollman};
58219880Swollman
58319880Swollmanstatic int
58419880Swollmanlinux_socket(struct thread *td, struct linux_socket_args *args)
58519880Swollman{
58619880Swollman#ifdef INET6
58719880Swollman#ifndef KLD_MODULE
58818316Swollman	INIT_VNET_INET6(curvnet);
58918316Swollman#endif
59018316Swollman#endif
59118316Swollman	struct socket_args /* {
59218316Swollman		int domain;
59318316Swollman		int type;
59418316Swollman		int protocol;
59518316Swollman	} */ bsd_args;
59646303Smarkm	int retval_socket;
59718316Swollman
59846303Smarkm	bsd_args.protocol = args->protocol;
59946303Smarkm	bsd_args.type = args->type;
60046303Smarkm	bsd_args.domain = linux_to_bsd_domain(args->domain);
60118316Swollman	if (bsd_args.domain == -1)
60218316Swollman		return (EINVAL);
60318316Swollman
60418316Swollman	retval_socket = socket(td, &bsd_args);
60518316Swollman	if (bsd_args.type == SOCK_RAW
60619880Swollman	    && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0)
60718316Swollman	    && bsd_args.domain == AF_INET
60818316Swollman	    && retval_socket >= 0) {
60918316Swollman		/* It's a raw IP socket: set the IP_HDRINCL option. */
61018316Swollman		int hdrincl;
61118316Swollman
61218316Swollman		hdrincl = 1;
61318316Swollman		/* We ignore any error returned by kern_setsockopt() */
61418316Swollman		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
61518316Swollman		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
61618316Swollman	}
61718316Swollman#ifdef INET6
61818316Swollman	/*
61918316Swollman	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by
62018316Swollman	 * default and some apps depend on this. So, set V6ONLY to 0
62118316Swollman	 * for Linux apps if the sysctl value is set to 1.
62218316Swollman	 */
62318316Swollman	if (bsd_args.domain == PF_INET6 && retval_socket >= 0
62418316Swollman#ifndef KLD_MODULE
62518316Swollman	    /*
62618316Swollman	     * XXX: Avoid undefined symbol error with an IPv4 only
62718316Swollman	     * kernel.
62818316Swollman	     */
62918316Swollman	    && V_ip6_v6only
63046303Smarkm#endif
63118316Swollman	    ) {
63218316Swollman		int v6only;
63346303Smarkm
63418316Swollman		v6only = 0;
63518316Swollman		/* We ignore any error returned by setsockopt() */
63618316Swollman		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
63718316Swollman		    &v6only, UIO_SYSSPACE, sizeof(v6only));
63818316Swollman	}
63918316Swollman#endif
64018316Swollman
64118316Swollman	return (retval_socket);
64218316Swollman}
64318316Swollman
64418316Swollmanstruct linux_bind_args {
64518316Swollman	int s;
64618316Swollman	l_uintptr_t name;
64718316Swollman	int namelen;
64818316Swollman};
64918316Swollman
65018316Swollmanstatic int
65118316Swollmanlinux_bind(struct thread *td, struct linux_bind_args *args)
65218316Swollman{
65318316Swollman	struct sockaddr *sa;
65418316Swollman	int error;
65518316Swollman
65618316Swollman	error = linux_getsockaddr(&sa, PTRIN(args->name),
65718316Swollman	    args->namelen);
65818316Swollman	if (error)
65918316Swollman		return (error);
66018316Swollman
66118316Swollman	error = kern_bind(td, args->s, sa);
66218316Swollman	free(sa, M_SONAME);
66318316Swollman	if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
66418316Swollman	   	return (EINVAL);
66518316Swollman	return (error);
66618316Swollman}
66718316Swollman
66818316Swollmanstruct linux_connect_args {
66918316Swollman	int s;
67018316Swollman	l_uintptr_t name;
67118316Swollman	int namelen;
67218316Swollman};
67318316Swollmanint linux_connect(struct thread *, struct linux_connect_args *);
67418316Swollman
67518316Swollmanint
67618316Swollmanlinux_connect(struct thread *td, struct linux_connect_args *args)
67718316Swollman{
67818316Swollman	struct socket *so;
67918316Swollman	struct sockaddr *sa;
68018316Swollman	u_int fflag;
68118316Swollman	int error;
68218316Swollman
68318316Swollman	error = linux_getsockaddr(&sa, (struct osockaddr *)PTRIN(args->name),
68418316Swollman	    args->namelen);
68518316Swollman	if (error)
68618316Swollman		return (error);
68718316Swollman
68818316Swollman	error = kern_connect(td, args->s, sa);
68918316Swollman	free(sa, M_SONAME);
69018316Swollman	if (error != EISCONN)
69118316Swollman		return (error);
69218316Swollman
69318316Swollman	/*
69418316Swollman	 * Linux doesn't return EISCONN the first time it occurs,
69518316Swollman	 * when on a non-blocking socket. Instead it returns the
69618316Swollman	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
69718316Swollman	 *
69818316Swollman	 * XXXRW: Instead of using fgetsock(), check that it is a
69918316Swollman	 * socket and use the file descriptor reference instead of
70018316Swollman	 * creating a new one.
70118316Swollman	 */
70218316Swollman	error = fgetsock(td, args->s, &so, &fflag);
70319880Swollman	if (error == 0) {
70419880Swollman		error = EISCONN;
70519880Swollman		if (fflag & FNONBLOCK) {
70619880Swollman			SOCK_LOCK(so);
70719880Swollman			if (so->so_emuldata == 0)
70819880Swollman				error = so->so_error;
70919880Swollman			so->so_emuldata = (void *)1;
71019880Swollman			SOCK_UNLOCK(so);
71119880Swollman		}
71219880Swollman		fputsock(so);
71319880Swollman	}
71419880Swollman	return (error);
71546303Smarkm}
71619880Swollman
71746303Smarkmstruct linux_listen_args {
71846303Smarkm	int s;
71919880Swollman	int backlog;
72046303Smarkm};
72119880Swollman
72246303Smarkmstatic int
72346303Smarkmlinux_listen(struct thread *td, struct linux_listen_args *args)
72419880Swollman{
72519880Swollman	struct listen_args /* {
72646303Smarkm		int s;
72719880Swollman		int backlog;
72819880Swollman	} */ bsd_args;
72919880Swollman
73019880Swollman	bsd_args.s = args->s;
73146303Smarkm	bsd_args.backlog = args->backlog;
73219880Swollman	return (listen(td, &bsd_args));
73318316Swollman}
73446303Smarkm
73546303Smarkmstruct linux_accept_args {
73646303Smarkm	int s;
73746303Smarkm	l_uintptr_t addr;
73846303Smarkm	l_uintptr_t namelen;
73946303Smarkm};
74046303Smarkm
74146303Smarkmstatic int
74246303Smarkmlinux_accept(struct thread *td, struct linux_accept_args *args)
74346303Smarkm{
74446303Smarkm	struct accept_args /* {
74546303Smarkm		int	s;
74646303Smarkm		struct sockaddr * __restrict name;
74718316Swollman		socklen_t * __restrict anamelen;
74818316Swollman	} */ bsd_args;
74918316Swollman	int error, fd;
75018316Swollman
75118316Swollman	bsd_args.s = args->s;
75218316Swollman	/* XXX: */
75318316Swollman	bsd_args.name = (struct sockaddr * __restrict)PTRIN(args->addr);
75418316Swollman	bsd_args.anamelen = PTRIN(args->namelen);/* XXX */
75518316Swollman	error = accept(td, &bsd_args);
75618316Swollman	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.name);
75718316Swollman	if (error) {
75818316Swollman		if (error == EFAULT && args->namelen != sizeof(struct sockaddr_in))
75946303Smarkm			return (EINVAL);
76018316Swollman		return (error);
76118316Swollman	}
76218316Swollman	if (args->addr) {
76318316Swollman		error = linux_sa_put(PTRIN(args->addr));
76418316Swollman		if (error) {
76518316Swollman			(void)kern_close(td, td->td_retval[0]);
76618316Swollman			return (error);
76718316Swollman		}
76818316Swollman	}
76918316Swollman
77018316Swollman	/*
77118316Swollman	 * linux appears not to copy flags from the parent socket to the
77218316Swollman	 * accepted one, so we must clear the flags in the new descriptor.
77318316Swollman	 * Ignore any errors, because we already have an open fd.
77418316Swollman	 */
77518316Swollman	fd = td->td_retval[0];
77618316Swollman	(void)kern_fcntl(td, fd, F_SETFL, 0);
77718316Swollman	td->td_retval[0] = fd;
77818316Swollman	return (0);
77918316Swollman}
78018316Swollman
78118316Swollmanstruct linux_getsockname_args {
78218316Swollman	int s;
78318316Swollman	l_uintptr_t addr;
78418316Swollman	l_uintptr_t namelen;
78590868Smike};
78618316Swollman
78718316Swollmanstatic int
78818316Swollmanlinux_getsockname(struct thread *td, struct linux_getsockname_args *args)
78918316Swollman{
79018316Swollman	struct getsockname_args /* {
79118316Swollman		int	fdes;
79218316Swollman		struct sockaddr * __restrict asa;
79318316Swollman		socklen_t * __restrict alen;
79418316Swollman	} */ bsd_args;
79518316Swollman	int error;
79618316Swollman
79718316Swollman	bsd_args.fdes = args->s;
79818316Swollman	/* XXX: */
79918316Swollman	bsd_args.asa = (struct sockaddr * __restrict)PTRIN(args->addr);
80018316Swollman	bsd_args.alen = PTRIN(args->namelen);	/* XXX */
80118316Swollman	error = getsockname(td, &bsd_args);
80218316Swollman	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
80318316Swollman	if (error)
80418316Swollman		return (error);
80518316Swollman	error = linux_sa_put(PTRIN(args->addr));
80618316Swollman	if (error)
80718316Swollman		return (error);
80818316Swollman	return (0);
80918316Swollman}
81018316Swollman
81118316Swollmanstruct linux_getpeername_args {
81218316Swollman	int s;
81318316Swollman	l_uintptr_t addr;
81446303Smarkm	l_uintptr_t namelen;
81518316Swollman};
81646303Smarkm
81718316Swollmanstatic int
81846303Smarkmlinux_getpeername(struct thread *td, struct linux_getpeername_args *args)
81918316Swollman{
82018316Swollman	struct getpeername_args /* {
82118316Swollman		int fdes;
82218316Swollman		caddr_t asa;
82318316Swollman		int *alen;
82418316Swollman	} */ bsd_args;
82518316Swollman	int error;
82618316Swollman
82718316Swollman	bsd_args.fdes = args->s;
82890868Smike	bsd_args.asa = (struct sockaddr *)PTRIN(args->addr);
82918316Swollman	bsd_args.alen = (int *)PTRIN(args->namelen);
83018316Swollman	error = getpeername(td, &bsd_args);
83118316Swollman	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
83218316Swollman	if (error)
83318316Swollman		return (error);
83418316Swollman	error = linux_sa_put(PTRIN(args->addr));
83518316Swollman	if (error)
83618316Swollman		return (error);
83718316Swollman	return (0);
83818316Swollman}
83918316Swollman
84018316Swollmanstruct linux_socketpair_args {
84118316Swollman	int domain;
84218316Swollman	int type;
84318316Swollman	int protocol;
84418316Swollman	l_uintptr_t rsv;
84518316Swollman};
84618316Swollman
84718316Swollmanstatic int
84818316Swollmanlinux_socketpair(struct thread *td, struct linux_socketpair_args *args)
84919880Swollman{
85019880Swollman	struct socketpair_args /* {
85119880Swollman		int domain;
85219880Swollman		int type;
85319880Swollman		int protocol;
85419880Swollman		int *rsv;
85546303Smarkm	} */ bsd_args;
85619880Swollman
85719880Swollman	bsd_args.domain = linux_to_bsd_domain(args->domain);
85819880Swollman	if (bsd_args.domain == -1)
85919880Swollman		return (EINVAL);
86046303Smarkm
86146303Smarkm	bsd_args.type = args->type;
86219880Swollman	bsd_args.protocol = args->protocol;
86319880Swollman	bsd_args.rsv = (int *)PTRIN(args->rsv);
86419880Swollman	return (socketpair(td, &bsd_args));
86519880Swollman}
86619880Swollman
86719880Swollmanstruct linux_send_args {
86819880Swollman	int s;
86919880Swollman	l_uintptr_t msg;
87019880Swollman	int len;
87119880Swollman	int flags;
87219880Swollman};
87319880Swollman
87419880Swollmanstatic int
87537815Sphklinux_send(struct thread *td, struct linux_send_args *args)
87619880Swollman{
87719880Swollman	struct sendto_args /* {
87819880Swollman		int s;
87919880Swollman		caddr_t buf;
88019880Swollman		int len;
88119880Swollman		int flags;
88219880Swollman		caddr_t to;
88319880Swollman		int tolen;
88419880Swollman	} */ bsd_args;
88519880Swollman
88619880Swollman	bsd_args.s = args->s;
88719880Swollman	bsd_args.buf = (caddr_t)PTRIN(args->msg);
88819880Swollman	bsd_args.len = args->len;
88919880Swollman	bsd_args.flags = args->flags;
89019880Swollman	bsd_args.to = NULL;
89119880Swollman	bsd_args.tolen = 0;
89219880Swollman	return sendto(td, &bsd_args);
89319880Swollman}
89419880Swollman
89519880Swollmanstruct linux_recv_args {
89619880Swollman	int s;
89719880Swollman	l_uintptr_t msg;
89819880Swollman	int len;
89919880Swollman	int flags;
90019880Swollman};
90119880Swollman
90219880Swollmanstatic int
90319880Swollmanlinux_recv(struct thread *td, struct linux_recv_args *args)
90419880Swollman{
90519880Swollman	struct recvfrom_args /* {
90619880Swollman		int s;
90719880Swollman		caddr_t buf;
90819880Swollman		int len;
90919880Swollman		int flags;
91019880Swollman		struct sockaddr *from;
911		socklen_t fromlenaddr;
912	} */ bsd_args;
913
914	bsd_args.s = args->s;
915	bsd_args.buf = (caddr_t)PTRIN(args->msg);
916	bsd_args.len = args->len;
917	bsd_args.flags = args->flags;
918	bsd_args.from = NULL;
919	bsd_args.fromlenaddr = 0;
920	return (recvfrom(td, &bsd_args));
921}
922
923static int
924linux_sendto(struct thread *td, struct linux_sendto_args *args)
925{
926	struct msghdr msg;
927	struct iovec aiov;
928	int error;
929
930	if (linux_check_hdrincl(td, args->s) == 0)
931		/* IP_HDRINCL set, tweak the packet before sending */
932		return (linux_sendto_hdrincl(td, args));
933
934	msg.msg_name = PTRIN(args->to);
935	msg.msg_namelen = args->tolen;
936	msg.msg_iov = &aiov;
937	msg.msg_iovlen = 1;
938	msg.msg_control = NULL;
939	msg.msg_flags = 0;
940	aiov.iov_base = PTRIN(args->msg);
941	aiov.iov_len = args->len;
942	error = linux_sendit(td, args->s, &msg, args->flags, NULL,
943	    UIO_USERSPACE);
944	return (error);
945}
946
947struct linux_recvfrom_args {
948	int s;
949	l_uintptr_t buf;
950	int len;
951	int flags;
952	l_uintptr_t from;
953	l_uintptr_t fromlen;
954};
955
956static int
957linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
958{
959	struct recvfrom_args /* {
960		int	s;
961		caddr_t	buf;
962		size_t	len;
963		int	flags;
964		struct sockaddr * __restrict from;
965		socklen_t * __restrict fromlenaddr;
966	} */ bsd_args;
967	size_t len;
968	int error;
969
970	if ((error = copyin(PTRIN(args->fromlen), &len, sizeof(size_t))))
971		return (error);
972
973	bsd_args.s = args->s;
974	bsd_args.buf = PTRIN(args->buf);
975	bsd_args.len = args->len;
976	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
977	/* XXX: */
978	bsd_args.from = (struct sockaddr * __restrict)PTRIN(args->from);
979	bsd_args.fromlenaddr = PTRIN(args->fromlen);/* XXX */
980
981	linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.from, len);
982	error = recvfrom(td, &bsd_args);
983	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.from);
984
985	if (error)
986		return (error);
987	if (args->from) {
988		error = linux_sa_put((struct osockaddr *)
989		    PTRIN(args->from));
990		if (error)
991			return (error);
992	}
993	return (0);
994}
995
996struct linux_sendmsg_args {
997	int s;
998	l_uintptr_t msg;
999	int flags;
1000};
1001
1002static int
1003linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
1004{
1005	struct cmsghdr *cmsg;
1006	struct mbuf *control;
1007	struct msghdr msg;
1008	struct l_cmsghdr linux_cmsg;
1009	struct l_cmsghdr *ptr_cmsg;
1010	struct l_msghdr linux_msg;
1011	struct iovec *iov;
1012	socklen_t datalen;
1013	void *data;
1014	int error;
1015
1016	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
1017	if (error)
1018		return (error);
1019	error = linux_to_bsd_msghdr(&msg, &linux_msg);
1020	if (error)
1021		return (error);
1022
1023	/*
1024	 * Some Linux applications (ping) define a non-NULL control data
1025	 * pointer, but a msg_controllen of 0, which is not allowed in the
1026	 * FreeBSD system call interface.  NULL the msg_control pointer in
1027	 * order to handle this case.  This should be checked, but allows the
1028	 * Linux ping to work.
1029	 */
1030	if (msg.msg_control != NULL && msg.msg_controllen == 0)
1031		msg.msg_control = NULL;
1032
1033#ifdef COMPAT_LINUX32
1034	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
1035	    &iov, EMSGSIZE);
1036#else
1037	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
1038#endif
1039	if (error)
1040		return (error);
1041
1042	if (msg.msg_control != NULL) {
1043		error = ENOBUFS;
1044		cmsg = malloc(CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
1045		control = m_get(M_WAIT, MT_CONTROL);
1046		if (control == NULL)
1047			goto bad;
1048		ptr_cmsg = LINUX_CMSG_FIRSTHDR(&msg);
1049
1050		do {
1051			error = copyin(ptr_cmsg, &linux_cmsg,
1052			    sizeof(struct l_cmsghdr));
1053			if (error)
1054				goto bad;
1055
1056			error = EINVAL;
1057			if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr))
1058				goto bad;
1059
1060			/*
1061			 * Now we support only SCM_RIGHTS, so return EINVAL
1062			 * in any other cmsg_type
1063			 */
1064			if ((cmsg->cmsg_type =
1065			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type)) == -1)
1066				goto bad;
1067			cmsg->cmsg_level =
1068			    linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
1069
1070			datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
1071			cmsg->cmsg_len = CMSG_LEN(datalen);
1072			data = LINUX_CMSG_DATA(ptr_cmsg);
1073
1074			error = ENOBUFS;
1075			if (!m_append(control, CMSG_HDRSZ, (c_caddr_t) cmsg))
1076				goto bad;
1077			if (!m_append(control, datalen, (c_caddr_t) data))
1078				goto bad;
1079		} while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&msg, ptr_cmsg)));
1080	} else {
1081		control = NULL;
1082		cmsg = NULL;
1083	}
1084
1085	msg.msg_iov = iov;
1086	msg.msg_flags = 0;
1087	error = linux_sendit(td, args->s, &msg, args->flags, control,
1088	    UIO_USERSPACE);
1089
1090bad:
1091	free(iov, M_IOV);
1092	if (cmsg)
1093		free(cmsg, M_TEMP);
1094	return (error);
1095}
1096
1097struct linux_recvmsg_args {
1098	int s;
1099	l_uintptr_t msg;
1100	int flags;
1101};
1102
1103static int
1104linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
1105{
1106	struct cmsghdr *cm;
1107	struct msghdr msg;
1108	struct l_cmsghdr *linux_cmsg = NULL;
1109	socklen_t datalen, outlen, clen;
1110	struct l_msghdr linux_msg;
1111	struct iovec *iov, *uiov;
1112	struct mbuf *control = NULL;
1113	struct mbuf **controlp;
1114	caddr_t outbuf;
1115	void *data;
1116	int error;
1117
1118	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
1119	if (error)
1120		return (error);
1121
1122	error = linux_to_bsd_msghdr(&msg, &linux_msg);
1123	if (error)
1124		return (error);
1125
1126#ifdef COMPAT_LINUX32
1127	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
1128	    &iov, EMSGSIZE);
1129#else
1130	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
1131#endif
1132	if (error)
1133		return (error);
1134
1135	if (msg.msg_name) {
1136		error = linux_to_bsd_sockaddr((struct sockaddr *)msg.msg_name,
1137		    msg.msg_namelen);
1138		if (error)
1139			goto bad;
1140	}
1141
1142	uiov = msg.msg_iov;
1143	msg.msg_iov = iov;
1144	controlp = (msg.msg_control != NULL) ? &control : NULL;
1145	error = kern_recvit(td, args->s, &msg, UIO_USERSPACE, controlp);
1146	msg.msg_iov = uiov;
1147	if (error)
1148		goto bad;
1149
1150	error = bsd_to_linux_msghdr(&msg, &linux_msg);
1151	if (error)
1152		goto bad;
1153
1154	if (linux_msg.msg_name) {
1155		error = bsd_to_linux_sockaddr((struct sockaddr *)
1156		    PTRIN(linux_msg.msg_name));
1157		if (error)
1158			goto bad;
1159	}
1160	if (linux_msg.msg_name && linux_msg.msg_namelen > 2) {
1161		error = linux_sa_put(PTRIN(linux_msg.msg_name));
1162		if (error)
1163			goto bad;
1164	}
1165
1166	if (control) {
1167
1168		linux_cmsg = malloc(L_CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
1169		outbuf = PTRIN(linux_msg.msg_control);
1170		cm = mtod(control, struct cmsghdr *);
1171		outlen = 0;
1172		clen = control->m_len;
1173
1174		while (cm != NULL) {
1175
1176			if ((linux_cmsg->cmsg_type =
1177			    bsd_to_linux_cmsg_type(cm->cmsg_type)) == -1)
1178			{
1179				error = EINVAL;
1180				goto bad;
1181			}
1182			data = CMSG_DATA(cm);
1183			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
1184
1185			if (outlen + LINUX_CMSG_LEN(datalen) >
1186			    linux_msg.msg_controllen) {
1187				if (outlen == 0) {
1188					error = EMSGSIZE;
1189					goto bad;
1190				} else {
1191					linux_msg.msg_flags |= LINUX_MSG_CTRUNC;
1192					goto out;
1193				}
1194			}
1195
1196			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
1197			linux_cmsg->cmsg_level =
1198			    bsd_to_linux_sockopt_level(cm->cmsg_level);
1199
1200			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
1201			if (error)
1202				goto bad;
1203			outbuf += L_CMSG_HDRSZ;
1204
1205			error = copyout(data, outbuf, datalen);
1206			if (error)
1207				goto bad;
1208
1209			outbuf += LINUX_CMSG_ALIGN(datalen);
1210			outlen += LINUX_CMSG_LEN(datalen);
1211			linux_msg.msg_controllen = outlen;
1212
1213			if (CMSG_SPACE(datalen) < clen) {
1214				clen -= CMSG_SPACE(datalen);
1215				cm = (struct cmsghdr *)
1216				    ((caddr_t)cm + CMSG_SPACE(datalen));
1217			} else
1218				cm = NULL;
1219		}
1220	}
1221
1222out:
1223	error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg));
1224
1225bad:
1226	free(iov, M_IOV);
1227	if (control != NULL)
1228		m_freem(control);
1229	if (linux_cmsg != NULL)
1230		free(linux_cmsg, M_TEMP);
1231
1232	return (error);
1233}
1234
1235struct linux_shutdown_args {
1236	int s;
1237	int how;
1238};
1239
1240static int
1241linux_shutdown(struct thread *td, struct linux_shutdown_args *args)
1242{
1243	struct shutdown_args /* {
1244		int s;
1245		int how;
1246	} */ bsd_args;
1247
1248	bsd_args.s = args->s;
1249	bsd_args.how = args->how;
1250	return (shutdown(td, &bsd_args));
1251}
1252
1253struct linux_setsockopt_args {
1254	int s;
1255	int level;
1256	int optname;
1257	l_uintptr_t optval;
1258	int optlen;
1259};
1260
1261static int
1262linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
1263{
1264	struct setsockopt_args /* {
1265		int s;
1266		int level;
1267		int name;
1268		caddr_t val;
1269		int valsize;
1270	} */ bsd_args;
1271	int error, name;
1272
1273	bsd_args.s = args->s;
1274	bsd_args.level = linux_to_bsd_sockopt_level(args->level);
1275	switch (bsd_args.level) {
1276	case SOL_SOCKET:
1277		name = linux_to_bsd_so_sockopt(args->optname);
1278		break;
1279	case IPPROTO_IP:
1280		name = linux_to_bsd_ip_sockopt(args->optname);
1281		break;
1282	case IPPROTO_TCP:
1283		/* Linux TCP option values match BSD's */
1284		name = args->optname;
1285		break;
1286	default:
1287		name = -1;
1288		break;
1289	}
1290	if (name == -1)
1291		return (ENOPROTOOPT);
1292
1293	bsd_args.name = name;
1294	bsd_args.val = PTRIN(args->optval);
1295	bsd_args.valsize = args->optlen;
1296
1297	if (name == IPV6_NEXTHOP) {
1298		linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.val,
1299			bsd_args.valsize);
1300		error = setsockopt(td, &bsd_args);
1301		bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
1302	} else
1303		error = setsockopt(td, &bsd_args);
1304
1305	return (error);
1306}
1307
1308struct linux_getsockopt_args {
1309	int s;
1310	int level;
1311	int optname;
1312	l_uintptr_t optval;
1313	l_uintptr_t optlen;
1314};
1315
1316static int
1317linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
1318{
1319	struct getsockopt_args /* {
1320		int s;
1321		int level;
1322		int name;
1323		caddr_t val;
1324		int *avalsize;
1325	} */ bsd_args;
1326	int error, name;
1327
1328	bsd_args.s = args->s;
1329	bsd_args.level = linux_to_bsd_sockopt_level(args->level);
1330	switch (bsd_args.level) {
1331	case SOL_SOCKET:
1332		name = linux_to_bsd_so_sockopt(args->optname);
1333		break;
1334	case IPPROTO_IP:
1335		name = linux_to_bsd_ip_sockopt(args->optname);
1336		break;
1337	case IPPROTO_TCP:
1338		/* Linux TCP option values match BSD's */
1339		name = args->optname;
1340		break;
1341	default:
1342		name = -1;
1343		break;
1344	}
1345	if (name == -1)
1346		return (EINVAL);
1347
1348	bsd_args.name = name;
1349	bsd_args.val = PTRIN(args->optval);
1350	bsd_args.avalsize = PTRIN(args->optlen);
1351
1352	if (name == IPV6_NEXTHOP) {
1353		error = getsockopt(td, &bsd_args);
1354		bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
1355	} else
1356		error = getsockopt(td, &bsd_args);
1357
1358	return (error);
1359}
1360
1361int
1362linux_socketcall(struct thread *td, struct linux_socketcall_args *args)
1363{
1364	void *arg = (void *)(intptr_t)args->args;
1365
1366	switch (args->what) {
1367	case LINUX_SOCKET:
1368		return (linux_socket(td, arg));
1369	case LINUX_BIND:
1370		return (linux_bind(td, arg));
1371	case LINUX_CONNECT:
1372		return (linux_connect(td, arg));
1373	case LINUX_LISTEN:
1374		return (linux_listen(td, arg));
1375	case LINUX_ACCEPT:
1376		return (linux_accept(td, arg));
1377	case LINUX_GETSOCKNAME:
1378		return (linux_getsockname(td, arg));
1379	case LINUX_GETPEERNAME:
1380		return (linux_getpeername(td, arg));
1381	case LINUX_SOCKETPAIR:
1382		return (linux_socketpair(td, arg));
1383	case LINUX_SEND:
1384		return (linux_send(td, arg));
1385	case LINUX_RECV:
1386		return (linux_recv(td, arg));
1387	case LINUX_SENDTO:
1388		return (linux_sendto(td, arg));
1389	case LINUX_RECVFROM:
1390		return (linux_recvfrom(td, arg));
1391	case LINUX_SHUTDOWN:
1392		return (linux_shutdown(td, arg));
1393	case LINUX_SETSOCKOPT:
1394		return (linux_setsockopt(td, arg));
1395	case LINUX_GETSOCKOPT:
1396		return (linux_getsockopt(td, arg));
1397	case LINUX_SENDMSG:
1398		return (linux_sendmsg(td, arg));
1399	case LINUX_RECVMSG:
1400		return (linux_recvmsg(td, arg));
1401	}
1402
1403	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
1404	return (ENOSYS);
1405}
1406