xref: /freebsd/sys/compat/linux/linux_socket.c (revision fc7510aef78781b0068da1a6ba190a636a54d6e7)
1c21dee17SSøren Schmidt /*-
20ba1b365SEd Maste  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37f2d13d6SPedro F. Giffuni  *
49a14aa01SUlrich Spörlein  * Copyright (c) 1995 Søren Schmidt
5c21dee17SSøren Schmidt  * All rights reserved.
6c21dee17SSøren Schmidt  *
7c21dee17SSøren Schmidt  * Redistribution and use in source and binary forms, with or without
8c21dee17SSøren Schmidt  * modification, are permitted provided that the following conditions
9c21dee17SSøren Schmidt  * are met:
10c21dee17SSøren Schmidt  * 1. Redistributions of source code must retain the above copyright
110ba1b365SEd Maste  *    notice, this list of conditions and the following disclaimer.
12c21dee17SSøren Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
13c21dee17SSøren Schmidt  *    notice, this list of conditions and the following disclaimer in the
14c21dee17SSøren Schmidt  *    documentation and/or other materials provided with the distribution.
15c21dee17SSøren Schmidt  *
160ba1b365SEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170ba1b365SEd Maste  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180ba1b365SEd Maste  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190ba1b365SEd Maste  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200ba1b365SEd Maste  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210ba1b365SEd Maste  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220ba1b365SEd Maste  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230ba1b365SEd Maste  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240ba1b365SEd Maste  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250ba1b365SEd Maste  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260ba1b365SEd Maste  * SUCH DAMAGE.
27c21dee17SSøren Schmidt  */
28c21dee17SSøren Schmidt 
2916dbc7f2SDavid E. O'Brien #include <sys/cdefs.h>
3016dbc7f2SDavid E. O'Brien __FBSDID("$FreeBSD$");
3116dbc7f2SDavid E. O'Brien 
321f3dad5aSBruce Evans /* XXX we use functions that might not exist. */
33aefce619SRuslan Ermilov #include "opt_compat.h"
34ca26842eSHajimu UMEMOTO #include "opt_inet6.h"
355591b823SEivind Eklund 
36c21dee17SSøren Schmidt #include <sys/param.h>
37f2477ae1SMike Smith #include <sys/proc.h>
38c21dee17SSøren Schmidt #include <sys/systm.h>
391f3dad5aSBruce Evans #include <sys/sysproto.h>
404a144410SRobert Watson #include <sys/capsicum.h>
41dad3b88aSMike Smith #include <sys/fcntl.h>
420bf301c0SJonathan Lemon #include <sys/file.h>
43104a9b7eSAlexander Kabaev #include <sys/limits.h>
444641373fSJohn Baldwin #include <sys/lock.h>
45ca26842eSHajimu UMEMOTO #include <sys/malloc.h>
464641373fSJohn Baldwin #include <sys/mutex.h>
475a8a13e0SDavid Malone #include <sys/mbuf.h>
48c21dee17SSøren Schmidt #include <sys/socket.h>
490bf301c0SJonathan Lemon #include <sys/socketvar.h>
50ca26842eSHajimu UMEMOTO #include <sys/syscallsubr.h>
5108637435SBruce Evans #include <sys/uio.h>
52*fc7510aeSEd Maste #include <sys/stat.h>
53ca26842eSHajimu UMEMOTO #include <sys/syslog.h>
54d0b2365eSKonstantin Belousov #include <sys/un.h>
55*fc7510aeSEd Maste #include <sys/unistd.h>
56*fc7510aeSEd Maste 
57*fc7510aeSEd Maste #include <security/audit/audit.h>
581f3dad5aSBruce Evans 
594b79449eSBjoern A. Zeeb #include <net/if.h>
60eedc7fd9SGleb Smirnoff #include <net/vnet.h>
61c21dee17SSøren Schmidt #include <netinet/in.h>
62f2477ae1SMike Smith #include <netinet/in_systm.h>
63f2477ae1SMike Smith #include <netinet/ip.h>
64fb709557SJohn Baldwin #include <netinet/tcp.h>
65ca26842eSHajimu UMEMOTO #ifdef INET6
66ca26842eSHajimu UMEMOTO #include <netinet/ip6.h>
67ca26842eSHajimu UMEMOTO #include <netinet6/ip6_var.h>
68ca26842eSHajimu UMEMOTO #endif
69c21dee17SSøren Schmidt 
701997c537SDavid E. O'Brien #ifdef COMPAT_LINUX32
714af27623STim J. Robbins #include <machine/../linux32/linux.h>
724af27623STim J. Robbins #include <machine/../linux32/linux32_proto.h>
731997c537SDavid E. O'Brien #else
741997c537SDavid E. O'Brien #include <machine/../linux/linux.h>
751997c537SDavid E. O'Brien #include <machine/../linux/linux_proto.h>
764af27623STim J. Robbins #endif
77d5368bf3SDmitry Chagin #include <compat/linux/linux_common.h>
784d0f380dSDmitry Chagin #include <compat/linux/linux_file.h>
79da6d8ae6SEdward Tomasz Napierala #include <compat/linux/linux_mib.h>
8040dbba57SAssar Westerlund #include <compat/linux/linux_socket.h>
81e1ff74c0SDmitry Chagin #include <compat/linux/linux_timer.h>
82ac951e62SMarcel Moolenaar #include <compat/linux/linux_util.h>
83c21dee17SSøren Schmidt 
84e1ff74c0SDmitry Chagin static int linux_sendmsg_common(struct thread *, l_int, struct l_msghdr *,
85e1ff74c0SDmitry Chagin 					l_uint);
86e1ff74c0SDmitry Chagin static int linux_recvmsg_common(struct thread *, l_int, struct l_msghdr *,
87e1ff74c0SDmitry Chagin 					l_uint, struct msghdr *);
884cf10e29SDmitry Chagin static int linux_set_socket_flags(int, int *);
89ca26842eSHajimu UMEMOTO 
90ca26842eSHajimu UMEMOTO 
91c21dee17SSøren Schmidt static int
92c21dee17SSøren Schmidt linux_to_bsd_sockopt_level(int level)
93c21dee17SSøren Schmidt {
943f3a4815SMarcel Moolenaar 
95c21dee17SSøren Schmidt 	switch (level) {
96c21dee17SSøren Schmidt 	case LINUX_SOL_SOCKET:
973f3a4815SMarcel Moolenaar 		return (SOL_SOCKET);
98c21dee17SSøren Schmidt 	}
993f3a4815SMarcel Moolenaar 	return (level);
100c21dee17SSøren Schmidt }
101c21dee17SSøren Schmidt 
1023f3a4815SMarcel Moolenaar static int
10384b11cd8SMitsuru IWASAKI bsd_to_linux_sockopt_level(int level)
10484b11cd8SMitsuru IWASAKI {
10584b11cd8SMitsuru IWASAKI 
10684b11cd8SMitsuru IWASAKI 	switch (level) {
10784b11cd8SMitsuru IWASAKI 	case SOL_SOCKET:
10884b11cd8SMitsuru IWASAKI 		return (LINUX_SOL_SOCKET);
10984b11cd8SMitsuru IWASAKI 	}
11084b11cd8SMitsuru IWASAKI 	return (level);
11184b11cd8SMitsuru IWASAKI }
11284b11cd8SMitsuru IWASAKI 
11384b11cd8SMitsuru IWASAKI static int
1143f3a4815SMarcel Moolenaar linux_to_bsd_ip_sockopt(int opt)
115c21dee17SSøren Schmidt {
1163f3a4815SMarcel Moolenaar 
117c21dee17SSøren Schmidt 	switch (opt) {
118c21dee17SSøren Schmidt 	case LINUX_IP_TOS:
1193f3a4815SMarcel Moolenaar 		return (IP_TOS);
120c21dee17SSøren Schmidt 	case LINUX_IP_TTL:
1213f3a4815SMarcel Moolenaar 		return (IP_TTL);
12266ff6a3cSBill Fenner 	case LINUX_IP_OPTIONS:
1233f3a4815SMarcel Moolenaar 		return (IP_OPTIONS);
12466ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_IF:
1253f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_IF);
12666ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_TTL:
1273f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_TTL);
12866ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_LOOP:
1293f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_LOOP);
13066ff6a3cSBill Fenner 	case LINUX_IP_ADD_MEMBERSHIP:
1313f3a4815SMarcel Moolenaar 		return (IP_ADD_MEMBERSHIP);
13266ff6a3cSBill Fenner 	case LINUX_IP_DROP_MEMBERSHIP:
1333f3a4815SMarcel Moolenaar 		return (IP_DROP_MEMBERSHIP);
13466ff6a3cSBill Fenner 	case LINUX_IP_HDRINCL:
1353f3a4815SMarcel Moolenaar 		return (IP_HDRINCL);
136c21dee17SSøren Schmidt 	}
1373f3a4815SMarcel Moolenaar 	return (-1);
138c21dee17SSøren Schmidt }
139c21dee17SSøren Schmidt 
140c21dee17SSøren Schmidt static int
14186a9058bSAndrey V. Elsukov linux_to_bsd_ip6_sockopt(int opt)
14286a9058bSAndrey V. Elsukov {
14386a9058bSAndrey V. Elsukov 
14486a9058bSAndrey V. Elsukov 	switch (opt) {
14586a9058bSAndrey V. Elsukov 	case LINUX_IPV6_NEXTHOP:
14686a9058bSAndrey V. Elsukov 		return (IPV6_NEXTHOP);
14786a9058bSAndrey V. Elsukov 	case LINUX_IPV6_UNICAST_HOPS:
14886a9058bSAndrey V. Elsukov 		return (IPV6_UNICAST_HOPS);
14986a9058bSAndrey V. Elsukov 	case LINUX_IPV6_MULTICAST_IF:
15086a9058bSAndrey V. Elsukov 		return (IPV6_MULTICAST_IF);
15186a9058bSAndrey V. Elsukov 	case LINUX_IPV6_MULTICAST_HOPS:
15286a9058bSAndrey V. Elsukov 		return (IPV6_MULTICAST_HOPS);
15386a9058bSAndrey V. Elsukov 	case LINUX_IPV6_MULTICAST_LOOP:
15486a9058bSAndrey V. Elsukov 		return (IPV6_MULTICAST_LOOP);
15586a9058bSAndrey V. Elsukov 	case LINUX_IPV6_ADD_MEMBERSHIP:
15686a9058bSAndrey V. Elsukov 		return (IPV6_JOIN_GROUP);
15786a9058bSAndrey V. Elsukov 	case LINUX_IPV6_DROP_MEMBERSHIP:
15886a9058bSAndrey V. Elsukov 		return (IPV6_LEAVE_GROUP);
15986a9058bSAndrey V. Elsukov 	case LINUX_IPV6_V6ONLY:
16086a9058bSAndrey V. Elsukov 		return (IPV6_V6ONLY);
16186a9058bSAndrey V. Elsukov 	case LINUX_IPV6_DONTFRAG:
16286a9058bSAndrey V. Elsukov 		return (IPV6_DONTFRAG);
16386a9058bSAndrey V. Elsukov #if 0
16486a9058bSAndrey V. Elsukov 	case LINUX_IPV6_CHECKSUM:
16586a9058bSAndrey V. Elsukov 		return (IPV6_CHECKSUM);
16686a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVPKTINFO:
16786a9058bSAndrey V. Elsukov 		return (IPV6_RECVPKTINFO);
16886a9058bSAndrey V. Elsukov 	case LINUX_IPV6_PKTINFO:
16986a9058bSAndrey V. Elsukov 		return (IPV6_PKTINFO);
17086a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVHOPLIMIT:
17186a9058bSAndrey V. Elsukov 		return (IPV6_RECVHOPLIMIT);
17286a9058bSAndrey V. Elsukov 	case LINUX_IPV6_HOPLIMIT:
17386a9058bSAndrey V. Elsukov 		return (IPV6_HOPLIMIT);
17486a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVHOPOPTS:
17586a9058bSAndrey V. Elsukov 		return (IPV6_RECVHOPOPTS);
17686a9058bSAndrey V. Elsukov 	case LINUX_IPV6_HOPOPTS:
17786a9058bSAndrey V. Elsukov 		return (IPV6_HOPOPTS);
17886a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RTHDRDSTOPTS:
17986a9058bSAndrey V. Elsukov 		return (IPV6_RTHDRDSTOPTS);
18086a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVRTHDR:
18186a9058bSAndrey V. Elsukov 		return (IPV6_RECVRTHDR);
18286a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RTHDR:
18386a9058bSAndrey V. Elsukov 		return (IPV6_RTHDR);
18486a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVDSTOPTS:
18586a9058bSAndrey V. Elsukov 		return (IPV6_RECVDSTOPTS);
18686a9058bSAndrey V. Elsukov 	case LINUX_IPV6_DSTOPTS:
18786a9058bSAndrey V. Elsukov 		return (IPV6_DSTOPTS);
18886a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVPATHMTU:
18986a9058bSAndrey V. Elsukov 		return (IPV6_RECVPATHMTU);
19086a9058bSAndrey V. Elsukov 	case LINUX_IPV6_PATHMTU:
19186a9058bSAndrey V. Elsukov 		return (IPV6_PATHMTU);
19286a9058bSAndrey V. Elsukov #endif
19386a9058bSAndrey V. Elsukov 	}
19486a9058bSAndrey V. Elsukov 	return (-1);
19586a9058bSAndrey V. Elsukov }
19686a9058bSAndrey V. Elsukov 
19786a9058bSAndrey V. Elsukov static int
198c21dee17SSøren Schmidt linux_to_bsd_so_sockopt(int opt)
199c21dee17SSøren Schmidt {
2003f3a4815SMarcel Moolenaar 
201c21dee17SSøren Schmidt 	switch (opt) {
202c21dee17SSøren Schmidt 	case LINUX_SO_DEBUG:
2033f3a4815SMarcel Moolenaar 		return (SO_DEBUG);
204c21dee17SSøren Schmidt 	case LINUX_SO_REUSEADDR:
2053f3a4815SMarcel Moolenaar 		return (SO_REUSEADDR);
206c21dee17SSøren Schmidt 	case LINUX_SO_TYPE:
2073f3a4815SMarcel Moolenaar 		return (SO_TYPE);
208c21dee17SSøren Schmidt 	case LINUX_SO_ERROR:
2093f3a4815SMarcel Moolenaar 		return (SO_ERROR);
210c21dee17SSøren Schmidt 	case LINUX_SO_DONTROUTE:
2113f3a4815SMarcel Moolenaar 		return (SO_DONTROUTE);
212c21dee17SSøren Schmidt 	case LINUX_SO_BROADCAST:
2133f3a4815SMarcel Moolenaar 		return (SO_BROADCAST);
214c21dee17SSøren Schmidt 	case LINUX_SO_SNDBUF:
2153f3a4815SMarcel Moolenaar 		return (SO_SNDBUF);
216c21dee17SSøren Schmidt 	case LINUX_SO_RCVBUF:
2173f3a4815SMarcel Moolenaar 		return (SO_RCVBUF);
218c21dee17SSøren Schmidt 	case LINUX_SO_KEEPALIVE:
2193f3a4815SMarcel Moolenaar 		return (SO_KEEPALIVE);
220c21dee17SSøren Schmidt 	case LINUX_SO_OOBINLINE:
2213f3a4815SMarcel Moolenaar 		return (SO_OOBINLINE);
222c21dee17SSøren Schmidt 	case LINUX_SO_LINGER:
2233f3a4815SMarcel Moolenaar 		return (SO_LINGER);
224d0b2365eSKonstantin Belousov 	case LINUX_SO_PEERCRED:
225d0b2365eSKonstantin Belousov 		return (LOCAL_PEERCRED);
226d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVLOWAT:
227d0b2365eSKonstantin Belousov 		return (SO_RCVLOWAT);
228d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDLOWAT:
229d0b2365eSKonstantin Belousov 		return (SO_SNDLOWAT);
230d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVTIMEO:
231d0b2365eSKonstantin Belousov 		return (SO_RCVTIMEO);
232d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDTIMEO:
233d0b2365eSKonstantin Belousov 		return (SO_SNDTIMEO);
234d0b2365eSKonstantin Belousov 	case LINUX_SO_TIMESTAMP:
235d0b2365eSKonstantin Belousov 		return (SO_TIMESTAMP);
236d0b2365eSKonstantin Belousov 	case LINUX_SO_ACCEPTCONN:
237d0b2365eSKonstantin Belousov 		return (SO_ACCEPTCONN);
238c21dee17SSøren Schmidt 	}
2393f3a4815SMarcel Moolenaar 	return (-1);
240c21dee17SSøren Schmidt }
241c21dee17SSøren Schmidt 
24240dbba57SAssar Westerlund static int
243fb709557SJohn Baldwin linux_to_bsd_tcp_sockopt(int opt)
244fb709557SJohn Baldwin {
245fb709557SJohn Baldwin 
246fb709557SJohn Baldwin 	switch (opt) {
247fb709557SJohn Baldwin 	case LINUX_TCP_NODELAY:
248fb709557SJohn Baldwin 		return (TCP_NODELAY);
249fb709557SJohn Baldwin 	case LINUX_TCP_MAXSEG:
250fb709557SJohn Baldwin 		return (TCP_MAXSEG);
251c2d47457SEdward Tomasz Napierala 	case LINUX_TCP_CORK:
252c2d47457SEdward Tomasz Napierala 		return (TCP_NOPUSH);
253fb709557SJohn Baldwin 	case LINUX_TCP_KEEPIDLE:
254fb709557SJohn Baldwin 		return (TCP_KEEPIDLE);
255fb709557SJohn Baldwin 	case LINUX_TCP_KEEPINTVL:
256fb709557SJohn Baldwin 		return (TCP_KEEPINTVL);
257fb709557SJohn Baldwin 	case LINUX_TCP_KEEPCNT:
258fb709557SJohn Baldwin 		return (TCP_KEEPCNT);
259fb709557SJohn Baldwin 	case LINUX_TCP_MD5SIG:
260fb709557SJohn Baldwin 		return (TCP_MD5SIG);
261fb709557SJohn Baldwin 	}
262fb709557SJohn Baldwin 	return (-1);
263fb709557SJohn Baldwin }
264fb709557SJohn Baldwin 
265fb709557SJohn Baldwin static int
26640dbba57SAssar Westerlund linux_to_bsd_msg_flags(int flags)
26740dbba57SAssar Westerlund {
26840dbba57SAssar Westerlund 	int ret_flags = 0;
26940dbba57SAssar Westerlund 
27040dbba57SAssar Westerlund 	if (flags & LINUX_MSG_OOB)
27140dbba57SAssar Westerlund 		ret_flags |= MSG_OOB;
27240dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PEEK)
27340dbba57SAssar Westerlund 		ret_flags |= MSG_PEEK;
27440dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTROUTE)
27540dbba57SAssar Westerlund 		ret_flags |= MSG_DONTROUTE;
27640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CTRUNC)
27740dbba57SAssar Westerlund 		ret_flags |= MSG_CTRUNC;
27840dbba57SAssar Westerlund 	if (flags & LINUX_MSG_TRUNC)
27940dbba57SAssar Westerlund 		ret_flags |= MSG_TRUNC;
28040dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTWAIT)
28140dbba57SAssar Westerlund 		ret_flags |= MSG_DONTWAIT;
28240dbba57SAssar Westerlund 	if (flags & LINUX_MSG_EOR)
28340dbba57SAssar Westerlund 		ret_flags |= MSG_EOR;
28440dbba57SAssar Westerlund 	if (flags & LINUX_MSG_WAITALL)
28540dbba57SAssar Westerlund 		ret_flags |= MSG_WAITALL;
2868d6e40c3SMaxim Sobolev 	if (flags & LINUX_MSG_NOSIGNAL)
2878d6e40c3SMaxim Sobolev 		ret_flags |= MSG_NOSIGNAL;
28840dbba57SAssar Westerlund #if 0 /* not handled */
28940dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PROXY)
29040dbba57SAssar Westerlund 		;
29140dbba57SAssar Westerlund 	if (flags & LINUX_MSG_FIN)
29240dbba57SAssar Westerlund 		;
29340dbba57SAssar Westerlund 	if (flags & LINUX_MSG_SYN)
29440dbba57SAssar Westerlund 		;
29540dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CONFIRM)
29640dbba57SAssar Westerlund 		;
29740dbba57SAssar Westerlund 	if (flags & LINUX_MSG_RST)
29840dbba57SAssar Westerlund 		;
29940dbba57SAssar Westerlund 	if (flags & LINUX_MSG_ERRQUEUE)
30040dbba57SAssar Westerlund 		;
30140dbba57SAssar Westerlund #endif
302e667ee63SDmitry Chagin 	return (ret_flags);
30340dbba57SAssar Westerlund }
30440dbba57SAssar Westerlund 
3055a8a13e0SDavid Malone static int
30674f5d680SKonstantin Belousov linux_to_bsd_cmsg_type(int cmsg_type)
3075a8a13e0SDavid Malone {
30874f5d680SKonstantin Belousov 
30974f5d680SKonstantin Belousov 	switch (cmsg_type) {
31074f5d680SKonstantin Belousov 	case LINUX_SCM_RIGHTS:
31174f5d680SKonstantin Belousov 		return (SCM_RIGHTS);
312605da56bSAndriy Gapon 	case LINUX_SCM_CREDENTIALS:
313605da56bSAndriy Gapon 		return (SCM_CREDS);
31474f5d680SKonstantin Belousov 	}
31574f5d680SKonstantin Belousov 	return (-1);
31674f5d680SKonstantin Belousov }
31774f5d680SKonstantin Belousov 
31874f5d680SKonstantin Belousov static int
31974f5d680SKonstantin Belousov bsd_to_linux_cmsg_type(int cmsg_type)
32074f5d680SKonstantin Belousov {
32174f5d680SKonstantin Belousov 
32274f5d680SKonstantin Belousov 	switch (cmsg_type) {
32374f5d680SKonstantin Belousov 	case SCM_RIGHTS:
32474f5d680SKonstantin Belousov 		return (LINUX_SCM_RIGHTS);
325605da56bSAndriy Gapon 	case SCM_CREDS:
326605da56bSAndriy Gapon 		return (LINUX_SCM_CREDENTIALS);
327bbf392d5SDmitry Chagin 	case SCM_TIMESTAMP:
328bbf392d5SDmitry Chagin 		return (LINUX_SCM_TIMESTAMP);
32974f5d680SKonstantin Belousov 	}
33074f5d680SKonstantin Belousov 	return (-1);
33174f5d680SKonstantin Belousov }
33274f5d680SKonstantin Belousov 
33374f5d680SKonstantin Belousov static int
33474f5d680SKonstantin Belousov linux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
33574f5d680SKonstantin Belousov {
33674f5d680SKonstantin Belousov 	if (lhdr->msg_controllen > INT_MAX)
33774f5d680SKonstantin Belousov 		return (ENOBUFS);
33874f5d680SKonstantin Belousov 
33974f5d680SKonstantin Belousov 	bhdr->msg_name		= PTRIN(lhdr->msg_name);
34074f5d680SKonstantin Belousov 	bhdr->msg_namelen	= lhdr->msg_namelen;
34174f5d680SKonstantin Belousov 	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
34274f5d680SKonstantin Belousov 	bhdr->msg_iovlen	= lhdr->msg_iovlen;
34374f5d680SKonstantin Belousov 	bhdr->msg_control	= PTRIN(lhdr->msg_control);
344605da56bSAndriy Gapon 
345605da56bSAndriy Gapon 	/*
346605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
347605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
348605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
349605da56bSAndriy Gapon 	 *
350605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
351605da56bSAndriy Gapon 	 * control messages.
352605da56bSAndriy Gapon 	 */
353605da56bSAndriy Gapon 
35474f5d680SKonstantin Belousov 	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
35574f5d680SKonstantin Belousov 	return (0);
35674f5d680SKonstantin Belousov }
35774f5d680SKonstantin Belousov 
35874f5d680SKonstantin Belousov static int
35974f5d680SKonstantin Belousov bsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
36074f5d680SKonstantin Belousov {
36174f5d680SKonstantin Belousov 	lhdr->msg_name		= PTROUT(bhdr->msg_name);
36274f5d680SKonstantin Belousov 	lhdr->msg_namelen	= bhdr->msg_namelen;
36374f5d680SKonstantin Belousov 	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
36474f5d680SKonstantin Belousov 	lhdr->msg_iovlen	= bhdr->msg_iovlen;
36574f5d680SKonstantin Belousov 	lhdr->msg_control	= PTROUT(bhdr->msg_control);
366605da56bSAndriy Gapon 
367605da56bSAndriy Gapon 	/*
368605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
369605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
370605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
371605da56bSAndriy Gapon 	 *
372605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
373605da56bSAndriy Gapon 	 * control messages.
374605da56bSAndriy Gapon 	 */
375605da56bSAndriy Gapon 
37674f5d680SKonstantin Belousov 	/* msg_flags skipped */
37774f5d680SKonstantin Belousov 	return (0);
37874f5d680SKonstantin Belousov }
37974f5d680SKonstantin Belousov 
38074f5d680SKonstantin Belousov static int
3814cf10e29SDmitry Chagin linux_set_socket_flags(int lflags, int *flags)
38238a18e97SDmitry Chagin {
38338a18e97SDmitry Chagin 
3844cf10e29SDmitry Chagin 	if (lflags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
3854cf10e29SDmitry Chagin 		return (EINVAL);
3864cf10e29SDmitry Chagin 	if (lflags & LINUX_SOCK_NONBLOCK)
3874cf10e29SDmitry Chagin 		*flags |= SOCK_NONBLOCK;
3884cf10e29SDmitry Chagin 	if (lflags & LINUX_SOCK_CLOEXEC)
3894cf10e29SDmitry Chagin 		*flags |= SOCK_CLOEXEC;
39038a18e97SDmitry Chagin 	return (0);
39138a18e97SDmitry Chagin }
39238a18e97SDmitry Chagin 
39338a18e97SDmitry Chagin static int
39474f5d680SKonstantin Belousov linux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
39574f5d680SKonstantin Belousov     struct mbuf *control, enum uio_seg segflg)
39674f5d680SKonstantin Belousov {
3975a8a13e0SDavid Malone 	struct sockaddr *to;
398d5368bf3SDmitry Chagin 	int error, len;
3995a8a13e0SDavid Malone 
4005a8a13e0SDavid Malone 	if (mp->msg_name != NULL) {
401d5368bf3SDmitry Chagin 		len = mp->msg_namelen;
402d5368bf3SDmitry Chagin 		error = linux_to_bsd_sockaddr(mp->msg_name, &to, &len);
403dddb7e7fSDmitry Chagin 		if (error != 0)
4045a8a13e0SDavid Malone 			return (error);
4055a8a13e0SDavid Malone 		mp->msg_name = to;
4065a8a13e0SDavid Malone 	} else
4075a8a13e0SDavid Malone 		to = NULL;
4085a8a13e0SDavid Malone 
409a6886ef1SMaxim Sobolev 	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
410a6886ef1SMaxim Sobolev 	    segflg);
4115a8a13e0SDavid Malone 
4125a8a13e0SDavid Malone 	if (to)
4131ede983cSDag-Erling Smørgrav 		free(to, M_SONAME);
4145a8a13e0SDavid Malone 	return (error);
4155a8a13e0SDavid Malone }
4165a8a13e0SDavid Malone 
4173f3a4815SMarcel Moolenaar /* Return 0 if IP_HDRINCL is set for the given socket. */
418f2477ae1SMike Smith static int
419e140eb43SDavid Malone linux_check_hdrincl(struct thread *td, int s)
420f2477ae1SMike Smith {
421857ad5a3SDmitry Chagin 	int error, optval;
422857ad5a3SDmitry Chagin 	socklen_t size_val;
423f2477ae1SMike Smith 
424e140eb43SDavid Malone 	size_val = sizeof(optval);
425e140eb43SDavid Malone 	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
426e140eb43SDavid Malone 	    &optval, UIO_SYSSPACE, &size_val);
427dddb7e7fSDmitry Chagin 	if (error != 0)
4283f3a4815SMarcel Moolenaar 		return (error);
4293f3a4815SMarcel Moolenaar 
4303f3a4815SMarcel Moolenaar 	return (optval == 0);
431f2477ae1SMike Smith }
432f2477ae1SMike Smith 
433f2477ae1SMike Smith /*
434f2477ae1SMike Smith  * Updated sendto() when IP_HDRINCL is set:
435f2477ae1SMike Smith  * tweak endian-dependent fields in the IP packet.
436f2477ae1SMike Smith  */
437f2477ae1SMike Smith static int
43838da2381SRobert Watson linux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
439f2477ae1SMike Smith {
440f2477ae1SMike Smith /*
441f2477ae1SMike Smith  * linux_ip_copysize defines how many bytes we should copy
442f2477ae1SMike Smith  * from the beginning of the IP packet before we customize it for BSD.
443a6886ef1SMaxim Sobolev  * It should include all the fields we modify (ip_len and ip_off).
444f2477ae1SMike Smith  */
445f2477ae1SMike Smith #define linux_ip_copysize	8
446f2477ae1SMike Smith 
447f2477ae1SMike Smith 	struct ip *packet;
4485a8a13e0SDavid Malone 	struct msghdr msg;
449a6886ef1SMaxim Sobolev 	struct iovec aiov[1];
450f2477ae1SMike Smith 	int error;
451f2477ae1SMike Smith 
452aa675b57SDavid Schultz 	/* Check that the packet isn't too big or too small. */
453aa675b57SDavid Schultz 	if (linux_args->len < linux_ip_copysize ||
454aa675b57SDavid Schultz 	    linux_args->len > IP_MAXPACKET)
4553f3a4815SMarcel Moolenaar 		return (EINVAL);
456f2477ae1SMike Smith 
457e0d3ea8cSDmitry Chagin 	packet = (struct ip *)malloc(linux_args->len, M_LINUX, M_WAITOK);
458f2477ae1SMike Smith 
459a6886ef1SMaxim Sobolev 	/* Make kernel copy of the packet to be sent */
4604af27623STim J. Robbins 	if ((error = copyin(PTRIN(linux_args->msg), packet,
461a6886ef1SMaxim Sobolev 	    linux_args->len)))
462a6886ef1SMaxim Sobolev 		goto goout;
463f2477ae1SMike Smith 
464f2477ae1SMike Smith 	/* Convert fields from Linux to BSD raw IP socket format */
4655a8a13e0SDavid Malone 	packet->ip_len = linux_args->len;
466f2477ae1SMike Smith 	packet->ip_off = ntohs(packet->ip_off);
467f2477ae1SMike Smith 
468f2477ae1SMike Smith 	/* Prepare the msghdr and iovec structures describing the new packet */
4694af27623STim J. Robbins 	msg.msg_name = PTRIN(linux_args->to);
4705a8a13e0SDavid Malone 	msg.msg_namelen = linux_args->tolen;
4715a8a13e0SDavid Malone 	msg.msg_iov = aiov;
472a6886ef1SMaxim Sobolev 	msg.msg_iovlen = 1;
4735a8a13e0SDavid Malone 	msg.msg_control = NULL;
4745a8a13e0SDavid Malone 	msg.msg_flags = 0;
4755a8a13e0SDavid Malone 	aiov[0].iov_base = (char *)packet;
476a6886ef1SMaxim Sobolev 	aiov[0].iov_len = linux_args->len;
477a6886ef1SMaxim Sobolev 	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
47874f5d680SKonstantin Belousov 	    NULL, UIO_SYSSPACE);
479a6886ef1SMaxim Sobolev goout:
480e0d3ea8cSDmitry Chagin 	free(packet, M_LINUX);
4815a8a13e0SDavid Malone 	return (error);
482f2477ae1SMike Smith }
483f2477ae1SMike Smith 
484a12b9b3dSDmitry Chagin int
485b40ce416SJulian Elischer linux_socket(struct thread *td, struct linux_socket_args *args)
486c21dee17SSøren Schmidt {
487d293f35cSEdward Tomasz Napierala 	int domain, retval_socket, type;
488c21dee17SSøren Schmidt 
489d293f35cSEdward Tomasz Napierala 	type = args->type & LINUX_SOCK_TYPE_MASK;
490d293f35cSEdward Tomasz Napierala 	if (type < 0 || type > LINUX_SOCK_MAX)
491eeb63e51SDmitry Chagin 		return (EINVAL);
4924cf10e29SDmitry Chagin 	retval_socket = linux_set_socket_flags(args->type & ~LINUX_SOCK_TYPE_MASK,
493d293f35cSEdward Tomasz Napierala 		&type);
4944cf10e29SDmitry Chagin 	if (retval_socket != 0)
4954cf10e29SDmitry Chagin 		return (retval_socket);
496d293f35cSEdward Tomasz Napierala 	domain = linux_to_bsd_domain(args->domain);
497d293f35cSEdward Tomasz Napierala 	if (domain == -1)
498d9b063ccSDmitry Chagin 		return (EAFNOSUPPORT);
499f2477ae1SMike Smith 
500d293f35cSEdward Tomasz Napierala 	retval_socket = kern_socket(td, domain, type, args->protocol);
5016994ea54SDmitry Chagin 	if (retval_socket)
5026994ea54SDmitry Chagin 		return (retval_socket);
5036994ea54SDmitry Chagin 
504d293f35cSEdward Tomasz Napierala 	if (type == SOCK_RAW
505d293f35cSEdward Tomasz Napierala 	    && (args->protocol == IPPROTO_RAW || args->protocol == 0)
506d293f35cSEdward Tomasz Napierala 	    && domain == PF_INET) {
507f2477ae1SMike Smith 		/* It's a raw IP socket: set the IP_HDRINCL option. */
508e140eb43SDavid Malone 		int hdrincl;
509f2477ae1SMike Smith 
510e140eb43SDavid Malone 		hdrincl = 1;
511e140eb43SDavid Malone 		/* We ignore any error returned by kern_setsockopt() */
512e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
513e140eb43SDavid Malone 		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
514f2477ae1SMike Smith 	}
515ca26842eSHajimu UMEMOTO #ifdef INET6
516ca26842eSHajimu UMEMOTO 	/*
517d97bee3eSBjoern A. Zeeb 	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by default
518d97bee3eSBjoern A. Zeeb 	 * and some apps depend on this. So, set V6ONLY to 0 for Linux apps.
519d97bee3eSBjoern A. Zeeb 	 * For simplicity we do this unconditionally of the net.inet6.ip6.v6only
520d97bee3eSBjoern A. Zeeb 	 * sysctl value.
521ca26842eSHajimu UMEMOTO 	 */
522d293f35cSEdward Tomasz Napierala 	if (domain == PF_INET6) {
523e140eb43SDavid Malone 		int v6only;
524ca26842eSHajimu UMEMOTO 
525e140eb43SDavid Malone 		v6only = 0;
526ca26842eSHajimu UMEMOTO 		/* We ignore any error returned by setsockopt() */
527e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
528e140eb43SDavid Malone 		    &v6only, UIO_SYSSPACE, sizeof(v6only));
529ca26842eSHajimu UMEMOTO 	}
530ca26842eSHajimu UMEMOTO #endif
5313f3a4815SMarcel Moolenaar 
5323f3a4815SMarcel Moolenaar 	return (retval_socket);
533c21dee17SSøren Schmidt }
534c21dee17SSøren Schmidt 
535a12b9b3dSDmitry Chagin int
536b40ce416SJulian Elischer linux_bind(struct thread *td, struct linux_bind_args *args)
537c21dee17SSøren Schmidt {
538ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
539c21dee17SSøren Schmidt 	int error;
540c21dee17SSøren Schmidt 
541d5368bf3SDmitry Chagin 	error = linux_to_bsd_sockaddr(PTRIN(args->name), &sa,
542d5368bf3SDmitry Chagin 	    &args->namelen);
543dddb7e7fSDmitry Chagin 	if (error != 0)
544ca26842eSHajimu UMEMOTO 		return (error);
545ca26842eSHajimu UMEMOTO 
5466e646651SKonstantin Belousov 	error = kern_bindat(td, AT_FDCWD, args->s, sa);
547b33887eaSJohn Baldwin 	free(sa, M_SONAME);
548d5368bf3SDmitry Chagin 
549d5368bf3SDmitry Chagin 	/* XXX */
550745aaef5SKonstantin Belousov 	if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
551d4b7423fSAlexander Leidinger 		return (EINVAL);
552b33887eaSJohn Baldwin 	return (error);
553c21dee17SSøren Schmidt }
554c21dee17SSøren Schmidt 
555930a65feSAndrew Gallatin int
556b40ce416SJulian Elischer linux_connect(struct thread *td, struct linux_connect_args *args)
557c21dee17SSøren Schmidt {
5580bf301c0SJonathan Lemon 	struct socket *so;
559ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
56095653579SGleb Smirnoff 	struct file *fp;
56139c95b83SMatthew Dillon 	u_int fflag;
562c21dee17SSøren Schmidt 	int error;
563c21dee17SSøren Schmidt 
564d5368bf3SDmitry Chagin 	error = linux_to_bsd_sockaddr(PTRIN(args->name), &sa,
565d5368bf3SDmitry Chagin 	    &args->namelen);
566dddb7e7fSDmitry Chagin 	if (error != 0)
567ca26842eSHajimu UMEMOTO 		return (error);
568ca26842eSHajimu UMEMOTO 
5696e646651SKonstantin Belousov 	error = kern_connectat(td, AT_FDCWD, args->s, sa);
570b33887eaSJohn Baldwin 	free(sa, M_SONAME);
5710bf301c0SJonathan Lemon 	if (error != EISCONN)
5720bf301c0SJonathan Lemon 		return (error);
5730bf301c0SJonathan Lemon 
574dad3b88aSMike Smith 	/*
575dad3b88aSMike Smith 	 * Linux doesn't return EISCONN the first time it occurs,
576dad3b88aSMike Smith 	 * when on a non-blocking socket. Instead it returns the
577dad3b88aSMike Smith 	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
578dad3b88aSMike Smith 	 */
579cbd92ce6SMatt Macy 	error = getsock_cap(td, args->s, &cap_connect_rights,
58095653579SGleb Smirnoff 	    &fp, &fflag, NULL);
58195653579SGleb Smirnoff 	if (error != 0)
58295653579SGleb Smirnoff 		return (error);
58395653579SGleb Smirnoff 
5840bf301c0SJonathan Lemon 	error = EISCONN;
58595653579SGleb Smirnoff 	so = fp->f_data;
58639c95b83SMatthew Dillon 	if (fflag & FNONBLOCK) {
5874641373fSJohn Baldwin 		SOCK_LOCK(so);
5885002a60fSMarcel Moolenaar 		if (so->so_emuldata == 0)
5890bf301c0SJonathan Lemon 			error = so->so_error;
5900bf301c0SJonathan Lemon 		so->so_emuldata = (void *)1;
5914641373fSJohn Baldwin 		SOCK_UNLOCK(so);
592dad3b88aSMike Smith 	}
59395653579SGleb Smirnoff 	fdrop(fp, td);
59495653579SGleb Smirnoff 
5953f3a4815SMarcel Moolenaar 	return (error);
596c21dee17SSøren Schmidt }
597c21dee17SSøren Schmidt 
598a12b9b3dSDmitry Chagin int
599b40ce416SJulian Elischer linux_listen(struct thread *td, struct linux_listen_args *args)
600c21dee17SSøren Schmidt {
601c21dee17SSøren Schmidt 
602d293f35cSEdward Tomasz Napierala 	return (kern_listen(td, args->s, args->backlog));
603c21dee17SSøren Schmidt }
604c21dee17SSøren Schmidt 
60501e0ffbaSAlexander Leidinger static int
606c8f37d61SDmitry Chagin linux_accept_common(struct thread *td, int s, l_uintptr_t addr,
607f83427b8SDmitry Chagin     l_uintptr_t namelen, int flags)
608c21dee17SSøren Schmidt {
609d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
610d5368bf3SDmitry Chagin 	struct sockaddr *sa;
611fc4b98fbSDmitry Chagin 	struct file *fp;
612d5368bf3SDmitry Chagin 	int bflags, len;
613d5368bf3SDmitry Chagin 	struct socket *so;
614fc4b98fbSDmitry Chagin 	int error, error1;
61593e694c9SDmitry Chagin 
616d5368bf3SDmitry Chagin 	bflags = 0;
617d5368bf3SDmitry Chagin 	error = linux_set_socket_flags(flags, &bflags);
6184cf10e29SDmitry Chagin 	if (error != 0)
6194cf10e29SDmitry Chagin 		return (error);
620d5368bf3SDmitry Chagin 
621d5368bf3SDmitry Chagin 	sa = NULL;
622d5368bf3SDmitry Chagin 	if (PTRIN(addr) == NULL) {
623d5368bf3SDmitry Chagin 		len = 0;
624d5368bf3SDmitry Chagin 		error = kern_accept4(td, s, NULL, NULL, bflags, NULL);
625d5368bf3SDmitry Chagin 	} else {
626d5368bf3SDmitry Chagin 		error = copyin(PTRIN(namelen), &len, sizeof(len));
627d5368bf3SDmitry Chagin 		if (error != 0)
628d5368bf3SDmitry Chagin 			return (error);
629d5368bf3SDmitry Chagin 		if (len < 0)
630d4b7423fSAlexander Leidinger 			return (EINVAL);
631d5368bf3SDmitry Chagin 		error = kern_accept4(td, s, &sa, &len, bflags, &fp);
632d5368bf3SDmitry Chagin 		if (error == 0)
633d5368bf3SDmitry Chagin 			fdrop(fp, td);
634d5368bf3SDmitry Chagin 	}
635d5368bf3SDmitry Chagin 
636d5368bf3SDmitry Chagin 	if (error != 0) {
637d5368bf3SDmitry Chagin 		/*
638d5368bf3SDmitry Chagin 		 * XXX. This is wrong, different sockaddr structures
639d5368bf3SDmitry Chagin 		 * have different sizes.
640d5368bf3SDmitry Chagin 		 */
641d5368bf3SDmitry Chagin 		if (error == EFAULT && namelen != sizeof(struct sockaddr_in))
642d5368bf3SDmitry Chagin 		{
643d5368bf3SDmitry Chagin 			error = EINVAL;
644d5368bf3SDmitry Chagin 			goto out;
645d5368bf3SDmitry Chagin 		}
646fc4b98fbSDmitry Chagin 		if (error == EINVAL) {
647cbd92ce6SMatt Macy 			error1 = getsock_cap(td, s, &cap_accept_rights, &fp, NULL, NULL);
648d5368bf3SDmitry Chagin 			if (error1 != 0) {
649d5368bf3SDmitry Chagin 				error = error1;
650d5368bf3SDmitry Chagin 				goto out;
651d5368bf3SDmitry Chagin 			}
65291f514e4SDmitry Chagin 			so = fp->f_data;
653d5368bf3SDmitry Chagin 			if (so->so_type == SOCK_DGRAM)
654d5368bf3SDmitry Chagin 				error = EOPNOTSUPP;
65591f514e4SDmitry Chagin 			fdrop(fp, td);
65691f514e4SDmitry Chagin 		}
657d5368bf3SDmitry Chagin 		goto out;
658d4b7423fSAlexander Leidinger 	}
659d5368bf3SDmitry Chagin 
660d5368bf3SDmitry Chagin 	if (len != 0 && error == 0) {
661d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
662d5368bf3SDmitry Chagin 		if (error == 0)
663d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(addr), len);
664d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
665d5368bf3SDmitry Chagin 	}
666d5368bf3SDmitry Chagin 
667d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
668d5368bf3SDmitry Chagin 
669d5368bf3SDmitry Chagin out:
670dddb7e7fSDmitry Chagin 	if (error != 0) {
67193e694c9SDmitry Chagin 		(void)kern_close(td, td->td_retval[0]);
67293e694c9SDmitry Chagin 		td->td_retval[0] = 0;
67393e694c9SDmitry Chagin 	}
67493e694c9SDmitry Chagin 	return (error);
675c21dee17SSøren Schmidt }
676c21dee17SSøren Schmidt 
677a12b9b3dSDmitry Chagin int
678c8f37d61SDmitry Chagin linux_accept(struct thread *td, struct linux_accept_args *args)
679c8f37d61SDmitry Chagin {
680c8f37d61SDmitry Chagin 
681c8f37d61SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
682f83427b8SDmitry Chagin 	    args->namelen, 0));
683c8f37d61SDmitry Chagin }
684c8f37d61SDmitry Chagin 
685a12b9b3dSDmitry Chagin int
686f8cd0af2SDmitry Chagin linux_accept4(struct thread *td, struct linux_accept4_args *args)
687f8cd0af2SDmitry Chagin {
688f8cd0af2SDmitry Chagin 
689f8cd0af2SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
690f8cd0af2SDmitry Chagin 	    args->namelen, args->flags));
691f8cd0af2SDmitry Chagin }
692f8cd0af2SDmitry Chagin 
693a12b9b3dSDmitry Chagin int
694b40ce416SJulian Elischer linux_getsockname(struct thread *td, struct linux_getsockname_args *args)
695c21dee17SSøren Schmidt {
696d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
697d5368bf3SDmitry Chagin 	struct sockaddr *sa;
698d5368bf3SDmitry Chagin 	int len, error;
699c21dee17SSøren Schmidt 
700d5368bf3SDmitry Chagin 	error = copyin(PTRIN(args->namelen), &len, sizeof(len));
701dddb7e7fSDmitry Chagin 	if (error != 0)
702ca26842eSHajimu UMEMOTO 		return (error);
703d5368bf3SDmitry Chagin 
704d5368bf3SDmitry Chagin 	error = kern_getsockname(td, args->s, &sa, &len);
705d5368bf3SDmitry Chagin 	if (error != 0)
706d5368bf3SDmitry Chagin 		return (error);
707d5368bf3SDmitry Chagin 
708d5368bf3SDmitry Chagin 	if (len != 0) {
709d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
710d5368bf3SDmitry Chagin 		if (error == 0)
711d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->addr),
712d5368bf3SDmitry Chagin 			    len);
713d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
714d5368bf3SDmitry Chagin 	}
715d5368bf3SDmitry Chagin 
716d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
717d5368bf3SDmitry Chagin 	if (error == 0)
718d5368bf3SDmitry Chagin 		error = copyout(&len, PTRIN(args->namelen), sizeof(len));
719d5368bf3SDmitry Chagin 	return (error);
720c21dee17SSøren Schmidt }
721c21dee17SSøren Schmidt 
722a12b9b3dSDmitry Chagin int
723b40ce416SJulian Elischer linux_getpeername(struct thread *td, struct linux_getpeername_args *args)
724c21dee17SSøren Schmidt {
725d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
726d5368bf3SDmitry Chagin 	struct sockaddr *sa;
727d5368bf3SDmitry Chagin 	int len, error;
728c21dee17SSøren Schmidt 
729d5368bf3SDmitry Chagin 	error = copyin(PTRIN(args->namelen), &len, sizeof(len));
730dddb7e7fSDmitry Chagin 	if (error != 0)
731ca26842eSHajimu UMEMOTO 		return (error);
732caaad873SDmitry Chagin 	if (len < 0)
733caaad873SDmitry Chagin 		return (EINVAL);
734d5368bf3SDmitry Chagin 
735d5368bf3SDmitry Chagin 	error = kern_getpeername(td, args->s, &sa, &len);
736d5368bf3SDmitry Chagin 	if (error != 0)
737d5368bf3SDmitry Chagin 		return (error);
738d5368bf3SDmitry Chagin 
739d5368bf3SDmitry Chagin 	if (len != 0) {
740d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
741d5368bf3SDmitry Chagin 		if (error == 0)
742d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->addr),
743d5368bf3SDmitry Chagin 			    len);
744d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
745d5368bf3SDmitry Chagin 	}
746d5368bf3SDmitry Chagin 
747d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
748d5368bf3SDmitry Chagin 	if (error == 0)
749d5368bf3SDmitry Chagin 		error = copyout(&len, PTRIN(args->namelen), sizeof(len));
750d5368bf3SDmitry Chagin 	return (error);
751c21dee17SSøren Schmidt }
752c21dee17SSøren Schmidt 
753a12b9b3dSDmitry Chagin int
754b40ce416SJulian Elischer linux_socketpair(struct thread *td, struct linux_socketpair_args *args)
755c21dee17SSøren Schmidt {
756ef04503dSPeter Wemm 	struct socketpair_args /* {
757c21dee17SSøren Schmidt 		int domain;
758c21dee17SSøren Schmidt 		int type;
759c21dee17SSøren Schmidt 		int protocol;
760c21dee17SSøren Schmidt 		int *rsv;
761ef04503dSPeter Wemm 	} */ bsd_args;
7624cf10e29SDmitry Chagin 	int error;
763c21dee17SSøren Schmidt 
764745aaef5SKonstantin Belousov 	bsd_args.domain = linux_to_bsd_domain(args->domain);
7651a52a4abSDmitry Chagin 	if (bsd_args.domain != PF_LOCAL)
7661a52a4abSDmitry Chagin 		return (EAFNOSUPPORT);
76739253cf9SDmitry Chagin 	bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
76839253cf9SDmitry Chagin 	if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
76939253cf9SDmitry Chagin 		return (EINVAL);
7704cf10e29SDmitry Chagin 	error = linux_set_socket_flags(args->type & ~LINUX_SOCK_TYPE_MASK,
7714cf10e29SDmitry Chagin 		&bsd_args.type);
7724cf10e29SDmitry Chagin 	if (error != 0)
7734cf10e29SDmitry Chagin 		return (error);
7741a52a4abSDmitry Chagin 	if (args->protocol != 0 && args->protocol != PF_UNIX)
7751a52a4abSDmitry Chagin 
7761a52a4abSDmitry Chagin 		/*
7771a52a4abSDmitry Chagin 		 * Use of PF_UNIX as protocol argument is not right,
7781a52a4abSDmitry Chagin 		 * but Linux does it.
7791a52a4abSDmitry Chagin 		 * Do not map PF_UNIX as its Linux value is identical
7801a52a4abSDmitry Chagin 		 * to FreeBSD one.
7811a52a4abSDmitry Chagin 		 */
7821a52a4abSDmitry Chagin 		return (EPROTONOSUPPORT);
78340092d93SDmitry Chagin 	else
7841a52a4abSDmitry Chagin 		bsd_args.protocol = 0;
785745aaef5SKonstantin Belousov 	bsd_args.rsv = (int *)PTRIN(args->rsv);
7864cf10e29SDmitry Chagin 	return (sys_socketpair(td, &bsd_args));
787c21dee17SSøren Schmidt }
788c21dee17SSøren Schmidt 
789a12b9b3dSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
79001e0ffbaSAlexander Leidinger struct linux_send_args {
79156fba8e6SDmitry Chagin 	register_t s;
79256fba8e6SDmitry Chagin 	register_t msg;
79356fba8e6SDmitry Chagin 	register_t len;
79456fba8e6SDmitry Chagin 	register_t flags;
795c21dee17SSøren Schmidt };
796c21dee17SSøren Schmidt 
79701e0ffbaSAlexander Leidinger static int
798b40ce416SJulian Elischer linux_send(struct thread *td, struct linux_send_args *args)
799c21dee17SSøren Schmidt {
80087d72a8fSPoul-Henning Kamp 	struct sendto_args /* {
801c21dee17SSøren Schmidt 		int s;
802c21dee17SSøren Schmidt 		caddr_t buf;
803044af7c3SJonathan Mini 		int len;
804c21dee17SSøren Schmidt 		int flags;
80587d72a8fSPoul-Henning Kamp 		caddr_t to;
80687d72a8fSPoul-Henning Kamp 		int tolen;
807ef04503dSPeter Wemm 	} */ bsd_args;
808aa288712SDmitry Chagin 	struct file *fp;
809aa288712SDmitry Chagin 	int error, fflag;
810c21dee17SSøren Schmidt 
811745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
812745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
813745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
814745aaef5SKonstantin Belousov 	bsd_args.flags = args->flags;
81587d72a8fSPoul-Henning Kamp 	bsd_args.to = NULL;
81687d72a8fSPoul-Henning Kamp 	bsd_args.tolen = 0;
817aa288712SDmitry Chagin 	error = sys_sendto(td, &bsd_args);
818aa288712SDmitry Chagin 	if (error == ENOTCONN) {
819aa288712SDmitry Chagin 		/*
820aa288712SDmitry Chagin 		 * Linux doesn't return ENOTCONN for non-blocking sockets.
821aa288712SDmitry Chagin 		 * Instead it returns the EAGAIN.
822aa288712SDmitry Chagin 		 */
823aa288712SDmitry Chagin 		error = getsock_cap(td, args->s, &cap_send_rights, &fp,
824aa288712SDmitry Chagin 		    &fflag, NULL);
825aa288712SDmitry Chagin 		if (error == 0) {
826aa288712SDmitry Chagin 			if (fflag & FNONBLOCK)
827aa288712SDmitry Chagin 				error = EAGAIN;
828aa288712SDmitry Chagin 			fdrop(fp, td);
829aa288712SDmitry Chagin 		}
830aa288712SDmitry Chagin 	}
831aa288712SDmitry Chagin 	return (error);
832c21dee17SSøren Schmidt }
833c21dee17SSøren Schmidt 
83401e0ffbaSAlexander Leidinger struct linux_recv_args {
83556fba8e6SDmitry Chagin 	register_t s;
83656fba8e6SDmitry Chagin 	register_t msg;
83756fba8e6SDmitry Chagin 	register_t len;
83856fba8e6SDmitry Chagin 	register_t flags;
839c21dee17SSøren Schmidt };
840c21dee17SSøren Schmidt 
84101e0ffbaSAlexander Leidinger static int
842b40ce416SJulian Elischer linux_recv(struct thread *td, struct linux_recv_args *args)
843c21dee17SSøren Schmidt {
84487d72a8fSPoul-Henning Kamp 	struct recvfrom_args /* {
845c21dee17SSøren Schmidt 		int s;
846c21dee17SSøren Schmidt 		caddr_t buf;
847c21dee17SSøren Schmidt 		int len;
848c21dee17SSøren Schmidt 		int flags;
84987d72a8fSPoul-Henning Kamp 		struct sockaddr *from;
85087d72a8fSPoul-Henning Kamp 		socklen_t fromlenaddr;
851ef04503dSPeter Wemm 	} */ bsd_args;
852c21dee17SSøren Schmidt 
853745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
854745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
855745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
8563980a435SDmitry Chagin 	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
85787d72a8fSPoul-Henning Kamp 	bsd_args.from = NULL;
85887d72a8fSPoul-Henning Kamp 	bsd_args.fromlenaddr = 0;
8598451d0ddSKip Macy 	return (sys_recvfrom(td, &bsd_args));
860c21dee17SSøren Schmidt }
861a12b9b3dSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
862c21dee17SSøren Schmidt 
863a12b9b3dSDmitry Chagin int
864b40ce416SJulian Elischer linux_sendto(struct thread *td, struct linux_sendto_args *args)
865c21dee17SSøren Schmidt {
8665a8a13e0SDavid Malone 	struct msghdr msg;
8675a8a13e0SDavid Malone 	struct iovec aiov;
868c21dee17SSøren Schmidt 
869745aaef5SKonstantin Belousov 	if (linux_check_hdrincl(td, args->s) == 0)
870f2477ae1SMike Smith 		/* IP_HDRINCL set, tweak the packet before sending */
871745aaef5SKonstantin Belousov 		return (linux_sendto_hdrincl(td, args));
872f2477ae1SMike Smith 
873745aaef5SKonstantin Belousov 	msg.msg_name = PTRIN(args->to);
874745aaef5SKonstantin Belousov 	msg.msg_namelen = args->tolen;
8755a8a13e0SDavid Malone 	msg.msg_iov = &aiov;
8765a8a13e0SDavid Malone 	msg.msg_iovlen = 1;
8775a8a13e0SDavid Malone 	msg.msg_control = NULL;
8785a8a13e0SDavid Malone 	msg.msg_flags = 0;
879745aaef5SKonstantin Belousov 	aiov.iov_base = PTRIN(args->msg);
880745aaef5SKonstantin Belousov 	aiov.iov_len = args->len;
881e667ee63SDmitry Chagin 	return (linux_sendit(td, args->s, &msg, args->flags, NULL,
882e667ee63SDmitry Chagin 	    UIO_USERSPACE));
883c21dee17SSøren Schmidt }
884c21dee17SSøren Schmidt 
885a12b9b3dSDmitry Chagin int
886b40ce416SJulian Elischer linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
887c21dee17SSøren Schmidt {
888d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
889d5368bf3SDmitry Chagin 	struct sockaddr *sa;
8909599b0ecSDmitry Chagin 	struct msghdr msg;
8919599b0ecSDmitry Chagin 	struct iovec aiov;
8923a49978fSDmitry Chagin 	int error, fromlen;
893c21dee17SSøren Schmidt 
8949599b0ecSDmitry Chagin 	if (PTRIN(args->fromlen) != NULL) {
8953a49978fSDmitry Chagin 		error = copyin(PTRIN(args->fromlen), &fromlen,
8963a49978fSDmitry Chagin 		    sizeof(fromlen));
8979599b0ecSDmitry Chagin 		if (error != 0)
8983f3a4815SMarcel Moolenaar 			return (error);
8993a49978fSDmitry Chagin 		if (fromlen < 0)
9003a49978fSDmitry Chagin 			return (EINVAL);
901d5368bf3SDmitry Chagin 		sa = malloc(fromlen, M_SONAME, M_WAITOK);
902d5368bf3SDmitry Chagin 	} else {
903d5368bf3SDmitry Chagin 		fromlen = 0;
904d5368bf3SDmitry Chagin 		sa = NULL;
905d5368bf3SDmitry Chagin 	}
9069599b0ecSDmitry Chagin 
907d5368bf3SDmitry Chagin 	msg.msg_name = sa;
908d5368bf3SDmitry Chagin 	msg.msg_namelen = fromlen;
9099599b0ecSDmitry Chagin 	msg.msg_iov = &aiov;
9109599b0ecSDmitry Chagin 	msg.msg_iovlen = 1;
9119599b0ecSDmitry Chagin 	aiov.iov_base = PTRIN(args->buf);
9129599b0ecSDmitry Chagin 	aiov.iov_len = args->len;
9139599b0ecSDmitry Chagin 	msg.msg_control = 0;
9149599b0ecSDmitry Chagin 	msg.msg_flags = linux_to_bsd_msg_flags(args->flags);
9159599b0ecSDmitry Chagin 
916d5368bf3SDmitry Chagin 	error = kern_recvit(td, args->s, &msg, UIO_SYSSPACE, NULL);
9179599b0ecSDmitry Chagin 	if (error != 0)
918dcd62418SDmitry Chagin 		goto out;
9199599b0ecSDmitry Chagin 
9209599b0ecSDmitry Chagin 	if (PTRIN(args->from) != NULL) {
921d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, msg.msg_namelen);
922d5368bf3SDmitry Chagin 		if (error == 0)
923d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->from),
924d5368bf3SDmitry Chagin 			    msg.msg_namelen);
925d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
926ca26842eSHajimu UMEMOTO 	}
9279599b0ecSDmitry Chagin 
928d5368bf3SDmitry Chagin 	if (error == 0 && PTRIN(args->fromlen) != NULL)
9299599b0ecSDmitry Chagin 		error = copyout(&msg.msg_namelen, PTRIN(args->fromlen),
9309599b0ecSDmitry Chagin 		    sizeof(msg.msg_namelen));
931dcd62418SDmitry Chagin out:
932d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
9339599b0ecSDmitry Chagin 	return (error);
934ca26842eSHajimu UMEMOTO }
935ca26842eSHajimu UMEMOTO 
936e1ff74c0SDmitry Chagin static int
937e1ff74c0SDmitry Chagin linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
938e1ff74c0SDmitry Chagin     l_uint flags)
939ca26842eSHajimu UMEMOTO {
94074f5d680SKonstantin Belousov 	struct cmsghdr *cmsg;
94174f5d680SKonstantin Belousov 	struct mbuf *control;
942ca26842eSHajimu UMEMOTO 	struct msghdr msg;
94374f5d680SKonstantin Belousov 	struct l_cmsghdr linux_cmsg;
94474f5d680SKonstantin Belousov 	struct l_cmsghdr *ptr_cmsg;
94574f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
946552afd9cSPoul-Henning Kamp 	struct iovec *iov;
94774f5d680SKonstantin Belousov 	socklen_t datalen;
948605da56bSAndriy Gapon 	struct sockaddr *sa;
9491410bfe1SDmitry Chagin 	struct socket *so;
950605da56bSAndriy Gapon 	sa_family_t sa_family;
9511410bfe1SDmitry Chagin 	struct file *fp;
95274f5d680SKonstantin Belousov 	void *data;
953e3b385fcSTijl Coosemans 	l_size_t len;
9547df0e7beSTijl Coosemans 	l_size_t clen;
9551410bfe1SDmitry Chagin 	int error, fflag;
956ca26842eSHajimu UMEMOTO 
957e1ff74c0SDmitry Chagin 	error = copyin(msghdr, &linux_msg, sizeof(linux_msg));
958e1ff74c0SDmitry Chagin 	if (error != 0)
95974f5d680SKonstantin Belousov 		return (error);
960d72a6158SRobert Watson 
961d72a6158SRobert Watson 	/*
962d72a6158SRobert Watson 	 * Some Linux applications (ping) define a non-NULL control data
963d72a6158SRobert Watson 	 * pointer, but a msg_controllen of 0, which is not allowed in the
964d72a6158SRobert Watson 	 * FreeBSD system call interface.  NULL the msg_control pointer in
965d72a6158SRobert Watson 	 * order to handle this case.  This should be checked, but allows the
966d72a6158SRobert Watson 	 * Linux ping to work.
967d72a6158SRobert Watson 	 */
968605da56bSAndriy Gapon 	if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0)
969605da56bSAndriy Gapon 		linux_msg.msg_control = PTROUT(NULL);
970605da56bSAndriy Gapon 
971605da56bSAndriy Gapon 	error = linux_to_bsd_msghdr(&msg, &linux_msg);
972e1ff74c0SDmitry Chagin 	if (error != 0)
973605da56bSAndriy Gapon 		return (error);
97474f5d680SKonstantin Belousov 
97574f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
97674f5d680SKonstantin Belousov 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
97774f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
97874f5d680SKonstantin Belousov #else
979552afd9cSPoul-Henning Kamp 	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
98074f5d680SKonstantin Belousov #endif
981e1ff74c0SDmitry Chagin 	if (error != 0)
982552afd9cSPoul-Henning Kamp 		return (error);
98374f5d680SKonstantin Belousov 
984605da56bSAndriy Gapon 	control = NULL;
985605da56bSAndriy Gapon 
986e1ff74c0SDmitry Chagin 	error = kern_getsockname(td, s, &sa, &datalen);
987e1ff74c0SDmitry Chagin 	if (error != 0)
988605da56bSAndriy Gapon 		goto bad;
989605da56bSAndriy Gapon 	sa_family = sa->sa_family;
990605da56bSAndriy Gapon 	free(sa, M_SONAME);
991605da56bSAndriy Gapon 
9921410bfe1SDmitry Chagin 	if (flags & LINUX_MSG_OOB) {
9931410bfe1SDmitry Chagin 		error = EOPNOTSUPP;
9941410bfe1SDmitry Chagin 		if (sa_family == AF_UNIX)
9951410bfe1SDmitry Chagin 			goto bad;
9961410bfe1SDmitry Chagin 
9971410bfe1SDmitry Chagin 		error = getsock_cap(td, s, &cap_send_rights, &fp,
9981410bfe1SDmitry Chagin 		    &fflag, NULL);
9991410bfe1SDmitry Chagin 		if (error != 0)
10001410bfe1SDmitry Chagin 			goto bad;
10011410bfe1SDmitry Chagin 		so = fp->f_data;
10021410bfe1SDmitry Chagin 		if (so->so_type != SOCK_STREAM)
10031410bfe1SDmitry Chagin 			error = EOPNOTSUPP;
10041410bfe1SDmitry Chagin 		fdrop(fp, td);
10051410bfe1SDmitry Chagin 		if (error != 0)
10061410bfe1SDmitry Chagin 			goto bad;
10071410bfe1SDmitry Chagin 	}
10081410bfe1SDmitry Chagin 
10091410bfe1SDmitry Chagin 	if (linux_msg.msg_controllen >= sizeof(struct l_cmsghdr)) {
10101410bfe1SDmitry Chagin 
101174f5d680SKonstantin Belousov 		error = ENOBUFS;
1012eb1b1807SGleb Smirnoff 		control = m_get(M_WAITOK, MT_CONTROL);
1013e3b385fcSTijl Coosemans 		MCLGET(control, M_WAITOK);
1014e3b385fcSTijl Coosemans 		data = mtod(control, void *);
1015e3b385fcSTijl Coosemans 		datalen = 0;
101674f5d680SKonstantin Belousov 
10177df0e7beSTijl Coosemans 		ptr_cmsg = PTRIN(linux_msg.msg_control);
10187df0e7beSTijl Coosemans 		clen = linux_msg.msg_controllen;
101974f5d680SKonstantin Belousov 		do {
102074f5d680SKonstantin Belousov 			error = copyin(ptr_cmsg, &linux_cmsg,
102174f5d680SKonstantin Belousov 			    sizeof(struct l_cmsghdr));
1022e1ff74c0SDmitry Chagin 			if (error != 0)
102374f5d680SKonstantin Belousov 				goto bad;
102474f5d680SKonstantin Belousov 
102574f5d680SKonstantin Belousov 			error = EINVAL;
10267df0e7beSTijl Coosemans 			if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr) ||
10277df0e7beSTijl Coosemans 			    linux_cmsg.cmsg_len > clen)
102874f5d680SKonstantin Belousov 				goto bad;
102974f5d680SKonstantin Belousov 
1030e3b385fcSTijl Coosemans 			if (datalen + CMSG_HDRSZ > MCLBYTES)
1031e3b385fcSTijl Coosemans 				goto bad;
1032e3b385fcSTijl Coosemans 
103374f5d680SKonstantin Belousov 			/*
1034605da56bSAndriy Gapon 			 * Now we support only SCM_RIGHTS and SCM_CRED,
1035605da56bSAndriy Gapon 			 * so return EINVAL in any other cmsg_type
103674f5d680SKonstantin Belousov 			 */
1037e3b385fcSTijl Coosemans 			cmsg = data;
1038605da56bSAndriy Gapon 			cmsg->cmsg_type =
1039605da56bSAndriy Gapon 			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
104074f5d680SKonstantin Belousov 			cmsg->cmsg_level =
104174f5d680SKonstantin Belousov 			    linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
1042605da56bSAndriy Gapon 			if (cmsg->cmsg_type == -1
1043605da56bSAndriy Gapon 			    || cmsg->cmsg_level != SOL_SOCKET)
1044605da56bSAndriy Gapon 				goto bad;
104574f5d680SKonstantin Belousov 
1046605da56bSAndriy Gapon 			/*
1047605da56bSAndriy Gapon 			 * Some applications (e.g. pulseaudio) attempt to
1048605da56bSAndriy Gapon 			 * send ancillary data even if the underlying protocol
1049605da56bSAndriy Gapon 			 * doesn't support it which is not allowed in the
1050605da56bSAndriy Gapon 			 * FreeBSD system call interface.
1051605da56bSAndriy Gapon 			 */
1052605da56bSAndriy Gapon 			if (sa_family != AF_UNIX)
1053605da56bSAndriy Gapon 				continue;
1054605da56bSAndriy Gapon 
1055e3b385fcSTijl Coosemans 			if (cmsg->cmsg_type == SCM_CREDS) {
1056e3b385fcSTijl Coosemans 				len = sizeof(struct cmsgcred);
1057e3b385fcSTijl Coosemans 				if (datalen + CMSG_SPACE(len) > MCLBYTES)
1058e3b385fcSTijl Coosemans 					goto bad;
1059605da56bSAndriy Gapon 
1060605da56bSAndriy Gapon 				/*
1061605da56bSAndriy Gapon 				 * The lower levels will fill in the structure
1062605da56bSAndriy Gapon 				 */
1063e3b385fcSTijl Coosemans 				memset(CMSG_DATA(data), 0, len);
1064e3b385fcSTijl Coosemans 			} else {
1065e3b385fcSTijl Coosemans 				len = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
1066e3b385fcSTijl Coosemans 				if (datalen + CMSG_SPACE(len) < datalen ||
1067e3b385fcSTijl Coosemans 				    datalen + CMSG_SPACE(len) > MCLBYTES)
1068e3b385fcSTijl Coosemans 					goto bad;
1069e3b385fcSTijl Coosemans 
1070e3b385fcSTijl Coosemans 				error = copyin(LINUX_CMSG_DATA(ptr_cmsg),
1071e3b385fcSTijl Coosemans 				    CMSG_DATA(data), len);
1072e3b385fcSTijl Coosemans 				if (error != 0)
1073e3b385fcSTijl Coosemans 					goto bad;
1074605da56bSAndriy Gapon 			}
1075605da56bSAndriy Gapon 
1076e3b385fcSTijl Coosemans 			cmsg->cmsg_len = CMSG_LEN(len);
1077e3b385fcSTijl Coosemans 			data = (char *)data + CMSG_SPACE(len);
1078e3b385fcSTijl Coosemans 			datalen += CMSG_SPACE(len);
10797df0e7beSTijl Coosemans 
10807df0e7beSTijl Coosemans 			if (clen <= LINUX_CMSG_ALIGN(linux_cmsg.cmsg_len))
10817df0e7beSTijl Coosemans 				break;
10827df0e7beSTijl Coosemans 
10837df0e7beSTijl Coosemans 			clen -= LINUX_CMSG_ALIGN(linux_cmsg.cmsg_len);
10847df0e7beSTijl Coosemans 			ptr_cmsg = (struct l_cmsghdr *)((char *)ptr_cmsg +
10857df0e7beSTijl Coosemans 			    LINUX_CMSG_ALIGN(linux_cmsg.cmsg_len));
10867df0e7beSTijl Coosemans 		} while(clen >= sizeof(struct l_cmsghdr));
1087605da56bSAndriy Gapon 
1088e3b385fcSTijl Coosemans 		control->m_len = datalen;
1089e3b385fcSTijl Coosemans 		if (datalen == 0) {
1090605da56bSAndriy Gapon 			m_freem(control);
109174f5d680SKonstantin Belousov 			control = NULL;
1092605da56bSAndriy Gapon 		}
109374f5d680SKonstantin Belousov 	}
109474f5d680SKonstantin Belousov 
10955a8a13e0SDavid Malone 	msg.msg_iov = iov;
10965a8a13e0SDavid Malone 	msg.msg_flags = 0;
1097e1ff74c0SDmitry Chagin 	error = linux_sendit(td, s, &msg, flags, control, UIO_USERSPACE);
109867968b35SDmitry Chagin 	control = NULL;
109974f5d680SKonstantin Belousov 
110074f5d680SKonstantin Belousov bad:
11014f65e9cfSDmitry Chagin 	m_freem(control);
1102552afd9cSPoul-Henning Kamp 	free(iov, M_IOV);
1103ca26842eSHajimu UMEMOTO 	return (error);
1104c21dee17SSøren Schmidt }
1105c21dee17SSøren Schmidt 
1106a12b9b3dSDmitry Chagin int
1107e1ff74c0SDmitry Chagin linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
1108e1ff74c0SDmitry Chagin {
1109e1ff74c0SDmitry Chagin 
1110e1ff74c0SDmitry Chagin 	return (linux_sendmsg_common(td, args->s, PTRIN(args->msg),
1111e1ff74c0SDmitry Chagin 	    args->flags));
1112e1ff74c0SDmitry Chagin }
1113e1ff74c0SDmitry Chagin 
1114e1ff74c0SDmitry Chagin int
1115e1ff74c0SDmitry Chagin linux_sendmmsg(struct thread *td, struct linux_sendmmsg_args *args)
1116e1ff74c0SDmitry Chagin {
1117e1ff74c0SDmitry Chagin 	struct l_mmsghdr *msg;
1118e1ff74c0SDmitry Chagin 	l_uint retval;
1119e1ff74c0SDmitry Chagin 	int error, datagrams;
1120e1ff74c0SDmitry Chagin 
1121e1ff74c0SDmitry Chagin 	if (args->vlen > UIO_MAXIOV)
1122e1ff74c0SDmitry Chagin 		args->vlen = UIO_MAXIOV;
1123e1ff74c0SDmitry Chagin 
1124e1ff74c0SDmitry Chagin 	msg = PTRIN(args->msg);
1125e1ff74c0SDmitry Chagin 	datagrams = 0;
1126e1ff74c0SDmitry Chagin 	while (datagrams < args->vlen) {
1127e1ff74c0SDmitry Chagin 		error = linux_sendmsg_common(td, args->s, &msg->msg_hdr,
1128e1ff74c0SDmitry Chagin 		    args->flags);
1129e1ff74c0SDmitry Chagin 		if (error != 0)
1130e1ff74c0SDmitry Chagin 			break;
1131e1ff74c0SDmitry Chagin 
1132e1ff74c0SDmitry Chagin 		retval = td->td_retval[0];
1133e1ff74c0SDmitry Chagin 		error = copyout(&retval, &msg->msg_len, sizeof(msg->msg_len));
1134e1ff74c0SDmitry Chagin 		if (error != 0)
1135e1ff74c0SDmitry Chagin 			break;
1136e1ff74c0SDmitry Chagin 		++msg;
1137e1ff74c0SDmitry Chagin 		++datagrams;
1138e1ff74c0SDmitry Chagin 	}
1139e1ff74c0SDmitry Chagin 	if (error == 0)
1140e1ff74c0SDmitry Chagin 		td->td_retval[0] = datagrams;
1141e1ff74c0SDmitry Chagin 	return (error);
1142e1ff74c0SDmitry Chagin }
1143e1ff74c0SDmitry Chagin 
1144e1ff74c0SDmitry Chagin static int
1145e1ff74c0SDmitry Chagin linux_recvmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
1146e1ff74c0SDmitry Chagin     l_uint flags, struct msghdr *msg)
114740dbba57SAssar Westerlund {
114874f5d680SKonstantin Belousov 	struct cmsghdr *cm;
1149605da56bSAndriy Gapon 	struct cmsgcred *cmcred;
115074f5d680SKonstantin Belousov 	struct l_cmsghdr *linux_cmsg = NULL;
1151605da56bSAndriy Gapon 	struct l_ucred linux_ucred;
1152c7902fbeSMark Johnston 	socklen_t datalen, maxlen, outlen;
115374f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
115474f5d680SKonstantin Belousov 	struct iovec *iov, *uiov;
115574f5d680SKonstantin Belousov 	struct mbuf *control = NULL;
115674f5d680SKonstantin Belousov 	struct mbuf **controlp;
1157bbf392d5SDmitry Chagin 	struct timeval *ftmvl;
1158d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
1159d5368bf3SDmitry Chagin 	struct sockaddr *sa;
1160bbf392d5SDmitry Chagin 	l_timeval ltmvl;
116174f5d680SKonstantin Belousov 	caddr_t outbuf;
116274f5d680SKonstantin Belousov 	void *data;
11633a72bf04SDmitry Chagin 	int error, i, fd, fds, *fdp;
116440dbba57SAssar Westerlund 
1165e1ff74c0SDmitry Chagin 	error = copyin(msghdr, &linux_msg, sizeof(linux_msg));
1166e1ff74c0SDmitry Chagin 	if (error != 0)
1167ca26842eSHajimu UMEMOTO 		return (error);
1168ca26842eSHajimu UMEMOTO 
1169e1ff74c0SDmitry Chagin 	error = linux_to_bsd_msghdr(msg, &linux_msg);
1170e1ff74c0SDmitry Chagin 	if (error != 0)
117174f5d680SKonstantin Belousov 		return (error);
117274f5d680SKonstantin Belousov 
117374f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
1174e1ff74c0SDmitry Chagin 	error = linux32_copyiniov(PTRIN(msg->msg_iov), msg->msg_iovlen,
117574f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
117674f5d680SKonstantin Belousov #else
1177e1ff74c0SDmitry Chagin 	error = copyiniov(msg->msg_iov, msg->msg_iovlen, &iov, EMSGSIZE);
117874f5d680SKonstantin Belousov #endif
1179e1ff74c0SDmitry Chagin 	if (error != 0)
118074f5d680SKonstantin Belousov 		return (error);
118174f5d680SKonstantin Belousov 
1182e1ff74c0SDmitry Chagin 	if (msg->msg_name) {
1183d5368bf3SDmitry Chagin 		sa = malloc(msg->msg_namelen, M_SONAME, M_WAITOK);
1184d5368bf3SDmitry Chagin 		msg->msg_name = sa;
11858128cfc5SDmitry Chagin 	} else
11868128cfc5SDmitry Chagin 		sa = NULL;
118784b11cd8SMitsuru IWASAKI 
1188e1ff74c0SDmitry Chagin 	uiov = msg->msg_iov;
1189e1ff74c0SDmitry Chagin 	msg->msg_iov = iov;
1190e1ff74c0SDmitry Chagin 	controlp = (msg->msg_control != NULL) ? &control : NULL;
1191d5368bf3SDmitry Chagin 	error = kern_recvit(td, s, msg, UIO_SYSSPACE, controlp);
1192e1ff74c0SDmitry Chagin 	msg->msg_iov = uiov;
1193e1ff74c0SDmitry Chagin 	if (error != 0)
119474f5d680SKonstantin Belousov 		goto bad;
119574f5d680SKonstantin Belousov 
119657cb29a7SDmitry Chagin 	if (msg->msg_name) {
1197d5368bf3SDmitry Chagin 		msg->msg_name = PTRIN(linux_msg.msg_name);
1198d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, msg->msg_namelen);
1199d5368bf3SDmitry Chagin 		if (error == 0)
1200d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(msg->msg_name),
1201d5368bf3SDmitry Chagin 			    msg->msg_namelen);
1202d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
1203d5368bf3SDmitry Chagin 		if (error != 0)
1204d5368bf3SDmitry Chagin 			goto bad;
1205d5368bf3SDmitry Chagin 	}
1206d5368bf3SDmitry Chagin 
1207e1ff74c0SDmitry Chagin 	error = bsd_to_linux_msghdr(msg, &linux_msg);
1208e1ff74c0SDmitry Chagin 	if (error != 0)
120974f5d680SKonstantin Belousov 		goto bad;
121074f5d680SKonstantin Belousov 
1211c7902fbeSMark Johnston 	maxlen = linux_msg.msg_controllen;
1212c7902fbeSMark Johnston 	linux_msg.msg_controllen = 0;
1213605da56bSAndriy Gapon 	if (control) {
1214e0d3ea8cSDmitry Chagin 		linux_cmsg = malloc(L_CMSG_HDRSZ, M_LINUX, M_WAITOK | M_ZERO);
1215605da56bSAndriy Gapon 
1216e1ff74c0SDmitry Chagin 		msg->msg_control = mtod(control, struct cmsghdr *);
1217e1ff74c0SDmitry Chagin 		msg->msg_controllen = control->m_len;
1218605da56bSAndriy Gapon 
1219e1ff74c0SDmitry Chagin 		cm = CMSG_FIRSTHDR(msg);
1220c7902fbeSMark Johnston 		outbuf = PTRIN(linux_msg.msg_control);
1221c7902fbeSMark Johnston 		outlen = 0;
122274f5d680SKonstantin Belousov 		while (cm != NULL) {
1223605da56bSAndriy Gapon 			linux_cmsg->cmsg_type =
1224605da56bSAndriy Gapon 			    bsd_to_linux_cmsg_type(cm->cmsg_type);
1225605da56bSAndriy Gapon 			linux_cmsg->cmsg_level =
1226605da56bSAndriy Gapon 			    bsd_to_linux_sockopt_level(cm->cmsg_level);
1227c7902fbeSMark Johnston 			if (linux_cmsg->cmsg_type == -1 ||
1228c7902fbeSMark Johnston 			    cm->cmsg_level != SOL_SOCKET) {
122974f5d680SKonstantin Belousov 				error = EINVAL;
123074f5d680SKonstantin Belousov 				goto bad;
123174f5d680SKonstantin Belousov 			}
1232605da56bSAndriy Gapon 
123374f5d680SKonstantin Belousov 			data = CMSG_DATA(cm);
123474f5d680SKonstantin Belousov 			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
123574f5d680SKonstantin Belousov 
1236c7902fbeSMark Johnston 			switch (cm->cmsg_type) {
1237605da56bSAndriy Gapon 			case SCM_RIGHTS:
1238e1ff74c0SDmitry Chagin 				if (flags & LINUX_MSG_CMSG_CLOEXEC) {
1239605da56bSAndriy Gapon 					fds = datalen / sizeof(int);
1240605da56bSAndriy Gapon 					fdp = data;
1241605da56bSAndriy Gapon 					for (i = 0; i < fds; i++) {
1242605da56bSAndriy Gapon 						fd = *fdp++;
1243605da56bSAndriy Gapon 						(void)kern_fcntl(td, fd,
1244605da56bSAndriy Gapon 						    F_SETFD, FD_CLOEXEC);
1245605da56bSAndriy Gapon 					}
1246605da56bSAndriy Gapon 				}
1247605da56bSAndriy Gapon 				break;
1248605da56bSAndriy Gapon 
1249605da56bSAndriy Gapon 			case SCM_CREDS:
1250605da56bSAndriy Gapon 				/*
1251605da56bSAndriy Gapon 				 * Currently LOCAL_CREDS is never in
1252605da56bSAndriy Gapon 				 * effect for Linux so no need to worry
1253605da56bSAndriy Gapon 				 * about sockcred
1254605da56bSAndriy Gapon 				 */
1255605da56bSAndriy Gapon 				if (datalen != sizeof(*cmcred)) {
1256605da56bSAndriy Gapon 					error = EMSGSIZE;
1257605da56bSAndriy Gapon 					goto bad;
1258605da56bSAndriy Gapon 				}
1259605da56bSAndriy Gapon 				cmcred = (struct cmsgcred *)data;
1260605da56bSAndriy Gapon 				bzero(&linux_ucred, sizeof(linux_ucred));
1261605da56bSAndriy Gapon 				linux_ucred.pid = cmcred->cmcred_pid;
1262605da56bSAndriy Gapon 				linux_ucred.uid = cmcred->cmcred_uid;
1263605da56bSAndriy Gapon 				linux_ucred.gid = cmcred->cmcred_gid;
1264605da56bSAndriy Gapon 				data = &linux_ucred;
1265605da56bSAndriy Gapon 				datalen = sizeof(linux_ucred);
1266605da56bSAndriy Gapon 				break;
1267bbf392d5SDmitry Chagin 
1268bbf392d5SDmitry Chagin 			case SCM_TIMESTAMP:
1269bbf392d5SDmitry Chagin 				if (datalen != sizeof(struct timeval)) {
1270bbf392d5SDmitry Chagin 					error = EMSGSIZE;
1271bbf392d5SDmitry Chagin 					goto bad;
1272bbf392d5SDmitry Chagin 				}
1273bbf392d5SDmitry Chagin 				ftmvl = (struct timeval *)data;
1274bbf392d5SDmitry Chagin 				ltmvl.tv_sec = ftmvl->tv_sec;
1275bbf392d5SDmitry Chagin 				ltmvl.tv_usec = ftmvl->tv_usec;
1276bbf392d5SDmitry Chagin 				data = &ltmvl;
1277bbf392d5SDmitry Chagin 				datalen = sizeof(ltmvl);
1278bbf392d5SDmitry Chagin 				break;
1279605da56bSAndriy Gapon 			}
1280605da56bSAndriy Gapon 
1281c7902fbeSMark Johnston 			if (outlen + LINUX_CMSG_LEN(datalen) > maxlen) {
128274f5d680SKonstantin Belousov 				if (outlen == 0) {
128374f5d680SKonstantin Belousov 					error = EMSGSIZE;
128474f5d680SKonstantin Belousov 					goto bad;
128574f5d680SKonstantin Belousov 				} else {
1286c7902fbeSMark Johnston 					linux_msg.msg_flags |= LINUX_MSG_CTRUNC;
1287c7902fbeSMark Johnston 					m_dispose_extcontrolm(control);
128874f5d680SKonstantin Belousov 					goto out;
128974f5d680SKonstantin Belousov 				}
129074f5d680SKonstantin Belousov 			}
129174f5d680SKonstantin Belousov 
129274f5d680SKonstantin Belousov 			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
129374f5d680SKonstantin Belousov 
129474f5d680SKonstantin Belousov 			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
1295dddb7e7fSDmitry Chagin 			if (error != 0)
129674f5d680SKonstantin Belousov 				goto bad;
129774f5d680SKonstantin Belousov 			outbuf += L_CMSG_HDRSZ;
129874f5d680SKonstantin Belousov 
129974f5d680SKonstantin Belousov 			error = copyout(data, outbuf, datalen);
1300dddb7e7fSDmitry Chagin 			if (error != 0)
130174f5d680SKonstantin Belousov 				goto bad;
130274f5d680SKonstantin Belousov 
130374f5d680SKonstantin Belousov 			outbuf += LINUX_CMSG_ALIGN(datalen);
130474f5d680SKonstantin Belousov 			outlen += LINUX_CMSG_LEN(datalen);
130574f5d680SKonstantin Belousov 
1306e1ff74c0SDmitry Chagin 			cm = CMSG_NXTHDR(msg, cm);
130774f5d680SKonstantin Belousov 		}
1308c7902fbeSMark Johnston 		linux_msg.msg_controllen = outlen;
130974f5d680SKonstantin Belousov 	}
131074f5d680SKonstantin Belousov 
131174f5d680SKonstantin Belousov out:
1312e1ff74c0SDmitry Chagin 	error = copyout(&linux_msg, msghdr, sizeof(linux_msg));
131374f5d680SKonstantin Belousov 
131474f5d680SKonstantin Belousov bad:
1315c7902fbeSMark Johnston 	if (control != NULL) {
1316c7902fbeSMark Johnston 		if (error != 0)
1317c7902fbeSMark Johnston 			m_dispose_extcontrolm(control);
131874f5d680SKonstantin Belousov 		m_freem(control);
1319c7902fbeSMark Johnston 	}
1320c7902fbeSMark Johnston 	free(iov, M_IOV);
1321e0d3ea8cSDmitry Chagin 	free(linux_cmsg, M_LINUX);
13228128cfc5SDmitry Chagin 	free(sa, M_SONAME);
132374f5d680SKonstantin Belousov 
1324ca26842eSHajimu UMEMOTO 	return (error);
132540dbba57SAssar Westerlund }
132640dbba57SAssar Westerlund 
1327a12b9b3dSDmitry Chagin int
1328e1ff74c0SDmitry Chagin linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
1329e1ff74c0SDmitry Chagin {
1330e1ff74c0SDmitry Chagin 	struct msghdr bsd_msg;
1331e1ff74c0SDmitry Chagin 
1332e1ff74c0SDmitry Chagin 	return (linux_recvmsg_common(td, args->s, PTRIN(args->msg),
1333e1ff74c0SDmitry Chagin 	    args->flags, &bsd_msg));
1334e1ff74c0SDmitry Chagin }
1335e1ff74c0SDmitry Chagin 
1336e1ff74c0SDmitry Chagin int
1337e1ff74c0SDmitry Chagin linux_recvmmsg(struct thread *td, struct linux_recvmmsg_args *args)
1338e1ff74c0SDmitry Chagin {
1339e1ff74c0SDmitry Chagin 	struct l_mmsghdr *msg;
1340e1ff74c0SDmitry Chagin 	struct msghdr bsd_msg;
1341e1ff74c0SDmitry Chagin 	struct l_timespec lts;
1342e1ff74c0SDmitry Chagin 	struct timespec ts, tts;
1343e1ff74c0SDmitry Chagin 	l_uint retval;
1344e1ff74c0SDmitry Chagin 	int error, datagrams;
1345e1ff74c0SDmitry Chagin 
1346e1ff74c0SDmitry Chagin 	if (args->timeout) {
1347e1ff74c0SDmitry Chagin 		error = copyin(args->timeout, &lts, sizeof(struct l_timespec));
1348e1ff74c0SDmitry Chagin 		if (error != 0)
1349e1ff74c0SDmitry Chagin 			return (error);
1350e1ff74c0SDmitry Chagin 		error = linux_to_native_timespec(&ts, &lts);
1351e1ff74c0SDmitry Chagin 		if (error != 0)
1352e1ff74c0SDmitry Chagin 			return (error);
1353e1ff74c0SDmitry Chagin 		getnanotime(&tts);
13546040822cSAlan Somers 		timespecadd(&tts, &ts, &tts);
1355e1ff74c0SDmitry Chagin 	}
1356e1ff74c0SDmitry Chagin 
1357e1ff74c0SDmitry Chagin 	msg = PTRIN(args->msg);
1358e1ff74c0SDmitry Chagin 	datagrams = 0;
1359e1ff74c0SDmitry Chagin 	while (datagrams < args->vlen) {
1360e1ff74c0SDmitry Chagin 		error = linux_recvmsg_common(td, args->s, &msg->msg_hdr,
1361e1ff74c0SDmitry Chagin 		    args->flags & ~LINUX_MSG_WAITFORONE, &bsd_msg);
1362e1ff74c0SDmitry Chagin 		if (error != 0)
1363e1ff74c0SDmitry Chagin 			break;
1364e1ff74c0SDmitry Chagin 
1365e1ff74c0SDmitry Chagin 		retval = td->td_retval[0];
1366e1ff74c0SDmitry Chagin 		error = copyout(&retval, &msg->msg_len, sizeof(msg->msg_len));
1367e1ff74c0SDmitry Chagin 		if (error != 0)
1368e1ff74c0SDmitry Chagin 			break;
1369e1ff74c0SDmitry Chagin 		++msg;
1370e1ff74c0SDmitry Chagin 		++datagrams;
1371e1ff74c0SDmitry Chagin 
1372e1ff74c0SDmitry Chagin 		/*
1373e1ff74c0SDmitry Chagin 		 * MSG_WAITFORONE turns on MSG_DONTWAIT after one packet.
1374e1ff74c0SDmitry Chagin 		 */
1375e1ff74c0SDmitry Chagin 		if (args->flags & LINUX_MSG_WAITFORONE)
1376e1ff74c0SDmitry Chagin 			args->flags |= LINUX_MSG_DONTWAIT;
1377e1ff74c0SDmitry Chagin 
1378e1ff74c0SDmitry Chagin 		/*
1379e1ff74c0SDmitry Chagin 		 * See BUGS section of recvmmsg(2).
1380e1ff74c0SDmitry Chagin 		 */
1381e1ff74c0SDmitry Chagin 		if (args->timeout) {
1382e1ff74c0SDmitry Chagin 			getnanotime(&ts);
13836040822cSAlan Somers 			timespecsub(&ts, &tts, &ts);
1384e1ff74c0SDmitry Chagin 			if (!timespecisset(&ts) || ts.tv_sec > 0)
1385e1ff74c0SDmitry Chagin 				break;
1386e1ff74c0SDmitry Chagin 		}
1387e1ff74c0SDmitry Chagin 		/* Out of band data, return right away. */
1388e1ff74c0SDmitry Chagin 		if (bsd_msg.msg_flags & MSG_OOB)
1389e1ff74c0SDmitry Chagin 			break;
1390e1ff74c0SDmitry Chagin 	}
1391e1ff74c0SDmitry Chagin 	if (error == 0)
1392e1ff74c0SDmitry Chagin 		td->td_retval[0] = datagrams;
1393e1ff74c0SDmitry Chagin 	return (error);
1394e1ff74c0SDmitry Chagin }
1395e1ff74c0SDmitry Chagin 
1396e1ff74c0SDmitry Chagin int
1397b40ce416SJulian Elischer linux_shutdown(struct thread *td, struct linux_shutdown_args *args)
1398c21dee17SSøren Schmidt {
1399c21dee17SSøren Schmidt 
1400d293f35cSEdward Tomasz Napierala 	return (kern_shutdown(td, args->s, args->how));
1401c21dee17SSøren Schmidt }
1402c21dee17SSøren Schmidt 
1403a12b9b3dSDmitry Chagin int
1404b40ce416SJulian Elischer linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
1405c21dee17SSøren Schmidt {
140603cc95d2SDmitry Chagin 	l_timeval linux_tv;
1407d5368bf3SDmitry Chagin 	struct sockaddr *sa;
140803cc95d2SDmitry Chagin 	struct timeval tv;
1409d5368bf3SDmitry Chagin 	socklen_t len;
14109c6eb0f9SEdward Tomasz Napierala 	int error, level, name;
1411c21dee17SSøren Schmidt 
14129c6eb0f9SEdward Tomasz Napierala 	level = linux_to_bsd_sockopt_level(args->level);
14139c6eb0f9SEdward Tomasz Napierala 	switch (level) {
1414c21dee17SSøren Schmidt 	case SOL_SOCKET:
1415745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
141603cc95d2SDmitry Chagin 		switch (name) {
141703cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
141803cc95d2SDmitry Chagin 			/* FALLTHROUGH */
141903cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
142003cc95d2SDmitry Chagin 			error = copyin(PTRIN(args->optval), &linux_tv,
142103cc95d2SDmitry Chagin 			    sizeof(linux_tv));
1422dddb7e7fSDmitry Chagin 			if (error != 0)
142303cc95d2SDmitry Chagin 				return (error);
142403cc95d2SDmitry Chagin 			tv.tv_sec = linux_tv.tv_sec;
142503cc95d2SDmitry Chagin 			tv.tv_usec = linux_tv.tv_usec;
14269c6eb0f9SEdward Tomasz Napierala 			return (kern_setsockopt(td, args->s, level,
142703cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, sizeof(tv)));
142803cc95d2SDmitry Chagin 			/* NOTREACHED */
142903cc95d2SDmitry Chagin 		default:
143003cc95d2SDmitry Chagin 			break;
143103cc95d2SDmitry Chagin 		}
1432c21dee17SSøren Schmidt 		break;
1433c21dee17SSøren Schmidt 	case IPPROTO_IP:
1434da6d8ae6SEdward Tomasz Napierala 		if (args->optname == LINUX_IP_RECVERR &&
1435da6d8ae6SEdward Tomasz Napierala 		    linux_ignore_ip_recverr) {
1436da6d8ae6SEdward Tomasz Napierala 			/*
1437da6d8ae6SEdward Tomasz Napierala 			 * XXX: This is a hack to unbreak DNS resolution
1438da6d8ae6SEdward Tomasz Napierala 			 *	with glibc 2.30 and above.
1439da6d8ae6SEdward Tomasz Napierala 			 */
1440da6d8ae6SEdward Tomasz Napierala 			return (0);
1441da6d8ae6SEdward Tomasz Napierala 		}
1442745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1443c21dee17SSøren Schmidt 		break;
144486a9058bSAndrey V. Elsukov 	case IPPROTO_IPV6:
144586a9058bSAndrey V. Elsukov 		name = linux_to_bsd_ip6_sockopt(args->optname);
144686a9058bSAndrey V. Elsukov 		break;
1447dad3b88aSMike Smith 	case IPPROTO_TCP:
1448fb709557SJohn Baldwin 		name = linux_to_bsd_tcp_sockopt(args->optname);
1449dad3b88aSMike Smith 		break;
1450c21dee17SSøren Schmidt 	default:
14513f3a4815SMarcel Moolenaar 		name = -1;
14523f3a4815SMarcel Moolenaar 		break;
1453c21dee17SSøren Schmidt 	}
1454c21dee17SSøren Schmidt 	if (name == -1)
1455d4b7423fSAlexander Leidinger 		return (ENOPROTOOPT);
14563f3a4815SMarcel Moolenaar 
1457d5368bf3SDmitry Chagin 
1458d5368bf3SDmitry Chagin 	if (name == IPV6_NEXTHOP) {
1459d5368bf3SDmitry Chagin 		len = args->optlen;
1460d5368bf3SDmitry Chagin 		error = linux_to_bsd_sockaddr(PTRIN(args->optval), &sa, &len);
1461d5368bf3SDmitry Chagin 		if (error != 0)
1462d5368bf3SDmitry Chagin 			return (error);
1463d5368bf3SDmitry Chagin 
14649c6eb0f9SEdward Tomasz Napierala 		error = kern_setsockopt(td, args->s, level,
1465d5368bf3SDmitry Chagin 		    name, sa, UIO_SYSSPACE, len);
1466d5368bf3SDmitry Chagin 		free(sa, M_SONAME);
1467d5368bf3SDmitry Chagin 	} else {
14689c6eb0f9SEdward Tomasz Napierala 		error = kern_setsockopt(td, args->s, level,
14699c6eb0f9SEdward Tomasz Napierala 		    name, PTRIN(args->optval), UIO_USERSPACE, args->optlen);
1470d5368bf3SDmitry Chagin 	}
14715c8919adSAlexander Leidinger 
14725c8919adSAlexander Leidinger 	return (error);
1473c21dee17SSøren Schmidt }
1474c21dee17SSøren Schmidt 
1475a12b9b3dSDmitry Chagin int
1476b40ce416SJulian Elischer linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
1477c21dee17SSøren Schmidt {
147803cc95d2SDmitry Chagin 	l_timeval linux_tv;
147903cc95d2SDmitry Chagin 	struct timeval tv;
1480d56e689eSDmitry Chagin 	socklen_t tv_len, xulen, len;
1481d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
1482d5368bf3SDmitry Chagin 	struct sockaddr *sa;
1483d4dd69c4SDmitry Chagin 	struct xucred xu;
1484d4dd69c4SDmitry Chagin 	struct l_ucred lxu;
1485dfd060c0SEdward Tomasz Napierala 	int error, level, name, newval;
1486c21dee17SSøren Schmidt 
1487dfd060c0SEdward Tomasz Napierala 	level = linux_to_bsd_sockopt_level(args->level);
1488dfd060c0SEdward Tomasz Napierala 	switch (level) {
1489c21dee17SSøren Schmidt 	case SOL_SOCKET:
1490745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
149103cc95d2SDmitry Chagin 		switch (name) {
149203cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
149303cc95d2SDmitry Chagin 			/* FALLTHROUGH */
149403cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
149503cc95d2SDmitry Chagin 			tv_len = sizeof(tv);
1496dfd060c0SEdward Tomasz Napierala 			error = kern_getsockopt(td, args->s, level,
149703cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, &tv_len);
1498dddb7e7fSDmitry Chagin 			if (error != 0)
149903cc95d2SDmitry Chagin 				return (error);
150003cc95d2SDmitry Chagin 			linux_tv.tv_sec = tv.tv_sec;
150103cc95d2SDmitry Chagin 			linux_tv.tv_usec = tv.tv_usec;
150203cc95d2SDmitry Chagin 			return (copyout(&linux_tv, PTRIN(args->optval),
150303cc95d2SDmitry Chagin 			    sizeof(linux_tv)));
150403cc95d2SDmitry Chagin 			/* NOTREACHED */
1505d4dd69c4SDmitry Chagin 		case LOCAL_PEERCRED:
1506cd92d27eSDmitry Chagin 			if (args->optlen < sizeof(lxu))
1507d4dd69c4SDmitry Chagin 				return (EINVAL);
1508bb376a99SMark Johnston 			/*
1509bb376a99SMark Johnston 			 * LOCAL_PEERCRED is not served at the SOL_SOCKET level,
1510bb376a99SMark Johnston 			 * but by the Unix socket's level 0.
1511bb376a99SMark Johnston 			 */
1512dfd060c0SEdward Tomasz Napierala 			level = 0;
1513d4dd69c4SDmitry Chagin 			xulen = sizeof(xu);
1514dfd060c0SEdward Tomasz Napierala 			error = kern_getsockopt(td, args->s, level,
1515d4dd69c4SDmitry Chagin 			    name, &xu, UIO_SYSSPACE, &xulen);
1516dddb7e7fSDmitry Chagin 			if (error != 0)
1517d4dd69c4SDmitry Chagin 				return (error);
1518c5afec6eSDmitry Chagin 			lxu.pid = xu.cr_pid;
1519d4dd69c4SDmitry Chagin 			lxu.uid = xu.cr_uid;
1520d4dd69c4SDmitry Chagin 			lxu.gid = xu.cr_gid;
1521d4dd69c4SDmitry Chagin 			return (copyout(&lxu, PTRIN(args->optval), sizeof(lxu)));
1522d4dd69c4SDmitry Chagin 			/* NOTREACHED */
1523d56e689eSDmitry Chagin 		case SO_ERROR:
1524d56e689eSDmitry Chagin 			len = sizeof(newval);
1525dfd060c0SEdward Tomasz Napierala 			error = kern_getsockopt(td, args->s, level,
1526d56e689eSDmitry Chagin 			    name, &newval, UIO_SYSSPACE, &len);
1527dddb7e7fSDmitry Chagin 			if (error != 0)
1528d56e689eSDmitry Chagin 				return (error);
1529d56e689eSDmitry Chagin 			newval = -SV_ABI_ERRNO(td->td_proc, newval);
1530d56e689eSDmitry Chagin 			return (copyout(&newval, PTRIN(args->optval), len));
1531d56e689eSDmitry Chagin 			/* NOTREACHED */
153203cc95d2SDmitry Chagin 		default:
153303cc95d2SDmitry Chagin 			break;
153403cc95d2SDmitry Chagin 		}
1535c21dee17SSøren Schmidt 		break;
1536c21dee17SSøren Schmidt 	case IPPROTO_IP:
1537745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1538c21dee17SSøren Schmidt 		break;
153986a9058bSAndrey V. Elsukov 	case IPPROTO_IPV6:
154086a9058bSAndrey V. Elsukov 		name = linux_to_bsd_ip6_sockopt(args->optname);
154186a9058bSAndrey V. Elsukov 		break;
1542dad3b88aSMike Smith 	case IPPROTO_TCP:
1543fb709557SJohn Baldwin 		name = linux_to_bsd_tcp_sockopt(args->optname);
1544dad3b88aSMike Smith 		break;
1545c21dee17SSøren Schmidt 	default:
15463f3a4815SMarcel Moolenaar 		name = -1;
15473f3a4815SMarcel Moolenaar 		break;
1548c21dee17SSøren Schmidt 	}
1549c21dee17SSøren Schmidt 	if (name == -1)
15503f3a4815SMarcel Moolenaar 		return (EINVAL);
15513f3a4815SMarcel Moolenaar 
15525c8919adSAlexander Leidinger 	if (name == IPV6_NEXTHOP) {
1553d5368bf3SDmitry Chagin 		error = copyin(PTRIN(args->optlen), &len, sizeof(len));
1554d5368bf3SDmitry Chagin                 if (error != 0)
1555d5368bf3SDmitry Chagin                         return (error);
1556d5368bf3SDmitry Chagin 		sa = malloc(len, M_SONAME, M_WAITOK);
1557d5368bf3SDmitry Chagin 
1558dfd060c0SEdward Tomasz Napierala 		error = kern_getsockopt(td, args->s, level,
1559d5368bf3SDmitry Chagin 		    name, sa, UIO_SYSSPACE, &len);
1560d5368bf3SDmitry Chagin 		if (error != 0)
1561d5368bf3SDmitry Chagin 			goto out;
1562d5368bf3SDmitry Chagin 
1563d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
1564d5368bf3SDmitry Chagin 		if (error == 0)
1565d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->optval), len);
1566d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
1567d5368bf3SDmitry Chagin 		if (error == 0)
1568d5368bf3SDmitry Chagin 			error = copyout(&len, PTRIN(args->optlen),
1569d5368bf3SDmitry Chagin 			    sizeof(len));
1570d5368bf3SDmitry Chagin out:
1571d5368bf3SDmitry Chagin 		free(sa, M_SONAME);
1572d5368bf3SDmitry Chagin 	} else {
1573dfd060c0SEdward Tomasz Napierala 		if (args->optval) {
1574dfd060c0SEdward Tomasz Napierala 			error = copyin(PTRIN(args->optlen), &len, sizeof(len));
1575dfd060c0SEdward Tomasz Napierala 			if (error != 0)
1576dfd060c0SEdward Tomasz Napierala 				return (error);
1577dfd060c0SEdward Tomasz Napierala 		}
1578dfd060c0SEdward Tomasz Napierala 		error = kern_getsockopt(td, args->s, level,
1579dfd060c0SEdward Tomasz Napierala 		    name, PTRIN(args->optval), UIO_USERSPACE, &len);
1580dfd060c0SEdward Tomasz Napierala 		if (error == 0)
1581dfd060c0SEdward Tomasz Napierala 			error = copyout(&len, PTRIN(args->optlen),
1582dfd060c0SEdward Tomasz Napierala 			    sizeof(len));
1583d5368bf3SDmitry Chagin 	}
15845c8919adSAlexander Leidinger 
15855c8919adSAlexander Leidinger 	return (error);
1586c21dee17SSøren Schmidt }
1587c21dee17SSøren Schmidt 
1588*fc7510aeSEd Maste static int
1589*fc7510aeSEd Maste linux_sendfile_common(struct thread *td, l_int out, l_int in,
1590*fc7510aeSEd Maste     l_loff_t *offset, l_size_t count)
1591*fc7510aeSEd Maste {
1592*fc7510aeSEd Maste 	off_t bytes_read;
1593*fc7510aeSEd Maste 	int error;
1594*fc7510aeSEd Maste 	l_loff_t current_offset;
1595*fc7510aeSEd Maste 	struct file *fp;
1596*fc7510aeSEd Maste 
1597*fc7510aeSEd Maste 	AUDIT_ARG_FD(in);
1598*fc7510aeSEd Maste 	error = fget_read(td, in, &cap_pread_rights, &fp);
1599*fc7510aeSEd Maste 	if (error != 0)
1600*fc7510aeSEd Maste 		return (error);
1601*fc7510aeSEd Maste 
1602*fc7510aeSEd Maste 	if (offset != NULL) {
1603*fc7510aeSEd Maste 		current_offset = *offset;
1604*fc7510aeSEd Maste 	} else {
1605*fc7510aeSEd Maste 		error = (fp->f_ops->fo_flags & DFLAG_SEEKABLE) != 0 ?
1606*fc7510aeSEd Maste 		    fo_seek(fp, 0, SEEK_CUR, td) : ESPIPE;
1607*fc7510aeSEd Maste 		if (error != 0)
1608*fc7510aeSEd Maste 			goto drop;
1609*fc7510aeSEd Maste 		current_offset = td->td_uretoff.tdu_off;
1610*fc7510aeSEd Maste 	}
1611*fc7510aeSEd Maste 
1612*fc7510aeSEd Maste 	bytes_read = 0;
1613*fc7510aeSEd Maste 
1614*fc7510aeSEd Maste 	/* Linux cannot have 0 count. */
1615*fc7510aeSEd Maste 	if (count <= 0 || current_offset < 0) {
1616*fc7510aeSEd Maste 		error = EINVAL;
1617*fc7510aeSEd Maste 		goto drop;
1618*fc7510aeSEd Maste 	}
1619*fc7510aeSEd Maste 
1620*fc7510aeSEd Maste 	error = fo_sendfile(fp, out, NULL, NULL, current_offset, count,
1621*fc7510aeSEd Maste 	    &bytes_read, 0, td);
1622*fc7510aeSEd Maste 	if (error != 0)
1623*fc7510aeSEd Maste 		goto drop;
1624*fc7510aeSEd Maste 	current_offset += bytes_read;
1625*fc7510aeSEd Maste 
1626*fc7510aeSEd Maste 	if (offset != NULL) {
1627*fc7510aeSEd Maste 		*offset = current_offset;
1628*fc7510aeSEd Maste 	} else {
1629*fc7510aeSEd Maste 		error = fo_seek(fp, current_offset, SEEK_SET, td);
1630*fc7510aeSEd Maste 		if (error != 0)
1631*fc7510aeSEd Maste 			goto drop;
1632*fc7510aeSEd Maste 	}
1633*fc7510aeSEd Maste 
1634*fc7510aeSEd Maste 	td->td_retval[0] = (ssize_t)bytes_read;
1635*fc7510aeSEd Maste drop:
1636*fc7510aeSEd Maste 	fdrop(fp, td);
1637*fc7510aeSEd Maste 	return (error);
1638*fc7510aeSEd Maste }
1639*fc7510aeSEd Maste 
1640*fc7510aeSEd Maste int
1641*fc7510aeSEd Maste linux_sendfile(struct thread *td, struct linux_sendfile_args *arg)
1642*fc7510aeSEd Maste {
1643*fc7510aeSEd Maste 	/*
1644*fc7510aeSEd Maste 	 * Differences between FreeBSD and Linux sendfile:
1645*fc7510aeSEd Maste 	 * - Linux doesn't send anything when count is 0 (FreeBSD uses 0 to
1646*fc7510aeSEd Maste 	 *   mean send the whole file.)  In linux_sendfile given fds are still
1647*fc7510aeSEd Maste 	 *   checked for validity when the count is 0.
1648*fc7510aeSEd Maste 	 * - Linux can send to any fd whereas FreeBSD only supports sockets.
1649*fc7510aeSEd Maste 	 *   The same restriction follows for linux_sendfile.
1650*fc7510aeSEd Maste 	 * - Linux doesn't have an equivalent for FreeBSD's flags and sf_hdtr.
1651*fc7510aeSEd Maste 	 * - Linux takes an offset pointer and updates it to the read location.
1652*fc7510aeSEd Maste 	 *   FreeBSD takes in an offset and a 'bytes read' parameter which is
1653*fc7510aeSEd Maste 	 *   only filled if it isn't NULL.  We use this parameter to update the
1654*fc7510aeSEd Maste 	 *   offset pointer if it exists.
1655*fc7510aeSEd Maste 	 * - Linux sendfile returns bytes read on success while FreeBSD
1656*fc7510aeSEd Maste 	 *   returns 0.  We use the 'bytes read' parameter to get this value.
1657*fc7510aeSEd Maste 	 */
1658*fc7510aeSEd Maste 
1659*fc7510aeSEd Maste 	l_loff_t offset64;
1660*fc7510aeSEd Maste 	l_long offset;
1661*fc7510aeSEd Maste 	int ret;
1662*fc7510aeSEd Maste 	int error;
1663*fc7510aeSEd Maste 
1664*fc7510aeSEd Maste 	if (arg->offset != NULL) {
1665*fc7510aeSEd Maste 		error = copyin(arg->offset, &offset, sizeof(offset));
1666*fc7510aeSEd Maste 		if (error != 0)
1667*fc7510aeSEd Maste 			return (error);
1668*fc7510aeSEd Maste 		offset64 = (l_loff_t)offset;
1669*fc7510aeSEd Maste 	}
1670*fc7510aeSEd Maste 
1671*fc7510aeSEd Maste 	ret = linux_sendfile_common(td, arg->out, arg->in,
1672*fc7510aeSEd Maste 	    arg->offset != NULL ? &offset64 : NULL, arg->count);
1673*fc7510aeSEd Maste 
1674*fc7510aeSEd Maste 	if (arg->offset != NULL) {
1675*fc7510aeSEd Maste #if defined(__i386__) || defined(__arm__) || \
1676*fc7510aeSEd Maste     (defined(__amd64__) && defined(COMPAT_LINUX32))
1677*fc7510aeSEd Maste 		if (offset64 > INT32_MAX)
1678*fc7510aeSEd Maste 			return (EOVERFLOW);
1679*fc7510aeSEd Maste #endif
1680*fc7510aeSEd Maste 		offset = (l_long)offset64;
1681*fc7510aeSEd Maste 		error = copyout(&offset, arg->offset, sizeof(offset));
1682*fc7510aeSEd Maste 		if (error != 0)
1683*fc7510aeSEd Maste 			return (error);
1684*fc7510aeSEd Maste 	}
1685*fc7510aeSEd Maste 
1686*fc7510aeSEd Maste 	return (ret);
1687*fc7510aeSEd Maste }
1688*fc7510aeSEd Maste 
1689*fc7510aeSEd Maste #if defined(__i386__) || defined(__arm__) || \
1690*fc7510aeSEd Maste     (defined(__amd64__) && defined(COMPAT_LINUX32))
1691*fc7510aeSEd Maste 
1692*fc7510aeSEd Maste int
1693*fc7510aeSEd Maste linux_sendfile64(struct thread *td, struct linux_sendfile64_args *arg)
1694*fc7510aeSEd Maste {
1695*fc7510aeSEd Maste 	l_loff_t offset;
1696*fc7510aeSEd Maste 	int ret;
1697*fc7510aeSEd Maste 	int error;
1698*fc7510aeSEd Maste 
1699*fc7510aeSEd Maste 	if (arg->offset != NULL) {
1700*fc7510aeSEd Maste 		error = copyin(arg->offset, &offset, sizeof(offset));
1701*fc7510aeSEd Maste 		if (error != 0)
1702*fc7510aeSEd Maste 			return (error);
1703*fc7510aeSEd Maste 	}
1704*fc7510aeSEd Maste 
1705*fc7510aeSEd Maste 	ret = linux_sendfile_common(td, arg->out, arg->in,
1706*fc7510aeSEd Maste 		arg->offset != NULL ? &offset : NULL, arg->count);
1707*fc7510aeSEd Maste 
1708*fc7510aeSEd Maste 	if (arg->offset != NULL) {
1709*fc7510aeSEd Maste 		error = copyout(&offset, arg->offset, sizeof(offset));
1710*fc7510aeSEd Maste 		if (error != 0)
1711*fc7510aeSEd Maste 			return (error);
1712*fc7510aeSEd Maste 	}
1713*fc7510aeSEd Maste 
1714*fc7510aeSEd Maste 	return (ret);
1715*fc7510aeSEd Maste }
17167f8f1d7fSDmitry Chagin 
1717ea7b81d2SDmitry Chagin /* Argument list sizes for linux_socketcall */
1718abf20e93SDmitry Chagin static const unsigned char lxs_args_cnt[] = {
1719abf20e93SDmitry Chagin 	0 /* unused*/,		3 /* socket */,
1720abf20e93SDmitry Chagin 	3 /* bind */,		3 /* connect */,
1721abf20e93SDmitry Chagin 	2 /* listen */,		3 /* accept */,
1722abf20e93SDmitry Chagin 	3 /* getsockname */,	3 /* getpeername */,
1723abf20e93SDmitry Chagin 	4 /* socketpair */,	4 /* send */,
1724abf20e93SDmitry Chagin 	4 /* recv */,		6 /* sendto */,
1725abf20e93SDmitry Chagin 	6 /* recvfrom */,	2 /* shutdown */,
1726abf20e93SDmitry Chagin 	5 /* setsockopt */,	5 /* getsockopt */,
1727abf20e93SDmitry Chagin 	3 /* sendmsg */,	3 /* recvmsg */,
1728abf20e93SDmitry Chagin 	4 /* accept4 */,	5 /* recvmmsg */,
1729*fc7510aeSEd Maste 	4 /* sendmmsg */,	4 /* sendfile */
1730ea7b81d2SDmitry Chagin };
1731abf20e93SDmitry Chagin #define	LINUX_ARGS_CNT		(nitems(lxs_args_cnt) - 1)
1732abf20e93SDmitry Chagin #define	LINUX_ARG_SIZE(x)	(lxs_args_cnt[x] * sizeof(l_ulong))
1733ea7b81d2SDmitry Chagin 
1734c21dee17SSøren Schmidt int
1735b40ce416SJulian Elischer linux_socketcall(struct thread *td, struct linux_socketcall_args *args)
1736c21dee17SSøren Schmidt {
1737ea7b81d2SDmitry Chagin 	l_ulong a[6];
1738abf20e93SDmitry Chagin #if defined(__amd64__) && defined(COMPAT_LINUX32)
1739abf20e93SDmitry Chagin 	register_t l_args[6];
1740abf20e93SDmitry Chagin #endif
1741ea7b81d2SDmitry Chagin 	void *arg;
1742ea7b81d2SDmitry Chagin 	int error;
17433f3a4815SMarcel Moolenaar 
1744abf20e93SDmitry Chagin 	if (args->what < LINUX_SOCKET || args->what > LINUX_ARGS_CNT)
1745ea7b81d2SDmitry Chagin 		return (EINVAL);
1746abf20e93SDmitry Chagin 	error = copyin(PTRIN(args->args), a, LINUX_ARG_SIZE(args->what));
1747abf20e93SDmitry Chagin 	if (error != 0)
1748ea7b81d2SDmitry Chagin 		return (error);
1749ea7b81d2SDmitry Chagin 
1750abf20e93SDmitry Chagin #if defined(__amd64__) && defined(COMPAT_LINUX32)
1751abf20e93SDmitry Chagin 	for (int i = 0; i < lxs_args_cnt[args->what]; ++i)
1752abf20e93SDmitry Chagin 		l_args[i] = a[i];
1753abf20e93SDmitry Chagin 	arg = l_args;
1754abf20e93SDmitry Chagin #else
1755ea7b81d2SDmitry Chagin 	arg = a;
1756abf20e93SDmitry Chagin #endif
1757c21dee17SSøren Schmidt 	switch (args->what) {
1758c21dee17SSøren Schmidt 	case LINUX_SOCKET:
1759b40ce416SJulian Elischer 		return (linux_socket(td, arg));
1760c21dee17SSøren Schmidt 	case LINUX_BIND:
1761b40ce416SJulian Elischer 		return (linux_bind(td, arg));
1762c21dee17SSøren Schmidt 	case LINUX_CONNECT:
1763b40ce416SJulian Elischer 		return (linux_connect(td, arg));
1764c21dee17SSøren Schmidt 	case LINUX_LISTEN:
1765b40ce416SJulian Elischer 		return (linux_listen(td, arg));
1766c21dee17SSøren Schmidt 	case LINUX_ACCEPT:
1767b40ce416SJulian Elischer 		return (linux_accept(td, arg));
1768c21dee17SSøren Schmidt 	case LINUX_GETSOCKNAME:
1769b40ce416SJulian Elischer 		return (linux_getsockname(td, arg));
1770c21dee17SSøren Schmidt 	case LINUX_GETPEERNAME:
1771b40ce416SJulian Elischer 		return (linux_getpeername(td, arg));
1772c21dee17SSøren Schmidt 	case LINUX_SOCKETPAIR:
1773b40ce416SJulian Elischer 		return (linux_socketpair(td, arg));
1774c21dee17SSøren Schmidt 	case LINUX_SEND:
1775b40ce416SJulian Elischer 		return (linux_send(td, arg));
1776c21dee17SSøren Schmidt 	case LINUX_RECV:
1777b40ce416SJulian Elischer 		return (linux_recv(td, arg));
1778c21dee17SSøren Schmidt 	case LINUX_SENDTO:
1779b40ce416SJulian Elischer 		return (linux_sendto(td, arg));
1780c21dee17SSøren Schmidt 	case LINUX_RECVFROM:
1781b40ce416SJulian Elischer 		return (linux_recvfrom(td, arg));
1782c21dee17SSøren Schmidt 	case LINUX_SHUTDOWN:
1783b40ce416SJulian Elischer 		return (linux_shutdown(td, arg));
1784c21dee17SSøren Schmidt 	case LINUX_SETSOCKOPT:
1785b40ce416SJulian Elischer 		return (linux_setsockopt(td, arg));
1786c21dee17SSøren Schmidt 	case LINUX_GETSOCKOPT:
1787b40ce416SJulian Elischer 		return (linux_getsockopt(td, arg));
1788e76bba09SSøren Schmidt 	case LINUX_SENDMSG:
1789ca26842eSHajimu UMEMOTO 		return (linux_sendmsg(td, arg));
1790e76bba09SSøren Schmidt 	case LINUX_RECVMSG:
1791b40ce416SJulian Elischer 		return (linux_recvmsg(td, arg));
1792f8cd0af2SDmitry Chagin 	case LINUX_ACCEPT4:
1793f8cd0af2SDmitry Chagin 		return (linux_accept4(td, arg));
1794e1ff74c0SDmitry Chagin 	case LINUX_RECVMMSG:
1795e1ff74c0SDmitry Chagin 		return (linux_recvmmsg(td, arg));
1796e1ff74c0SDmitry Chagin 	case LINUX_SENDMMSG:
1797e1ff74c0SDmitry Chagin 		return (linux_sendmmsg(td, arg));
1798*fc7510aeSEd Maste 	case LINUX_SENDFILE:
1799*fc7510aeSEd Maste 		return (linux_sendfile(td, arg));
1800c21dee17SSøren Schmidt 	}
18013f3a4815SMarcel Moolenaar 
18023f3a4815SMarcel Moolenaar 	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
18033f3a4815SMarcel Moolenaar 	return (ENOSYS);
1804c21dee17SSøren Schmidt }
1805*fc7510aeSEd Maste #endif /* __i386__ || __arm__ || (__amd64__ && COMPAT_LINUX32) */
1806