xref: /freebsd/sys/compat/linux/linux_socket.c (revision c2d47457052edcbffc6c46d8188253ba0a039c5b)
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>
52ca26842eSHajimu UMEMOTO #include <sys/syslog.h>
53d0b2365eSKonstantin Belousov #include <sys/un.h>
541f3dad5aSBruce Evans 
554b79449eSBjoern A. Zeeb #include <net/if.h>
56eedc7fd9SGleb Smirnoff #include <net/vnet.h>
57c21dee17SSøren Schmidt #include <netinet/in.h>
58f2477ae1SMike Smith #include <netinet/in_systm.h>
59f2477ae1SMike Smith #include <netinet/ip.h>
60fb709557SJohn Baldwin #include <netinet/tcp.h>
61ca26842eSHajimu UMEMOTO #ifdef INET6
62ca26842eSHajimu UMEMOTO #include <netinet/ip6.h>
63ca26842eSHajimu UMEMOTO #include <netinet6/ip6_var.h>
64ca26842eSHajimu UMEMOTO #endif
65c21dee17SSøren Schmidt 
661997c537SDavid E. O'Brien #ifdef COMPAT_LINUX32
674af27623STim J. Robbins #include <machine/../linux32/linux.h>
684af27623STim J. Robbins #include <machine/../linux32/linux32_proto.h>
691997c537SDavid E. O'Brien #else
701997c537SDavid E. O'Brien #include <machine/../linux/linux.h>
711997c537SDavid E. O'Brien #include <machine/../linux/linux_proto.h>
724af27623STim J. Robbins #endif
73d5368bf3SDmitry Chagin #include <compat/linux/linux_common.h>
744d0f380dSDmitry Chagin #include <compat/linux/linux_file.h>
75da6d8ae6SEdward Tomasz Napierala #include <compat/linux/linux_mib.h>
7640dbba57SAssar Westerlund #include <compat/linux/linux_socket.h>
77e1ff74c0SDmitry Chagin #include <compat/linux/linux_timer.h>
78ac951e62SMarcel Moolenaar #include <compat/linux/linux_util.h>
79c21dee17SSøren Schmidt 
80e1ff74c0SDmitry Chagin static int linux_sendmsg_common(struct thread *, l_int, struct l_msghdr *,
81e1ff74c0SDmitry Chagin 					l_uint);
82e1ff74c0SDmitry Chagin static int linux_recvmsg_common(struct thread *, l_int, struct l_msghdr *,
83e1ff74c0SDmitry Chagin 					l_uint, struct msghdr *);
844cf10e29SDmitry Chagin static int linux_set_socket_flags(int, int *);
85ca26842eSHajimu UMEMOTO 
86ca26842eSHajimu UMEMOTO 
87c21dee17SSøren Schmidt static int
88c21dee17SSøren Schmidt linux_to_bsd_sockopt_level(int level)
89c21dee17SSøren Schmidt {
903f3a4815SMarcel Moolenaar 
91c21dee17SSøren Schmidt 	switch (level) {
92c21dee17SSøren Schmidt 	case LINUX_SOL_SOCKET:
933f3a4815SMarcel Moolenaar 		return (SOL_SOCKET);
94c21dee17SSøren Schmidt 	}
953f3a4815SMarcel Moolenaar 	return (level);
96c21dee17SSøren Schmidt }
97c21dee17SSøren Schmidt 
983f3a4815SMarcel Moolenaar static int
9984b11cd8SMitsuru IWASAKI bsd_to_linux_sockopt_level(int level)
10084b11cd8SMitsuru IWASAKI {
10184b11cd8SMitsuru IWASAKI 
10284b11cd8SMitsuru IWASAKI 	switch (level) {
10384b11cd8SMitsuru IWASAKI 	case SOL_SOCKET:
10484b11cd8SMitsuru IWASAKI 		return (LINUX_SOL_SOCKET);
10584b11cd8SMitsuru IWASAKI 	}
10684b11cd8SMitsuru IWASAKI 	return (level);
10784b11cd8SMitsuru IWASAKI }
10884b11cd8SMitsuru IWASAKI 
10984b11cd8SMitsuru IWASAKI static int
1103f3a4815SMarcel Moolenaar linux_to_bsd_ip_sockopt(int opt)
111c21dee17SSøren Schmidt {
1123f3a4815SMarcel Moolenaar 
113c21dee17SSøren Schmidt 	switch (opt) {
114c21dee17SSøren Schmidt 	case LINUX_IP_TOS:
1153f3a4815SMarcel Moolenaar 		return (IP_TOS);
116c21dee17SSøren Schmidt 	case LINUX_IP_TTL:
1173f3a4815SMarcel Moolenaar 		return (IP_TTL);
11866ff6a3cSBill Fenner 	case LINUX_IP_OPTIONS:
1193f3a4815SMarcel Moolenaar 		return (IP_OPTIONS);
12066ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_IF:
1213f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_IF);
12266ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_TTL:
1233f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_TTL);
12466ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_LOOP:
1253f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_LOOP);
12666ff6a3cSBill Fenner 	case LINUX_IP_ADD_MEMBERSHIP:
1273f3a4815SMarcel Moolenaar 		return (IP_ADD_MEMBERSHIP);
12866ff6a3cSBill Fenner 	case LINUX_IP_DROP_MEMBERSHIP:
1293f3a4815SMarcel Moolenaar 		return (IP_DROP_MEMBERSHIP);
13066ff6a3cSBill Fenner 	case LINUX_IP_HDRINCL:
1313f3a4815SMarcel Moolenaar 		return (IP_HDRINCL);
132c21dee17SSøren Schmidt 	}
1333f3a4815SMarcel Moolenaar 	return (-1);
134c21dee17SSøren Schmidt }
135c21dee17SSøren Schmidt 
136c21dee17SSøren Schmidt static int
13786a9058bSAndrey V. Elsukov linux_to_bsd_ip6_sockopt(int opt)
13886a9058bSAndrey V. Elsukov {
13986a9058bSAndrey V. Elsukov 
14086a9058bSAndrey V. Elsukov 	switch (opt) {
14186a9058bSAndrey V. Elsukov 	case LINUX_IPV6_NEXTHOP:
14286a9058bSAndrey V. Elsukov 		return (IPV6_NEXTHOP);
14386a9058bSAndrey V. Elsukov 	case LINUX_IPV6_UNICAST_HOPS:
14486a9058bSAndrey V. Elsukov 		return (IPV6_UNICAST_HOPS);
14586a9058bSAndrey V. Elsukov 	case LINUX_IPV6_MULTICAST_IF:
14686a9058bSAndrey V. Elsukov 		return (IPV6_MULTICAST_IF);
14786a9058bSAndrey V. Elsukov 	case LINUX_IPV6_MULTICAST_HOPS:
14886a9058bSAndrey V. Elsukov 		return (IPV6_MULTICAST_HOPS);
14986a9058bSAndrey V. Elsukov 	case LINUX_IPV6_MULTICAST_LOOP:
15086a9058bSAndrey V. Elsukov 		return (IPV6_MULTICAST_LOOP);
15186a9058bSAndrey V. Elsukov 	case LINUX_IPV6_ADD_MEMBERSHIP:
15286a9058bSAndrey V. Elsukov 		return (IPV6_JOIN_GROUP);
15386a9058bSAndrey V. Elsukov 	case LINUX_IPV6_DROP_MEMBERSHIP:
15486a9058bSAndrey V. Elsukov 		return (IPV6_LEAVE_GROUP);
15586a9058bSAndrey V. Elsukov 	case LINUX_IPV6_V6ONLY:
15686a9058bSAndrey V. Elsukov 		return (IPV6_V6ONLY);
15786a9058bSAndrey V. Elsukov 	case LINUX_IPV6_DONTFRAG:
15886a9058bSAndrey V. Elsukov 		return (IPV6_DONTFRAG);
15986a9058bSAndrey V. Elsukov #if 0
16086a9058bSAndrey V. Elsukov 	case LINUX_IPV6_CHECKSUM:
16186a9058bSAndrey V. Elsukov 		return (IPV6_CHECKSUM);
16286a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVPKTINFO:
16386a9058bSAndrey V. Elsukov 		return (IPV6_RECVPKTINFO);
16486a9058bSAndrey V. Elsukov 	case LINUX_IPV6_PKTINFO:
16586a9058bSAndrey V. Elsukov 		return (IPV6_PKTINFO);
16686a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVHOPLIMIT:
16786a9058bSAndrey V. Elsukov 		return (IPV6_RECVHOPLIMIT);
16886a9058bSAndrey V. Elsukov 	case LINUX_IPV6_HOPLIMIT:
16986a9058bSAndrey V. Elsukov 		return (IPV6_HOPLIMIT);
17086a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVHOPOPTS:
17186a9058bSAndrey V. Elsukov 		return (IPV6_RECVHOPOPTS);
17286a9058bSAndrey V. Elsukov 	case LINUX_IPV6_HOPOPTS:
17386a9058bSAndrey V. Elsukov 		return (IPV6_HOPOPTS);
17486a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RTHDRDSTOPTS:
17586a9058bSAndrey V. Elsukov 		return (IPV6_RTHDRDSTOPTS);
17686a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVRTHDR:
17786a9058bSAndrey V. Elsukov 		return (IPV6_RECVRTHDR);
17886a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RTHDR:
17986a9058bSAndrey V. Elsukov 		return (IPV6_RTHDR);
18086a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVDSTOPTS:
18186a9058bSAndrey V. Elsukov 		return (IPV6_RECVDSTOPTS);
18286a9058bSAndrey V. Elsukov 	case LINUX_IPV6_DSTOPTS:
18386a9058bSAndrey V. Elsukov 		return (IPV6_DSTOPTS);
18486a9058bSAndrey V. Elsukov 	case LINUX_IPV6_RECVPATHMTU:
18586a9058bSAndrey V. Elsukov 		return (IPV6_RECVPATHMTU);
18686a9058bSAndrey V. Elsukov 	case LINUX_IPV6_PATHMTU:
18786a9058bSAndrey V. Elsukov 		return (IPV6_PATHMTU);
18886a9058bSAndrey V. Elsukov #endif
18986a9058bSAndrey V. Elsukov 	}
19086a9058bSAndrey V. Elsukov 	return (-1);
19186a9058bSAndrey V. Elsukov }
19286a9058bSAndrey V. Elsukov 
19386a9058bSAndrey V. Elsukov static int
194c21dee17SSøren Schmidt linux_to_bsd_so_sockopt(int opt)
195c21dee17SSøren Schmidt {
1963f3a4815SMarcel Moolenaar 
197c21dee17SSøren Schmidt 	switch (opt) {
198c21dee17SSøren Schmidt 	case LINUX_SO_DEBUG:
1993f3a4815SMarcel Moolenaar 		return (SO_DEBUG);
200c21dee17SSøren Schmidt 	case LINUX_SO_REUSEADDR:
2013f3a4815SMarcel Moolenaar 		return (SO_REUSEADDR);
202c21dee17SSøren Schmidt 	case LINUX_SO_TYPE:
2033f3a4815SMarcel Moolenaar 		return (SO_TYPE);
204c21dee17SSøren Schmidt 	case LINUX_SO_ERROR:
2053f3a4815SMarcel Moolenaar 		return (SO_ERROR);
206c21dee17SSøren Schmidt 	case LINUX_SO_DONTROUTE:
2073f3a4815SMarcel Moolenaar 		return (SO_DONTROUTE);
208c21dee17SSøren Schmidt 	case LINUX_SO_BROADCAST:
2093f3a4815SMarcel Moolenaar 		return (SO_BROADCAST);
210c21dee17SSøren Schmidt 	case LINUX_SO_SNDBUF:
2113f3a4815SMarcel Moolenaar 		return (SO_SNDBUF);
212c21dee17SSøren Schmidt 	case LINUX_SO_RCVBUF:
2133f3a4815SMarcel Moolenaar 		return (SO_RCVBUF);
214c21dee17SSøren Schmidt 	case LINUX_SO_KEEPALIVE:
2153f3a4815SMarcel Moolenaar 		return (SO_KEEPALIVE);
216c21dee17SSøren Schmidt 	case LINUX_SO_OOBINLINE:
2173f3a4815SMarcel Moolenaar 		return (SO_OOBINLINE);
218c21dee17SSøren Schmidt 	case LINUX_SO_LINGER:
2193f3a4815SMarcel Moolenaar 		return (SO_LINGER);
220d0b2365eSKonstantin Belousov 	case LINUX_SO_PEERCRED:
221d0b2365eSKonstantin Belousov 		return (LOCAL_PEERCRED);
222d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVLOWAT:
223d0b2365eSKonstantin Belousov 		return (SO_RCVLOWAT);
224d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDLOWAT:
225d0b2365eSKonstantin Belousov 		return (SO_SNDLOWAT);
226d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVTIMEO:
227d0b2365eSKonstantin Belousov 		return (SO_RCVTIMEO);
228d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDTIMEO:
229d0b2365eSKonstantin Belousov 		return (SO_SNDTIMEO);
230d0b2365eSKonstantin Belousov 	case LINUX_SO_TIMESTAMP:
231d0b2365eSKonstantin Belousov 		return (SO_TIMESTAMP);
232d0b2365eSKonstantin Belousov 	case LINUX_SO_ACCEPTCONN:
233d0b2365eSKonstantin Belousov 		return (SO_ACCEPTCONN);
234c21dee17SSøren Schmidt 	}
2353f3a4815SMarcel Moolenaar 	return (-1);
236c21dee17SSøren Schmidt }
237c21dee17SSøren Schmidt 
23840dbba57SAssar Westerlund static int
239fb709557SJohn Baldwin linux_to_bsd_tcp_sockopt(int opt)
240fb709557SJohn Baldwin {
241fb709557SJohn Baldwin 
242fb709557SJohn Baldwin 	switch (opt) {
243fb709557SJohn Baldwin 	case LINUX_TCP_NODELAY:
244fb709557SJohn Baldwin 		return (TCP_NODELAY);
245fb709557SJohn Baldwin 	case LINUX_TCP_MAXSEG:
246fb709557SJohn Baldwin 		return (TCP_MAXSEG);
247*c2d47457SEdward Tomasz Napierala 	case LINUX_TCP_CORK:
248*c2d47457SEdward Tomasz Napierala 		return (TCP_NOPUSH);
249fb709557SJohn Baldwin 	case LINUX_TCP_KEEPIDLE:
250fb709557SJohn Baldwin 		return (TCP_KEEPIDLE);
251fb709557SJohn Baldwin 	case LINUX_TCP_KEEPINTVL:
252fb709557SJohn Baldwin 		return (TCP_KEEPINTVL);
253fb709557SJohn Baldwin 	case LINUX_TCP_KEEPCNT:
254fb709557SJohn Baldwin 		return (TCP_KEEPCNT);
255fb709557SJohn Baldwin 	case LINUX_TCP_MD5SIG:
256fb709557SJohn Baldwin 		return (TCP_MD5SIG);
257fb709557SJohn Baldwin 	}
258fb709557SJohn Baldwin 	return (-1);
259fb709557SJohn Baldwin }
260fb709557SJohn Baldwin 
261fb709557SJohn Baldwin static int
26240dbba57SAssar Westerlund linux_to_bsd_msg_flags(int flags)
26340dbba57SAssar Westerlund {
26440dbba57SAssar Westerlund 	int ret_flags = 0;
26540dbba57SAssar Westerlund 
26640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_OOB)
26740dbba57SAssar Westerlund 		ret_flags |= MSG_OOB;
26840dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PEEK)
26940dbba57SAssar Westerlund 		ret_flags |= MSG_PEEK;
27040dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTROUTE)
27140dbba57SAssar Westerlund 		ret_flags |= MSG_DONTROUTE;
27240dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CTRUNC)
27340dbba57SAssar Westerlund 		ret_flags |= MSG_CTRUNC;
27440dbba57SAssar Westerlund 	if (flags & LINUX_MSG_TRUNC)
27540dbba57SAssar Westerlund 		ret_flags |= MSG_TRUNC;
27640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTWAIT)
27740dbba57SAssar Westerlund 		ret_flags |= MSG_DONTWAIT;
27840dbba57SAssar Westerlund 	if (flags & LINUX_MSG_EOR)
27940dbba57SAssar Westerlund 		ret_flags |= MSG_EOR;
28040dbba57SAssar Westerlund 	if (flags & LINUX_MSG_WAITALL)
28140dbba57SAssar Westerlund 		ret_flags |= MSG_WAITALL;
2828d6e40c3SMaxim Sobolev 	if (flags & LINUX_MSG_NOSIGNAL)
2838d6e40c3SMaxim Sobolev 		ret_flags |= MSG_NOSIGNAL;
28440dbba57SAssar Westerlund #if 0 /* not handled */
28540dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PROXY)
28640dbba57SAssar Westerlund 		;
28740dbba57SAssar Westerlund 	if (flags & LINUX_MSG_FIN)
28840dbba57SAssar Westerlund 		;
28940dbba57SAssar Westerlund 	if (flags & LINUX_MSG_SYN)
29040dbba57SAssar Westerlund 		;
29140dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CONFIRM)
29240dbba57SAssar Westerlund 		;
29340dbba57SAssar Westerlund 	if (flags & LINUX_MSG_RST)
29440dbba57SAssar Westerlund 		;
29540dbba57SAssar Westerlund 	if (flags & LINUX_MSG_ERRQUEUE)
29640dbba57SAssar Westerlund 		;
29740dbba57SAssar Westerlund #endif
298e667ee63SDmitry Chagin 	return (ret_flags);
29940dbba57SAssar Westerlund }
30040dbba57SAssar Westerlund 
3015a8a13e0SDavid Malone static int
30274f5d680SKonstantin Belousov linux_to_bsd_cmsg_type(int cmsg_type)
3035a8a13e0SDavid Malone {
30474f5d680SKonstantin Belousov 
30574f5d680SKonstantin Belousov 	switch (cmsg_type) {
30674f5d680SKonstantin Belousov 	case LINUX_SCM_RIGHTS:
30774f5d680SKonstantin Belousov 		return (SCM_RIGHTS);
308605da56bSAndriy Gapon 	case LINUX_SCM_CREDENTIALS:
309605da56bSAndriy Gapon 		return (SCM_CREDS);
31074f5d680SKonstantin Belousov 	}
31174f5d680SKonstantin Belousov 	return (-1);
31274f5d680SKonstantin Belousov }
31374f5d680SKonstantin Belousov 
31474f5d680SKonstantin Belousov static int
31574f5d680SKonstantin Belousov bsd_to_linux_cmsg_type(int cmsg_type)
31674f5d680SKonstantin Belousov {
31774f5d680SKonstantin Belousov 
31874f5d680SKonstantin Belousov 	switch (cmsg_type) {
31974f5d680SKonstantin Belousov 	case SCM_RIGHTS:
32074f5d680SKonstantin Belousov 		return (LINUX_SCM_RIGHTS);
321605da56bSAndriy Gapon 	case SCM_CREDS:
322605da56bSAndriy Gapon 		return (LINUX_SCM_CREDENTIALS);
323bbf392d5SDmitry Chagin 	case SCM_TIMESTAMP:
324bbf392d5SDmitry Chagin 		return (LINUX_SCM_TIMESTAMP);
32574f5d680SKonstantin Belousov 	}
32674f5d680SKonstantin Belousov 	return (-1);
32774f5d680SKonstantin Belousov }
32874f5d680SKonstantin Belousov 
32974f5d680SKonstantin Belousov static int
33074f5d680SKonstantin Belousov linux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
33174f5d680SKonstantin Belousov {
33274f5d680SKonstantin Belousov 	if (lhdr->msg_controllen > INT_MAX)
33374f5d680SKonstantin Belousov 		return (ENOBUFS);
33474f5d680SKonstantin Belousov 
33574f5d680SKonstantin Belousov 	bhdr->msg_name		= PTRIN(lhdr->msg_name);
33674f5d680SKonstantin Belousov 	bhdr->msg_namelen	= lhdr->msg_namelen;
33774f5d680SKonstantin Belousov 	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
33874f5d680SKonstantin Belousov 	bhdr->msg_iovlen	= lhdr->msg_iovlen;
33974f5d680SKonstantin Belousov 	bhdr->msg_control	= PTRIN(lhdr->msg_control);
340605da56bSAndriy Gapon 
341605da56bSAndriy Gapon 	/*
342605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
343605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
344605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
345605da56bSAndriy Gapon 	 *
346605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
347605da56bSAndriy Gapon 	 * control messages.
348605da56bSAndriy Gapon 	 */
349605da56bSAndriy Gapon 
35074f5d680SKonstantin Belousov 	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
35174f5d680SKonstantin Belousov 	return (0);
35274f5d680SKonstantin Belousov }
35374f5d680SKonstantin Belousov 
35474f5d680SKonstantin Belousov static int
35574f5d680SKonstantin Belousov bsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
35674f5d680SKonstantin Belousov {
35774f5d680SKonstantin Belousov 	lhdr->msg_name		= PTROUT(bhdr->msg_name);
35874f5d680SKonstantin Belousov 	lhdr->msg_namelen	= bhdr->msg_namelen;
35974f5d680SKonstantin Belousov 	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
36074f5d680SKonstantin Belousov 	lhdr->msg_iovlen	= bhdr->msg_iovlen;
36174f5d680SKonstantin Belousov 	lhdr->msg_control	= PTROUT(bhdr->msg_control);
362605da56bSAndriy Gapon 
363605da56bSAndriy Gapon 	/*
364605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
365605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
366605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
367605da56bSAndriy Gapon 	 *
368605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
369605da56bSAndriy Gapon 	 * control messages.
370605da56bSAndriy Gapon 	 */
371605da56bSAndriy Gapon 
37274f5d680SKonstantin Belousov 	/* msg_flags skipped */
37374f5d680SKonstantin Belousov 	return (0);
37474f5d680SKonstantin Belousov }
37574f5d680SKonstantin Belousov 
37674f5d680SKonstantin Belousov static int
3774cf10e29SDmitry Chagin linux_set_socket_flags(int lflags, int *flags)
37838a18e97SDmitry Chagin {
37938a18e97SDmitry Chagin 
3804cf10e29SDmitry Chagin 	if (lflags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
3814cf10e29SDmitry Chagin 		return (EINVAL);
3824cf10e29SDmitry Chagin 	if (lflags & LINUX_SOCK_NONBLOCK)
3834cf10e29SDmitry Chagin 		*flags |= SOCK_NONBLOCK;
3844cf10e29SDmitry Chagin 	if (lflags & LINUX_SOCK_CLOEXEC)
3854cf10e29SDmitry Chagin 		*flags |= SOCK_CLOEXEC;
38638a18e97SDmitry Chagin 	return (0);
38738a18e97SDmitry Chagin }
38838a18e97SDmitry Chagin 
38938a18e97SDmitry Chagin static int
39074f5d680SKonstantin Belousov linux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
39174f5d680SKonstantin Belousov     struct mbuf *control, enum uio_seg segflg)
39274f5d680SKonstantin Belousov {
3935a8a13e0SDavid Malone 	struct sockaddr *to;
394d5368bf3SDmitry Chagin 	int error, len;
3955a8a13e0SDavid Malone 
3965a8a13e0SDavid Malone 	if (mp->msg_name != NULL) {
397d5368bf3SDmitry Chagin 		len = mp->msg_namelen;
398d5368bf3SDmitry Chagin 		error = linux_to_bsd_sockaddr(mp->msg_name, &to, &len);
399dddb7e7fSDmitry Chagin 		if (error != 0)
4005a8a13e0SDavid Malone 			return (error);
4015a8a13e0SDavid Malone 		mp->msg_name = to;
4025a8a13e0SDavid Malone 	} else
4035a8a13e0SDavid Malone 		to = NULL;
4045a8a13e0SDavid Malone 
405a6886ef1SMaxim Sobolev 	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
406a6886ef1SMaxim Sobolev 	    segflg);
4075a8a13e0SDavid Malone 
4085a8a13e0SDavid Malone 	if (to)
4091ede983cSDag-Erling Smørgrav 		free(to, M_SONAME);
4105a8a13e0SDavid Malone 	return (error);
4115a8a13e0SDavid Malone }
4125a8a13e0SDavid Malone 
4133f3a4815SMarcel Moolenaar /* Return 0 if IP_HDRINCL is set for the given socket. */
414f2477ae1SMike Smith static int
415e140eb43SDavid Malone linux_check_hdrincl(struct thread *td, int s)
416f2477ae1SMike Smith {
417857ad5a3SDmitry Chagin 	int error, optval;
418857ad5a3SDmitry Chagin 	socklen_t size_val;
419f2477ae1SMike Smith 
420e140eb43SDavid Malone 	size_val = sizeof(optval);
421e140eb43SDavid Malone 	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
422e140eb43SDavid Malone 	    &optval, UIO_SYSSPACE, &size_val);
423dddb7e7fSDmitry Chagin 	if (error != 0)
4243f3a4815SMarcel Moolenaar 		return (error);
4253f3a4815SMarcel Moolenaar 
4263f3a4815SMarcel Moolenaar 	return (optval == 0);
427f2477ae1SMike Smith }
428f2477ae1SMike Smith 
429f2477ae1SMike Smith /*
430f2477ae1SMike Smith  * Updated sendto() when IP_HDRINCL is set:
431f2477ae1SMike Smith  * tweak endian-dependent fields in the IP packet.
432f2477ae1SMike Smith  */
433f2477ae1SMike Smith static int
43438da2381SRobert Watson linux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
435f2477ae1SMike Smith {
436f2477ae1SMike Smith /*
437f2477ae1SMike Smith  * linux_ip_copysize defines how many bytes we should copy
438f2477ae1SMike Smith  * from the beginning of the IP packet before we customize it for BSD.
439a6886ef1SMaxim Sobolev  * It should include all the fields we modify (ip_len and ip_off).
440f2477ae1SMike Smith  */
441f2477ae1SMike Smith #define linux_ip_copysize	8
442f2477ae1SMike Smith 
443f2477ae1SMike Smith 	struct ip *packet;
4445a8a13e0SDavid Malone 	struct msghdr msg;
445a6886ef1SMaxim Sobolev 	struct iovec aiov[1];
446f2477ae1SMike Smith 	int error;
447f2477ae1SMike Smith 
448aa675b57SDavid Schultz 	/* Check that the packet isn't too big or too small. */
449aa675b57SDavid Schultz 	if (linux_args->len < linux_ip_copysize ||
450aa675b57SDavid Schultz 	    linux_args->len > IP_MAXPACKET)
4513f3a4815SMarcel Moolenaar 		return (EINVAL);
452f2477ae1SMike Smith 
453e0d3ea8cSDmitry Chagin 	packet = (struct ip *)malloc(linux_args->len, M_LINUX, M_WAITOK);
454f2477ae1SMike Smith 
455a6886ef1SMaxim Sobolev 	/* Make kernel copy of the packet to be sent */
4564af27623STim J. Robbins 	if ((error = copyin(PTRIN(linux_args->msg), packet,
457a6886ef1SMaxim Sobolev 	    linux_args->len)))
458a6886ef1SMaxim Sobolev 		goto goout;
459f2477ae1SMike Smith 
460f2477ae1SMike Smith 	/* Convert fields from Linux to BSD raw IP socket format */
4615a8a13e0SDavid Malone 	packet->ip_len = linux_args->len;
462f2477ae1SMike Smith 	packet->ip_off = ntohs(packet->ip_off);
463f2477ae1SMike Smith 
464f2477ae1SMike Smith 	/* Prepare the msghdr and iovec structures describing the new packet */
4654af27623STim J. Robbins 	msg.msg_name = PTRIN(linux_args->to);
4665a8a13e0SDavid Malone 	msg.msg_namelen = linux_args->tolen;
4675a8a13e0SDavid Malone 	msg.msg_iov = aiov;
468a6886ef1SMaxim Sobolev 	msg.msg_iovlen = 1;
4695a8a13e0SDavid Malone 	msg.msg_control = NULL;
4705a8a13e0SDavid Malone 	msg.msg_flags = 0;
4715a8a13e0SDavid Malone 	aiov[0].iov_base = (char *)packet;
472a6886ef1SMaxim Sobolev 	aiov[0].iov_len = linux_args->len;
473a6886ef1SMaxim Sobolev 	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
47474f5d680SKonstantin Belousov 	    NULL, UIO_SYSSPACE);
475a6886ef1SMaxim Sobolev goout:
476e0d3ea8cSDmitry Chagin 	free(packet, M_LINUX);
4775a8a13e0SDavid Malone 	return (error);
478f2477ae1SMike Smith }
479f2477ae1SMike Smith 
480a12b9b3dSDmitry Chagin int
481b40ce416SJulian Elischer linux_socket(struct thread *td, struct linux_socket_args *args)
482c21dee17SSøren Schmidt {
483d293f35cSEdward Tomasz Napierala 	int domain, retval_socket, type;
484c21dee17SSøren Schmidt 
485d293f35cSEdward Tomasz Napierala 	type = args->type & LINUX_SOCK_TYPE_MASK;
486d293f35cSEdward Tomasz Napierala 	if (type < 0 || type > LINUX_SOCK_MAX)
487eeb63e51SDmitry Chagin 		return (EINVAL);
4884cf10e29SDmitry Chagin 	retval_socket = linux_set_socket_flags(args->type & ~LINUX_SOCK_TYPE_MASK,
489d293f35cSEdward Tomasz Napierala 		&type);
4904cf10e29SDmitry Chagin 	if (retval_socket != 0)
4914cf10e29SDmitry Chagin 		return (retval_socket);
492d293f35cSEdward Tomasz Napierala 	domain = linux_to_bsd_domain(args->domain);
493d293f35cSEdward Tomasz Napierala 	if (domain == -1)
494d9b063ccSDmitry Chagin 		return (EAFNOSUPPORT);
495f2477ae1SMike Smith 
496d293f35cSEdward Tomasz Napierala 	retval_socket = kern_socket(td, domain, type, args->protocol);
4976994ea54SDmitry Chagin 	if (retval_socket)
4986994ea54SDmitry Chagin 		return (retval_socket);
4996994ea54SDmitry Chagin 
500d293f35cSEdward Tomasz Napierala 	if (type == SOCK_RAW
501d293f35cSEdward Tomasz Napierala 	    && (args->protocol == IPPROTO_RAW || args->protocol == 0)
502d293f35cSEdward Tomasz Napierala 	    && domain == PF_INET) {
503f2477ae1SMike Smith 		/* It's a raw IP socket: set the IP_HDRINCL option. */
504e140eb43SDavid Malone 		int hdrincl;
505f2477ae1SMike Smith 
506e140eb43SDavid Malone 		hdrincl = 1;
507e140eb43SDavid Malone 		/* We ignore any error returned by kern_setsockopt() */
508e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
509e140eb43SDavid Malone 		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
510f2477ae1SMike Smith 	}
511ca26842eSHajimu UMEMOTO #ifdef INET6
512ca26842eSHajimu UMEMOTO 	/*
513d97bee3eSBjoern A. Zeeb 	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by default
514d97bee3eSBjoern A. Zeeb 	 * and some apps depend on this. So, set V6ONLY to 0 for Linux apps.
515d97bee3eSBjoern A. Zeeb 	 * For simplicity we do this unconditionally of the net.inet6.ip6.v6only
516d97bee3eSBjoern A. Zeeb 	 * sysctl value.
517ca26842eSHajimu UMEMOTO 	 */
518d293f35cSEdward Tomasz Napierala 	if (domain == PF_INET6) {
519e140eb43SDavid Malone 		int v6only;
520ca26842eSHajimu UMEMOTO 
521e140eb43SDavid Malone 		v6only = 0;
522ca26842eSHajimu UMEMOTO 		/* We ignore any error returned by setsockopt() */
523e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
524e140eb43SDavid Malone 		    &v6only, UIO_SYSSPACE, sizeof(v6only));
525ca26842eSHajimu UMEMOTO 	}
526ca26842eSHajimu UMEMOTO #endif
5273f3a4815SMarcel Moolenaar 
5283f3a4815SMarcel Moolenaar 	return (retval_socket);
529c21dee17SSøren Schmidt }
530c21dee17SSøren Schmidt 
531a12b9b3dSDmitry Chagin int
532b40ce416SJulian Elischer linux_bind(struct thread *td, struct linux_bind_args *args)
533c21dee17SSøren Schmidt {
534ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
535c21dee17SSøren Schmidt 	int error;
536c21dee17SSøren Schmidt 
537d5368bf3SDmitry Chagin 	error = linux_to_bsd_sockaddr(PTRIN(args->name), &sa,
538d5368bf3SDmitry Chagin 	    &args->namelen);
539dddb7e7fSDmitry Chagin 	if (error != 0)
540ca26842eSHajimu UMEMOTO 		return (error);
541ca26842eSHajimu UMEMOTO 
5426e646651SKonstantin Belousov 	error = kern_bindat(td, AT_FDCWD, args->s, sa);
543b33887eaSJohn Baldwin 	free(sa, M_SONAME);
544d5368bf3SDmitry Chagin 
545d5368bf3SDmitry Chagin 	/* XXX */
546745aaef5SKonstantin Belousov 	if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
547d4b7423fSAlexander Leidinger 		return (EINVAL);
548b33887eaSJohn Baldwin 	return (error);
549c21dee17SSøren Schmidt }
550c21dee17SSøren Schmidt 
551930a65feSAndrew Gallatin int
552b40ce416SJulian Elischer linux_connect(struct thread *td, struct linux_connect_args *args)
553c21dee17SSøren Schmidt {
5540bf301c0SJonathan Lemon 	struct socket *so;
555ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
55695653579SGleb Smirnoff 	struct file *fp;
55739c95b83SMatthew Dillon 	u_int fflag;
558c21dee17SSøren Schmidt 	int error;
559c21dee17SSøren Schmidt 
560d5368bf3SDmitry Chagin 	error = linux_to_bsd_sockaddr(PTRIN(args->name), &sa,
561d5368bf3SDmitry Chagin 	    &args->namelen);
562dddb7e7fSDmitry Chagin 	if (error != 0)
563ca26842eSHajimu UMEMOTO 		return (error);
564ca26842eSHajimu UMEMOTO 
5656e646651SKonstantin Belousov 	error = kern_connectat(td, AT_FDCWD, args->s, sa);
566b33887eaSJohn Baldwin 	free(sa, M_SONAME);
5670bf301c0SJonathan Lemon 	if (error != EISCONN)
5680bf301c0SJonathan Lemon 		return (error);
5690bf301c0SJonathan Lemon 
570dad3b88aSMike Smith 	/*
571dad3b88aSMike Smith 	 * Linux doesn't return EISCONN the first time it occurs,
572dad3b88aSMike Smith 	 * when on a non-blocking socket. Instead it returns the
573dad3b88aSMike Smith 	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
574dad3b88aSMike Smith 	 */
575cbd92ce6SMatt Macy 	error = getsock_cap(td, args->s, &cap_connect_rights,
57695653579SGleb Smirnoff 	    &fp, &fflag, NULL);
57795653579SGleb Smirnoff 	if (error != 0)
57895653579SGleb Smirnoff 		return (error);
57995653579SGleb Smirnoff 
5800bf301c0SJonathan Lemon 	error = EISCONN;
58195653579SGleb Smirnoff 	so = fp->f_data;
58239c95b83SMatthew Dillon 	if (fflag & FNONBLOCK) {
5834641373fSJohn Baldwin 		SOCK_LOCK(so);
5845002a60fSMarcel Moolenaar 		if (so->so_emuldata == 0)
5850bf301c0SJonathan Lemon 			error = so->so_error;
5860bf301c0SJonathan Lemon 		so->so_emuldata = (void *)1;
5874641373fSJohn Baldwin 		SOCK_UNLOCK(so);
588dad3b88aSMike Smith 	}
58995653579SGleb Smirnoff 	fdrop(fp, td);
59095653579SGleb Smirnoff 
5913f3a4815SMarcel Moolenaar 	return (error);
592c21dee17SSøren Schmidt }
593c21dee17SSøren Schmidt 
594a12b9b3dSDmitry Chagin int
595b40ce416SJulian Elischer linux_listen(struct thread *td, struct linux_listen_args *args)
596c21dee17SSøren Schmidt {
597c21dee17SSøren Schmidt 
598d293f35cSEdward Tomasz Napierala 	return (kern_listen(td, args->s, args->backlog));
599c21dee17SSøren Schmidt }
600c21dee17SSøren Schmidt 
60101e0ffbaSAlexander Leidinger static int
602c8f37d61SDmitry Chagin linux_accept_common(struct thread *td, int s, l_uintptr_t addr,
603f83427b8SDmitry Chagin     l_uintptr_t namelen, int flags)
604c21dee17SSøren Schmidt {
605d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
606d5368bf3SDmitry Chagin 	struct sockaddr *sa;
607fc4b98fbSDmitry Chagin 	struct file *fp;
608d5368bf3SDmitry Chagin 	int bflags, len;
609d5368bf3SDmitry Chagin 	struct socket *so;
610fc4b98fbSDmitry Chagin 	int error, error1;
61193e694c9SDmitry Chagin 
612d5368bf3SDmitry Chagin 	bflags = 0;
613d5368bf3SDmitry Chagin 	error = linux_set_socket_flags(flags, &bflags);
6144cf10e29SDmitry Chagin 	if (error != 0)
6154cf10e29SDmitry Chagin 		return (error);
616d5368bf3SDmitry Chagin 
617d5368bf3SDmitry Chagin 	sa = NULL;
618d5368bf3SDmitry Chagin 	if (PTRIN(addr) == NULL) {
619d5368bf3SDmitry Chagin 		len = 0;
620d5368bf3SDmitry Chagin 		error = kern_accept4(td, s, NULL, NULL, bflags, NULL);
621d5368bf3SDmitry Chagin 	} else {
622d5368bf3SDmitry Chagin 		error = copyin(PTRIN(namelen), &len, sizeof(len));
623d5368bf3SDmitry Chagin 		if (error != 0)
624d5368bf3SDmitry Chagin 			return (error);
625d5368bf3SDmitry Chagin 		if (len < 0)
626d4b7423fSAlexander Leidinger 			return (EINVAL);
627d5368bf3SDmitry Chagin 		error = kern_accept4(td, s, &sa, &len, bflags, &fp);
628d5368bf3SDmitry Chagin 		if (error == 0)
629d5368bf3SDmitry Chagin 			fdrop(fp, td);
630d5368bf3SDmitry Chagin 	}
631d5368bf3SDmitry Chagin 
632d5368bf3SDmitry Chagin 	if (error != 0) {
633d5368bf3SDmitry Chagin 		/*
634d5368bf3SDmitry Chagin 		 * XXX. This is wrong, different sockaddr structures
635d5368bf3SDmitry Chagin 		 * have different sizes.
636d5368bf3SDmitry Chagin 		 */
637d5368bf3SDmitry Chagin 		if (error == EFAULT && namelen != sizeof(struct sockaddr_in))
638d5368bf3SDmitry Chagin 		{
639d5368bf3SDmitry Chagin 			error = EINVAL;
640d5368bf3SDmitry Chagin 			goto out;
641d5368bf3SDmitry Chagin 		}
642fc4b98fbSDmitry Chagin 		if (error == EINVAL) {
643cbd92ce6SMatt Macy 			error1 = getsock_cap(td, s, &cap_accept_rights, &fp, NULL, NULL);
644d5368bf3SDmitry Chagin 			if (error1 != 0) {
645d5368bf3SDmitry Chagin 				error = error1;
646d5368bf3SDmitry Chagin 				goto out;
647d5368bf3SDmitry Chagin 			}
64891f514e4SDmitry Chagin 			so = fp->f_data;
649d5368bf3SDmitry Chagin 			if (so->so_type == SOCK_DGRAM)
650d5368bf3SDmitry Chagin 				error = EOPNOTSUPP;
65191f514e4SDmitry Chagin 			fdrop(fp, td);
65291f514e4SDmitry Chagin 		}
653d5368bf3SDmitry Chagin 		goto out;
654d4b7423fSAlexander Leidinger 	}
655d5368bf3SDmitry Chagin 
656d5368bf3SDmitry Chagin 	if (len != 0 && error == 0) {
657d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
658d5368bf3SDmitry Chagin 		if (error == 0)
659d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(addr), len);
660d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
661d5368bf3SDmitry Chagin 	}
662d5368bf3SDmitry Chagin 
663d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
664d5368bf3SDmitry Chagin 
665d5368bf3SDmitry Chagin out:
666dddb7e7fSDmitry Chagin 	if (error != 0) {
66793e694c9SDmitry Chagin 		(void)kern_close(td, td->td_retval[0]);
66893e694c9SDmitry Chagin 		td->td_retval[0] = 0;
66993e694c9SDmitry Chagin 	}
67093e694c9SDmitry Chagin 	return (error);
671c21dee17SSøren Schmidt }
672c21dee17SSøren Schmidt 
673a12b9b3dSDmitry Chagin int
674c8f37d61SDmitry Chagin linux_accept(struct thread *td, struct linux_accept_args *args)
675c8f37d61SDmitry Chagin {
676c8f37d61SDmitry Chagin 
677c8f37d61SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
678f83427b8SDmitry Chagin 	    args->namelen, 0));
679c8f37d61SDmitry Chagin }
680c8f37d61SDmitry Chagin 
681a12b9b3dSDmitry Chagin int
682f8cd0af2SDmitry Chagin linux_accept4(struct thread *td, struct linux_accept4_args *args)
683f8cd0af2SDmitry Chagin {
684f8cd0af2SDmitry Chagin 
685f8cd0af2SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
686f8cd0af2SDmitry Chagin 	    args->namelen, args->flags));
687f8cd0af2SDmitry Chagin }
688f8cd0af2SDmitry Chagin 
689a12b9b3dSDmitry Chagin int
690b40ce416SJulian Elischer linux_getsockname(struct thread *td, struct linux_getsockname_args *args)
691c21dee17SSøren Schmidt {
692d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
693d5368bf3SDmitry Chagin 	struct sockaddr *sa;
694d5368bf3SDmitry Chagin 	int len, error;
695c21dee17SSøren Schmidt 
696d5368bf3SDmitry Chagin 	error = copyin(PTRIN(args->namelen), &len, sizeof(len));
697dddb7e7fSDmitry Chagin 	if (error != 0)
698ca26842eSHajimu UMEMOTO 		return (error);
699d5368bf3SDmitry Chagin 
700d5368bf3SDmitry Chagin 	error = kern_getsockname(td, args->s, &sa, &len);
701d5368bf3SDmitry Chagin 	if (error != 0)
702d5368bf3SDmitry Chagin 		return (error);
703d5368bf3SDmitry Chagin 
704d5368bf3SDmitry Chagin 	if (len != 0) {
705d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
706d5368bf3SDmitry Chagin 		if (error == 0)
707d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->addr),
708d5368bf3SDmitry Chagin 			    len);
709d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
710d5368bf3SDmitry Chagin 	}
711d5368bf3SDmitry Chagin 
712d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
713d5368bf3SDmitry Chagin 	if (error == 0)
714d5368bf3SDmitry Chagin 		error = copyout(&len, PTRIN(args->namelen), sizeof(len));
715d5368bf3SDmitry Chagin 	return (error);
716c21dee17SSøren Schmidt }
717c21dee17SSøren Schmidt 
718a12b9b3dSDmitry Chagin int
719b40ce416SJulian Elischer linux_getpeername(struct thread *td, struct linux_getpeername_args *args)
720c21dee17SSøren Schmidt {
721d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
722d5368bf3SDmitry Chagin 	struct sockaddr *sa;
723d5368bf3SDmitry Chagin 	int len, error;
724c21dee17SSøren Schmidt 
725d5368bf3SDmitry Chagin 	error = copyin(PTRIN(args->namelen), &len, sizeof(len));
726dddb7e7fSDmitry Chagin 	if (error != 0)
727ca26842eSHajimu UMEMOTO 		return (error);
728caaad873SDmitry Chagin 	if (len < 0)
729caaad873SDmitry Chagin 		return (EINVAL);
730d5368bf3SDmitry Chagin 
731d5368bf3SDmitry Chagin 	error = kern_getpeername(td, args->s, &sa, &len);
732d5368bf3SDmitry Chagin 	if (error != 0)
733d5368bf3SDmitry Chagin 		return (error);
734d5368bf3SDmitry Chagin 
735d5368bf3SDmitry Chagin 	if (len != 0) {
736d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
737d5368bf3SDmitry Chagin 		if (error == 0)
738d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->addr),
739d5368bf3SDmitry Chagin 			    len);
740d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
741d5368bf3SDmitry Chagin 	}
742d5368bf3SDmitry Chagin 
743d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
744d5368bf3SDmitry Chagin 	if (error == 0)
745d5368bf3SDmitry Chagin 		error = copyout(&len, PTRIN(args->namelen), sizeof(len));
746d5368bf3SDmitry Chagin 	return (error);
747c21dee17SSøren Schmidt }
748c21dee17SSøren Schmidt 
749a12b9b3dSDmitry Chagin int
750b40ce416SJulian Elischer linux_socketpair(struct thread *td, struct linux_socketpair_args *args)
751c21dee17SSøren Schmidt {
752ef04503dSPeter Wemm 	struct socketpair_args /* {
753c21dee17SSøren Schmidt 		int domain;
754c21dee17SSøren Schmidt 		int type;
755c21dee17SSøren Schmidt 		int protocol;
756c21dee17SSøren Schmidt 		int *rsv;
757ef04503dSPeter Wemm 	} */ bsd_args;
7584cf10e29SDmitry Chagin 	int error;
759c21dee17SSøren Schmidt 
760745aaef5SKonstantin Belousov 	bsd_args.domain = linux_to_bsd_domain(args->domain);
7611a52a4abSDmitry Chagin 	if (bsd_args.domain != PF_LOCAL)
7621a52a4abSDmitry Chagin 		return (EAFNOSUPPORT);
76339253cf9SDmitry Chagin 	bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
76439253cf9SDmitry Chagin 	if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
76539253cf9SDmitry Chagin 		return (EINVAL);
7664cf10e29SDmitry Chagin 	error = linux_set_socket_flags(args->type & ~LINUX_SOCK_TYPE_MASK,
7674cf10e29SDmitry Chagin 		&bsd_args.type);
7684cf10e29SDmitry Chagin 	if (error != 0)
7694cf10e29SDmitry Chagin 		return (error);
7701a52a4abSDmitry Chagin 	if (args->protocol != 0 && args->protocol != PF_UNIX)
7711a52a4abSDmitry Chagin 
7721a52a4abSDmitry Chagin 		/*
7731a52a4abSDmitry Chagin 		 * Use of PF_UNIX as protocol argument is not right,
7741a52a4abSDmitry Chagin 		 * but Linux does it.
7751a52a4abSDmitry Chagin 		 * Do not map PF_UNIX as its Linux value is identical
7761a52a4abSDmitry Chagin 		 * to FreeBSD one.
7771a52a4abSDmitry Chagin 		 */
7781a52a4abSDmitry Chagin 		return (EPROTONOSUPPORT);
77940092d93SDmitry Chagin 	else
7801a52a4abSDmitry Chagin 		bsd_args.protocol = 0;
781745aaef5SKonstantin Belousov 	bsd_args.rsv = (int *)PTRIN(args->rsv);
7824cf10e29SDmitry Chagin 	return (sys_socketpair(td, &bsd_args));
783c21dee17SSøren Schmidt }
784c21dee17SSøren Schmidt 
785a12b9b3dSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
78601e0ffbaSAlexander Leidinger struct linux_send_args {
78756fba8e6SDmitry Chagin 	register_t s;
78856fba8e6SDmitry Chagin 	register_t msg;
78956fba8e6SDmitry Chagin 	register_t len;
79056fba8e6SDmitry Chagin 	register_t flags;
791c21dee17SSøren Schmidt };
792c21dee17SSøren Schmidt 
79301e0ffbaSAlexander Leidinger static int
794b40ce416SJulian Elischer linux_send(struct thread *td, struct linux_send_args *args)
795c21dee17SSøren Schmidt {
79687d72a8fSPoul-Henning Kamp 	struct sendto_args /* {
797c21dee17SSøren Schmidt 		int s;
798c21dee17SSøren Schmidt 		caddr_t buf;
799044af7c3SJonathan Mini 		int len;
800c21dee17SSøren Schmidt 		int flags;
80187d72a8fSPoul-Henning Kamp 		caddr_t to;
80287d72a8fSPoul-Henning Kamp 		int tolen;
803ef04503dSPeter Wemm 	} */ bsd_args;
804aa288712SDmitry Chagin 	struct file *fp;
805aa288712SDmitry Chagin 	int error, fflag;
806c21dee17SSøren Schmidt 
807745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
808745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
809745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
810745aaef5SKonstantin Belousov 	bsd_args.flags = args->flags;
81187d72a8fSPoul-Henning Kamp 	bsd_args.to = NULL;
81287d72a8fSPoul-Henning Kamp 	bsd_args.tolen = 0;
813aa288712SDmitry Chagin 	error = sys_sendto(td, &bsd_args);
814aa288712SDmitry Chagin 	if (error == ENOTCONN) {
815aa288712SDmitry Chagin 		/*
816aa288712SDmitry Chagin 		 * Linux doesn't return ENOTCONN for non-blocking sockets.
817aa288712SDmitry Chagin 		 * Instead it returns the EAGAIN.
818aa288712SDmitry Chagin 		 */
819aa288712SDmitry Chagin 		error = getsock_cap(td, args->s, &cap_send_rights, &fp,
820aa288712SDmitry Chagin 		    &fflag, NULL);
821aa288712SDmitry Chagin 		if (error == 0) {
822aa288712SDmitry Chagin 			if (fflag & FNONBLOCK)
823aa288712SDmitry Chagin 				error = EAGAIN;
824aa288712SDmitry Chagin 			fdrop(fp, td);
825aa288712SDmitry Chagin 		}
826aa288712SDmitry Chagin 	}
827aa288712SDmitry Chagin 	return (error);
828c21dee17SSøren Schmidt }
829c21dee17SSøren Schmidt 
83001e0ffbaSAlexander Leidinger struct linux_recv_args {
83156fba8e6SDmitry Chagin 	register_t s;
83256fba8e6SDmitry Chagin 	register_t msg;
83356fba8e6SDmitry Chagin 	register_t len;
83456fba8e6SDmitry Chagin 	register_t flags;
835c21dee17SSøren Schmidt };
836c21dee17SSøren Schmidt 
83701e0ffbaSAlexander Leidinger static int
838b40ce416SJulian Elischer linux_recv(struct thread *td, struct linux_recv_args *args)
839c21dee17SSøren Schmidt {
84087d72a8fSPoul-Henning Kamp 	struct recvfrom_args /* {
841c21dee17SSøren Schmidt 		int s;
842c21dee17SSøren Schmidt 		caddr_t buf;
843c21dee17SSøren Schmidt 		int len;
844c21dee17SSøren Schmidt 		int flags;
84587d72a8fSPoul-Henning Kamp 		struct sockaddr *from;
84687d72a8fSPoul-Henning Kamp 		socklen_t fromlenaddr;
847ef04503dSPeter Wemm 	} */ bsd_args;
848c21dee17SSøren Schmidt 
849745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
850745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
851745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
8523980a435SDmitry Chagin 	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
85387d72a8fSPoul-Henning Kamp 	bsd_args.from = NULL;
85487d72a8fSPoul-Henning Kamp 	bsd_args.fromlenaddr = 0;
8558451d0ddSKip Macy 	return (sys_recvfrom(td, &bsd_args));
856c21dee17SSøren Schmidt }
857a12b9b3dSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
858c21dee17SSøren Schmidt 
859a12b9b3dSDmitry Chagin int
860b40ce416SJulian Elischer linux_sendto(struct thread *td, struct linux_sendto_args *args)
861c21dee17SSøren Schmidt {
8625a8a13e0SDavid Malone 	struct msghdr msg;
8635a8a13e0SDavid Malone 	struct iovec aiov;
864c21dee17SSøren Schmidt 
865745aaef5SKonstantin Belousov 	if (linux_check_hdrincl(td, args->s) == 0)
866f2477ae1SMike Smith 		/* IP_HDRINCL set, tweak the packet before sending */
867745aaef5SKonstantin Belousov 		return (linux_sendto_hdrincl(td, args));
868f2477ae1SMike Smith 
869745aaef5SKonstantin Belousov 	msg.msg_name = PTRIN(args->to);
870745aaef5SKonstantin Belousov 	msg.msg_namelen = args->tolen;
8715a8a13e0SDavid Malone 	msg.msg_iov = &aiov;
8725a8a13e0SDavid Malone 	msg.msg_iovlen = 1;
8735a8a13e0SDavid Malone 	msg.msg_control = NULL;
8745a8a13e0SDavid Malone 	msg.msg_flags = 0;
875745aaef5SKonstantin Belousov 	aiov.iov_base = PTRIN(args->msg);
876745aaef5SKonstantin Belousov 	aiov.iov_len = args->len;
877e667ee63SDmitry Chagin 	return (linux_sendit(td, args->s, &msg, args->flags, NULL,
878e667ee63SDmitry Chagin 	    UIO_USERSPACE));
879c21dee17SSøren Schmidt }
880c21dee17SSøren Schmidt 
881a12b9b3dSDmitry Chagin int
882b40ce416SJulian Elischer linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
883c21dee17SSøren Schmidt {
884d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
885d5368bf3SDmitry Chagin 	struct sockaddr *sa;
8869599b0ecSDmitry Chagin 	struct msghdr msg;
8879599b0ecSDmitry Chagin 	struct iovec aiov;
8883a49978fSDmitry Chagin 	int error, fromlen;
889c21dee17SSøren Schmidt 
8909599b0ecSDmitry Chagin 	if (PTRIN(args->fromlen) != NULL) {
8913a49978fSDmitry Chagin 		error = copyin(PTRIN(args->fromlen), &fromlen,
8923a49978fSDmitry Chagin 		    sizeof(fromlen));
8939599b0ecSDmitry Chagin 		if (error != 0)
8943f3a4815SMarcel Moolenaar 			return (error);
8953a49978fSDmitry Chagin 		if (fromlen < 0)
8963a49978fSDmitry Chagin 			return (EINVAL);
897d5368bf3SDmitry Chagin 		sa = malloc(fromlen, M_SONAME, M_WAITOK);
898d5368bf3SDmitry Chagin 	} else {
899d5368bf3SDmitry Chagin 		fromlen = 0;
900d5368bf3SDmitry Chagin 		sa = NULL;
901d5368bf3SDmitry Chagin 	}
9029599b0ecSDmitry Chagin 
903d5368bf3SDmitry Chagin 	msg.msg_name = sa;
904d5368bf3SDmitry Chagin 	msg.msg_namelen = fromlen;
9059599b0ecSDmitry Chagin 	msg.msg_iov = &aiov;
9069599b0ecSDmitry Chagin 	msg.msg_iovlen = 1;
9079599b0ecSDmitry Chagin 	aiov.iov_base = PTRIN(args->buf);
9089599b0ecSDmitry Chagin 	aiov.iov_len = args->len;
9099599b0ecSDmitry Chagin 	msg.msg_control = 0;
9109599b0ecSDmitry Chagin 	msg.msg_flags = linux_to_bsd_msg_flags(args->flags);
9119599b0ecSDmitry Chagin 
912d5368bf3SDmitry Chagin 	error = kern_recvit(td, args->s, &msg, UIO_SYSSPACE, NULL);
9139599b0ecSDmitry Chagin 	if (error != 0)
914dcd62418SDmitry Chagin 		goto out;
9159599b0ecSDmitry Chagin 
9169599b0ecSDmitry Chagin 	if (PTRIN(args->from) != NULL) {
917d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, msg.msg_namelen);
918d5368bf3SDmitry Chagin 		if (error == 0)
919d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->from),
920d5368bf3SDmitry Chagin 			    msg.msg_namelen);
921d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
922ca26842eSHajimu UMEMOTO 	}
9239599b0ecSDmitry Chagin 
924d5368bf3SDmitry Chagin 	if (error == 0 && PTRIN(args->fromlen) != NULL)
9259599b0ecSDmitry Chagin 		error = copyout(&msg.msg_namelen, PTRIN(args->fromlen),
9269599b0ecSDmitry Chagin 		    sizeof(msg.msg_namelen));
927dcd62418SDmitry Chagin out:
928d5368bf3SDmitry Chagin 	free(sa, M_SONAME);
9299599b0ecSDmitry Chagin 	return (error);
930ca26842eSHajimu UMEMOTO }
931ca26842eSHajimu UMEMOTO 
932e1ff74c0SDmitry Chagin static int
933e1ff74c0SDmitry Chagin linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
934e1ff74c0SDmitry Chagin     l_uint flags)
935ca26842eSHajimu UMEMOTO {
93674f5d680SKonstantin Belousov 	struct cmsghdr *cmsg;
93774f5d680SKonstantin Belousov 	struct mbuf *control;
938ca26842eSHajimu UMEMOTO 	struct msghdr msg;
93974f5d680SKonstantin Belousov 	struct l_cmsghdr linux_cmsg;
94074f5d680SKonstantin Belousov 	struct l_cmsghdr *ptr_cmsg;
94174f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
942552afd9cSPoul-Henning Kamp 	struct iovec *iov;
94374f5d680SKonstantin Belousov 	socklen_t datalen;
944605da56bSAndriy Gapon 	struct sockaddr *sa;
9451410bfe1SDmitry Chagin 	struct socket *so;
946605da56bSAndriy Gapon 	sa_family_t sa_family;
9471410bfe1SDmitry Chagin 	struct file *fp;
94874f5d680SKonstantin Belousov 	void *data;
949e3b385fcSTijl Coosemans 	l_size_t len;
9507df0e7beSTijl Coosemans 	l_size_t clen;
9511410bfe1SDmitry Chagin 	int error, fflag;
952ca26842eSHajimu UMEMOTO 
953e1ff74c0SDmitry Chagin 	error = copyin(msghdr, &linux_msg, sizeof(linux_msg));
954e1ff74c0SDmitry Chagin 	if (error != 0)
95574f5d680SKonstantin Belousov 		return (error);
956d72a6158SRobert Watson 
957d72a6158SRobert Watson 	/*
958d72a6158SRobert Watson 	 * Some Linux applications (ping) define a non-NULL control data
959d72a6158SRobert Watson 	 * pointer, but a msg_controllen of 0, which is not allowed in the
960d72a6158SRobert Watson 	 * FreeBSD system call interface.  NULL the msg_control pointer in
961d72a6158SRobert Watson 	 * order to handle this case.  This should be checked, but allows the
962d72a6158SRobert Watson 	 * Linux ping to work.
963d72a6158SRobert Watson 	 */
964605da56bSAndriy Gapon 	if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0)
965605da56bSAndriy Gapon 		linux_msg.msg_control = PTROUT(NULL);
966605da56bSAndriy Gapon 
967605da56bSAndriy Gapon 	error = linux_to_bsd_msghdr(&msg, &linux_msg);
968e1ff74c0SDmitry Chagin 	if (error != 0)
969605da56bSAndriy Gapon 		return (error);
97074f5d680SKonstantin Belousov 
97174f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
97274f5d680SKonstantin Belousov 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
97374f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
97474f5d680SKonstantin Belousov #else
975552afd9cSPoul-Henning Kamp 	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
97674f5d680SKonstantin Belousov #endif
977e1ff74c0SDmitry Chagin 	if (error != 0)
978552afd9cSPoul-Henning Kamp 		return (error);
97974f5d680SKonstantin Belousov 
980605da56bSAndriy Gapon 	control = NULL;
981605da56bSAndriy Gapon 
982e1ff74c0SDmitry Chagin 	error = kern_getsockname(td, s, &sa, &datalen);
983e1ff74c0SDmitry Chagin 	if (error != 0)
984605da56bSAndriy Gapon 		goto bad;
985605da56bSAndriy Gapon 	sa_family = sa->sa_family;
986605da56bSAndriy Gapon 	free(sa, M_SONAME);
987605da56bSAndriy Gapon 
9881410bfe1SDmitry Chagin 	if (flags & LINUX_MSG_OOB) {
9891410bfe1SDmitry Chagin 		error = EOPNOTSUPP;
9901410bfe1SDmitry Chagin 		if (sa_family == AF_UNIX)
9911410bfe1SDmitry Chagin 			goto bad;
9921410bfe1SDmitry Chagin 
9931410bfe1SDmitry Chagin 		error = getsock_cap(td, s, &cap_send_rights, &fp,
9941410bfe1SDmitry Chagin 		    &fflag, NULL);
9951410bfe1SDmitry Chagin 		if (error != 0)
9961410bfe1SDmitry Chagin 			goto bad;
9971410bfe1SDmitry Chagin 		so = fp->f_data;
9981410bfe1SDmitry Chagin 		if (so->so_type != SOCK_STREAM)
9991410bfe1SDmitry Chagin 			error = EOPNOTSUPP;
10001410bfe1SDmitry Chagin 		fdrop(fp, td);
10011410bfe1SDmitry Chagin 		if (error != 0)
10021410bfe1SDmitry Chagin 			goto bad;
10031410bfe1SDmitry Chagin 	}
10041410bfe1SDmitry Chagin 
10051410bfe1SDmitry Chagin 	if (linux_msg.msg_controllen >= sizeof(struct l_cmsghdr)) {
10061410bfe1SDmitry Chagin 
100774f5d680SKonstantin Belousov 		error = ENOBUFS;
1008eb1b1807SGleb Smirnoff 		control = m_get(M_WAITOK, MT_CONTROL);
1009e3b385fcSTijl Coosemans 		MCLGET(control, M_WAITOK);
1010e3b385fcSTijl Coosemans 		data = mtod(control, void *);
1011e3b385fcSTijl Coosemans 		datalen = 0;
101274f5d680SKonstantin Belousov 
10137df0e7beSTijl Coosemans 		ptr_cmsg = PTRIN(linux_msg.msg_control);
10147df0e7beSTijl Coosemans 		clen = linux_msg.msg_controllen;
101574f5d680SKonstantin Belousov 		do {
101674f5d680SKonstantin Belousov 			error = copyin(ptr_cmsg, &linux_cmsg,
101774f5d680SKonstantin Belousov 			    sizeof(struct l_cmsghdr));
1018e1ff74c0SDmitry Chagin 			if (error != 0)
101974f5d680SKonstantin Belousov 				goto bad;
102074f5d680SKonstantin Belousov 
102174f5d680SKonstantin Belousov 			error = EINVAL;
10227df0e7beSTijl Coosemans 			if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr) ||
10237df0e7beSTijl Coosemans 			    linux_cmsg.cmsg_len > clen)
102474f5d680SKonstantin Belousov 				goto bad;
102574f5d680SKonstantin Belousov 
1026e3b385fcSTijl Coosemans 			if (datalen + CMSG_HDRSZ > MCLBYTES)
1027e3b385fcSTijl Coosemans 				goto bad;
1028e3b385fcSTijl Coosemans 
102974f5d680SKonstantin Belousov 			/*
1030605da56bSAndriy Gapon 			 * Now we support only SCM_RIGHTS and SCM_CRED,
1031605da56bSAndriy Gapon 			 * so return EINVAL in any other cmsg_type
103274f5d680SKonstantin Belousov 			 */
1033e3b385fcSTijl Coosemans 			cmsg = data;
1034605da56bSAndriy Gapon 			cmsg->cmsg_type =
1035605da56bSAndriy Gapon 			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
103674f5d680SKonstantin Belousov 			cmsg->cmsg_level =
103774f5d680SKonstantin Belousov 			    linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
1038605da56bSAndriy Gapon 			if (cmsg->cmsg_type == -1
1039605da56bSAndriy Gapon 			    || cmsg->cmsg_level != SOL_SOCKET)
1040605da56bSAndriy Gapon 				goto bad;
104174f5d680SKonstantin Belousov 
1042605da56bSAndriy Gapon 			/*
1043605da56bSAndriy Gapon 			 * Some applications (e.g. pulseaudio) attempt to
1044605da56bSAndriy Gapon 			 * send ancillary data even if the underlying protocol
1045605da56bSAndriy Gapon 			 * doesn't support it which is not allowed in the
1046605da56bSAndriy Gapon 			 * FreeBSD system call interface.
1047605da56bSAndriy Gapon 			 */
1048605da56bSAndriy Gapon 			if (sa_family != AF_UNIX)
1049605da56bSAndriy Gapon 				continue;
1050605da56bSAndriy Gapon 
1051e3b385fcSTijl Coosemans 			if (cmsg->cmsg_type == SCM_CREDS) {
1052e3b385fcSTijl Coosemans 				len = sizeof(struct cmsgcred);
1053e3b385fcSTijl Coosemans 				if (datalen + CMSG_SPACE(len) > MCLBYTES)
1054e3b385fcSTijl Coosemans 					goto bad;
1055605da56bSAndriy Gapon 
1056605da56bSAndriy Gapon 				/*
1057605da56bSAndriy Gapon 				 * The lower levels will fill in the structure
1058605da56bSAndriy Gapon 				 */
1059e3b385fcSTijl Coosemans 				memset(CMSG_DATA(data), 0, len);
1060e3b385fcSTijl Coosemans 			} else {
1061e3b385fcSTijl Coosemans 				len = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
1062e3b385fcSTijl Coosemans 				if (datalen + CMSG_SPACE(len) < datalen ||
1063e3b385fcSTijl Coosemans 				    datalen + CMSG_SPACE(len) > MCLBYTES)
1064e3b385fcSTijl Coosemans 					goto bad;
1065e3b385fcSTijl Coosemans 
1066e3b385fcSTijl Coosemans 				error = copyin(LINUX_CMSG_DATA(ptr_cmsg),
1067e3b385fcSTijl Coosemans 				    CMSG_DATA(data), len);
1068e3b385fcSTijl Coosemans 				if (error != 0)
1069e3b385fcSTijl Coosemans 					goto bad;
1070605da56bSAndriy Gapon 			}
1071605da56bSAndriy Gapon 
1072e3b385fcSTijl Coosemans 			cmsg->cmsg_len = CMSG_LEN(len);
1073e3b385fcSTijl Coosemans 			data = (char *)data + CMSG_SPACE(len);
1074e3b385fcSTijl Coosemans 			datalen += CMSG_SPACE(len);
10757df0e7beSTijl Coosemans 
10767df0e7beSTijl Coosemans 			if (clen <= LINUX_CMSG_ALIGN(linux_cmsg.cmsg_len))
10777df0e7beSTijl Coosemans 				break;
10787df0e7beSTijl Coosemans 
10797df0e7beSTijl Coosemans 			clen -= LINUX_CMSG_ALIGN(linux_cmsg.cmsg_len);
10807df0e7beSTijl Coosemans 			ptr_cmsg = (struct l_cmsghdr *)((char *)ptr_cmsg +
10817df0e7beSTijl Coosemans 			    LINUX_CMSG_ALIGN(linux_cmsg.cmsg_len));
10827df0e7beSTijl Coosemans 		} while(clen >= sizeof(struct l_cmsghdr));
1083605da56bSAndriy Gapon 
1084e3b385fcSTijl Coosemans 		control->m_len = datalen;
1085e3b385fcSTijl Coosemans 		if (datalen == 0) {
1086605da56bSAndriy Gapon 			m_freem(control);
108774f5d680SKonstantin Belousov 			control = NULL;
1088605da56bSAndriy Gapon 		}
108974f5d680SKonstantin Belousov 	}
109074f5d680SKonstantin Belousov 
10915a8a13e0SDavid Malone 	msg.msg_iov = iov;
10925a8a13e0SDavid Malone 	msg.msg_flags = 0;
1093e1ff74c0SDmitry Chagin 	error = linux_sendit(td, s, &msg, flags, control, UIO_USERSPACE);
109467968b35SDmitry Chagin 	control = NULL;
109574f5d680SKonstantin Belousov 
109674f5d680SKonstantin Belousov bad:
10974f65e9cfSDmitry Chagin 	m_freem(control);
1098552afd9cSPoul-Henning Kamp 	free(iov, M_IOV);
1099ca26842eSHajimu UMEMOTO 	return (error);
1100c21dee17SSøren Schmidt }
1101c21dee17SSøren Schmidt 
1102a12b9b3dSDmitry Chagin int
1103e1ff74c0SDmitry Chagin linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
1104e1ff74c0SDmitry Chagin {
1105e1ff74c0SDmitry Chagin 
1106e1ff74c0SDmitry Chagin 	return (linux_sendmsg_common(td, args->s, PTRIN(args->msg),
1107e1ff74c0SDmitry Chagin 	    args->flags));
1108e1ff74c0SDmitry Chagin }
1109e1ff74c0SDmitry Chagin 
1110e1ff74c0SDmitry Chagin int
1111e1ff74c0SDmitry Chagin linux_sendmmsg(struct thread *td, struct linux_sendmmsg_args *args)
1112e1ff74c0SDmitry Chagin {
1113e1ff74c0SDmitry Chagin 	struct l_mmsghdr *msg;
1114e1ff74c0SDmitry Chagin 	l_uint retval;
1115e1ff74c0SDmitry Chagin 	int error, datagrams;
1116e1ff74c0SDmitry Chagin 
1117e1ff74c0SDmitry Chagin 	if (args->vlen > UIO_MAXIOV)
1118e1ff74c0SDmitry Chagin 		args->vlen = UIO_MAXIOV;
1119e1ff74c0SDmitry Chagin 
1120e1ff74c0SDmitry Chagin 	msg = PTRIN(args->msg);
1121e1ff74c0SDmitry Chagin 	datagrams = 0;
1122e1ff74c0SDmitry Chagin 	while (datagrams < args->vlen) {
1123e1ff74c0SDmitry Chagin 		error = linux_sendmsg_common(td, args->s, &msg->msg_hdr,
1124e1ff74c0SDmitry Chagin 		    args->flags);
1125e1ff74c0SDmitry Chagin 		if (error != 0)
1126e1ff74c0SDmitry Chagin 			break;
1127e1ff74c0SDmitry Chagin 
1128e1ff74c0SDmitry Chagin 		retval = td->td_retval[0];
1129e1ff74c0SDmitry Chagin 		error = copyout(&retval, &msg->msg_len, sizeof(msg->msg_len));
1130e1ff74c0SDmitry Chagin 		if (error != 0)
1131e1ff74c0SDmitry Chagin 			break;
1132e1ff74c0SDmitry Chagin 		++msg;
1133e1ff74c0SDmitry Chagin 		++datagrams;
1134e1ff74c0SDmitry Chagin 	}
1135e1ff74c0SDmitry Chagin 	if (error == 0)
1136e1ff74c0SDmitry Chagin 		td->td_retval[0] = datagrams;
1137e1ff74c0SDmitry Chagin 	return (error);
1138e1ff74c0SDmitry Chagin }
1139e1ff74c0SDmitry Chagin 
1140e1ff74c0SDmitry Chagin static int
1141e1ff74c0SDmitry Chagin linux_recvmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
1142e1ff74c0SDmitry Chagin     l_uint flags, struct msghdr *msg)
114340dbba57SAssar Westerlund {
114474f5d680SKonstantin Belousov 	struct cmsghdr *cm;
1145605da56bSAndriy Gapon 	struct cmsgcred *cmcred;
114674f5d680SKonstantin Belousov 	struct l_cmsghdr *linux_cmsg = NULL;
1147605da56bSAndriy Gapon 	struct l_ucred linux_ucred;
1148c7902fbeSMark Johnston 	socklen_t datalen, maxlen, outlen;
114974f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
115074f5d680SKonstantin Belousov 	struct iovec *iov, *uiov;
115174f5d680SKonstantin Belousov 	struct mbuf *control = NULL;
115274f5d680SKonstantin Belousov 	struct mbuf **controlp;
1153bbf392d5SDmitry Chagin 	struct timeval *ftmvl;
1154d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
1155d5368bf3SDmitry Chagin 	struct sockaddr *sa;
1156bbf392d5SDmitry Chagin 	l_timeval ltmvl;
115774f5d680SKonstantin Belousov 	caddr_t outbuf;
115874f5d680SKonstantin Belousov 	void *data;
11593a72bf04SDmitry Chagin 	int error, i, fd, fds, *fdp;
116040dbba57SAssar Westerlund 
1161e1ff74c0SDmitry Chagin 	error = copyin(msghdr, &linux_msg, sizeof(linux_msg));
1162e1ff74c0SDmitry Chagin 	if (error != 0)
1163ca26842eSHajimu UMEMOTO 		return (error);
1164ca26842eSHajimu UMEMOTO 
1165e1ff74c0SDmitry Chagin 	error = linux_to_bsd_msghdr(msg, &linux_msg);
1166e1ff74c0SDmitry Chagin 	if (error != 0)
116774f5d680SKonstantin Belousov 		return (error);
116874f5d680SKonstantin Belousov 
116974f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
1170e1ff74c0SDmitry Chagin 	error = linux32_copyiniov(PTRIN(msg->msg_iov), msg->msg_iovlen,
117174f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
117274f5d680SKonstantin Belousov #else
1173e1ff74c0SDmitry Chagin 	error = copyiniov(msg->msg_iov, msg->msg_iovlen, &iov, EMSGSIZE);
117474f5d680SKonstantin Belousov #endif
1175e1ff74c0SDmitry Chagin 	if (error != 0)
117674f5d680SKonstantin Belousov 		return (error);
117774f5d680SKonstantin Belousov 
1178e1ff74c0SDmitry Chagin 	if (msg->msg_name) {
1179d5368bf3SDmitry Chagin 		sa = malloc(msg->msg_namelen, M_SONAME, M_WAITOK);
1180d5368bf3SDmitry Chagin 		msg->msg_name = sa;
11818128cfc5SDmitry Chagin 	} else
11828128cfc5SDmitry Chagin 		sa = NULL;
118384b11cd8SMitsuru IWASAKI 
1184e1ff74c0SDmitry Chagin 	uiov = msg->msg_iov;
1185e1ff74c0SDmitry Chagin 	msg->msg_iov = iov;
1186e1ff74c0SDmitry Chagin 	controlp = (msg->msg_control != NULL) ? &control : NULL;
1187d5368bf3SDmitry Chagin 	error = kern_recvit(td, s, msg, UIO_SYSSPACE, controlp);
1188e1ff74c0SDmitry Chagin 	msg->msg_iov = uiov;
1189e1ff74c0SDmitry Chagin 	if (error != 0)
119074f5d680SKonstantin Belousov 		goto bad;
119174f5d680SKonstantin Belousov 
119257cb29a7SDmitry Chagin 	if (msg->msg_name) {
1193d5368bf3SDmitry Chagin 		msg->msg_name = PTRIN(linux_msg.msg_name);
1194d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, msg->msg_namelen);
1195d5368bf3SDmitry Chagin 		if (error == 0)
1196d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(msg->msg_name),
1197d5368bf3SDmitry Chagin 			    msg->msg_namelen);
1198d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
1199d5368bf3SDmitry Chagin 		if (error != 0)
1200d5368bf3SDmitry Chagin 			goto bad;
1201d5368bf3SDmitry Chagin 	}
1202d5368bf3SDmitry Chagin 
1203e1ff74c0SDmitry Chagin 	error = bsd_to_linux_msghdr(msg, &linux_msg);
1204e1ff74c0SDmitry Chagin 	if (error != 0)
120574f5d680SKonstantin Belousov 		goto bad;
120674f5d680SKonstantin Belousov 
1207c7902fbeSMark Johnston 	maxlen = linux_msg.msg_controllen;
1208c7902fbeSMark Johnston 	linux_msg.msg_controllen = 0;
1209605da56bSAndriy Gapon 	if (control) {
1210e0d3ea8cSDmitry Chagin 		linux_cmsg = malloc(L_CMSG_HDRSZ, M_LINUX, M_WAITOK | M_ZERO);
1211605da56bSAndriy Gapon 
1212e1ff74c0SDmitry Chagin 		msg->msg_control = mtod(control, struct cmsghdr *);
1213e1ff74c0SDmitry Chagin 		msg->msg_controllen = control->m_len;
1214605da56bSAndriy Gapon 
1215e1ff74c0SDmitry Chagin 		cm = CMSG_FIRSTHDR(msg);
1216c7902fbeSMark Johnston 		outbuf = PTRIN(linux_msg.msg_control);
1217c7902fbeSMark Johnston 		outlen = 0;
121874f5d680SKonstantin Belousov 		while (cm != NULL) {
1219605da56bSAndriy Gapon 			linux_cmsg->cmsg_type =
1220605da56bSAndriy Gapon 			    bsd_to_linux_cmsg_type(cm->cmsg_type);
1221605da56bSAndriy Gapon 			linux_cmsg->cmsg_level =
1222605da56bSAndriy Gapon 			    bsd_to_linux_sockopt_level(cm->cmsg_level);
1223c7902fbeSMark Johnston 			if (linux_cmsg->cmsg_type == -1 ||
1224c7902fbeSMark Johnston 			    cm->cmsg_level != SOL_SOCKET) {
122574f5d680SKonstantin Belousov 				error = EINVAL;
122674f5d680SKonstantin Belousov 				goto bad;
122774f5d680SKonstantin Belousov 			}
1228605da56bSAndriy Gapon 
122974f5d680SKonstantin Belousov 			data = CMSG_DATA(cm);
123074f5d680SKonstantin Belousov 			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
123174f5d680SKonstantin Belousov 
1232c7902fbeSMark Johnston 			switch (cm->cmsg_type) {
1233605da56bSAndriy Gapon 			case SCM_RIGHTS:
1234e1ff74c0SDmitry Chagin 				if (flags & LINUX_MSG_CMSG_CLOEXEC) {
1235605da56bSAndriy Gapon 					fds = datalen / sizeof(int);
1236605da56bSAndriy Gapon 					fdp = data;
1237605da56bSAndriy Gapon 					for (i = 0; i < fds; i++) {
1238605da56bSAndriy Gapon 						fd = *fdp++;
1239605da56bSAndriy Gapon 						(void)kern_fcntl(td, fd,
1240605da56bSAndriy Gapon 						    F_SETFD, FD_CLOEXEC);
1241605da56bSAndriy Gapon 					}
1242605da56bSAndriy Gapon 				}
1243605da56bSAndriy Gapon 				break;
1244605da56bSAndriy Gapon 
1245605da56bSAndriy Gapon 			case SCM_CREDS:
1246605da56bSAndriy Gapon 				/*
1247605da56bSAndriy Gapon 				 * Currently LOCAL_CREDS is never in
1248605da56bSAndriy Gapon 				 * effect for Linux so no need to worry
1249605da56bSAndriy Gapon 				 * about sockcred
1250605da56bSAndriy Gapon 				 */
1251605da56bSAndriy Gapon 				if (datalen != sizeof(*cmcred)) {
1252605da56bSAndriy Gapon 					error = EMSGSIZE;
1253605da56bSAndriy Gapon 					goto bad;
1254605da56bSAndriy Gapon 				}
1255605da56bSAndriy Gapon 				cmcred = (struct cmsgcred *)data;
1256605da56bSAndriy Gapon 				bzero(&linux_ucred, sizeof(linux_ucred));
1257605da56bSAndriy Gapon 				linux_ucred.pid = cmcred->cmcred_pid;
1258605da56bSAndriy Gapon 				linux_ucred.uid = cmcred->cmcred_uid;
1259605da56bSAndriy Gapon 				linux_ucred.gid = cmcred->cmcred_gid;
1260605da56bSAndriy Gapon 				data = &linux_ucred;
1261605da56bSAndriy Gapon 				datalen = sizeof(linux_ucred);
1262605da56bSAndriy Gapon 				break;
1263bbf392d5SDmitry Chagin 
1264bbf392d5SDmitry Chagin 			case SCM_TIMESTAMP:
1265bbf392d5SDmitry Chagin 				if (datalen != sizeof(struct timeval)) {
1266bbf392d5SDmitry Chagin 					error = EMSGSIZE;
1267bbf392d5SDmitry Chagin 					goto bad;
1268bbf392d5SDmitry Chagin 				}
1269bbf392d5SDmitry Chagin 				ftmvl = (struct timeval *)data;
1270bbf392d5SDmitry Chagin 				ltmvl.tv_sec = ftmvl->tv_sec;
1271bbf392d5SDmitry Chagin 				ltmvl.tv_usec = ftmvl->tv_usec;
1272bbf392d5SDmitry Chagin 				data = &ltmvl;
1273bbf392d5SDmitry Chagin 				datalen = sizeof(ltmvl);
1274bbf392d5SDmitry Chagin 				break;
1275605da56bSAndriy Gapon 			}
1276605da56bSAndriy Gapon 
1277c7902fbeSMark Johnston 			if (outlen + LINUX_CMSG_LEN(datalen) > maxlen) {
127874f5d680SKonstantin Belousov 				if (outlen == 0) {
127974f5d680SKonstantin Belousov 					error = EMSGSIZE;
128074f5d680SKonstantin Belousov 					goto bad;
128174f5d680SKonstantin Belousov 				} else {
1282c7902fbeSMark Johnston 					linux_msg.msg_flags |= LINUX_MSG_CTRUNC;
1283c7902fbeSMark Johnston 					m_dispose_extcontrolm(control);
128474f5d680SKonstantin Belousov 					goto out;
128574f5d680SKonstantin Belousov 				}
128674f5d680SKonstantin Belousov 			}
128774f5d680SKonstantin Belousov 
128874f5d680SKonstantin Belousov 			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
128974f5d680SKonstantin Belousov 
129074f5d680SKonstantin Belousov 			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
1291dddb7e7fSDmitry Chagin 			if (error != 0)
129274f5d680SKonstantin Belousov 				goto bad;
129374f5d680SKonstantin Belousov 			outbuf += L_CMSG_HDRSZ;
129474f5d680SKonstantin Belousov 
129574f5d680SKonstantin Belousov 			error = copyout(data, outbuf, datalen);
1296dddb7e7fSDmitry Chagin 			if (error != 0)
129774f5d680SKonstantin Belousov 				goto bad;
129874f5d680SKonstantin Belousov 
129974f5d680SKonstantin Belousov 			outbuf += LINUX_CMSG_ALIGN(datalen);
130074f5d680SKonstantin Belousov 			outlen += LINUX_CMSG_LEN(datalen);
130174f5d680SKonstantin Belousov 
1302e1ff74c0SDmitry Chagin 			cm = CMSG_NXTHDR(msg, cm);
130374f5d680SKonstantin Belousov 		}
1304c7902fbeSMark Johnston 		linux_msg.msg_controllen = outlen;
130574f5d680SKonstantin Belousov 	}
130674f5d680SKonstantin Belousov 
130774f5d680SKonstantin Belousov out:
1308e1ff74c0SDmitry Chagin 	error = copyout(&linux_msg, msghdr, sizeof(linux_msg));
130974f5d680SKonstantin Belousov 
131074f5d680SKonstantin Belousov bad:
1311c7902fbeSMark Johnston 	if (control != NULL) {
1312c7902fbeSMark Johnston 		if (error != 0)
1313c7902fbeSMark Johnston 			m_dispose_extcontrolm(control);
131474f5d680SKonstantin Belousov 		m_freem(control);
1315c7902fbeSMark Johnston 	}
1316c7902fbeSMark Johnston 	free(iov, M_IOV);
1317e0d3ea8cSDmitry Chagin 	free(linux_cmsg, M_LINUX);
13188128cfc5SDmitry Chagin 	free(sa, M_SONAME);
131974f5d680SKonstantin Belousov 
1320ca26842eSHajimu UMEMOTO 	return (error);
132140dbba57SAssar Westerlund }
132240dbba57SAssar Westerlund 
1323a12b9b3dSDmitry Chagin int
1324e1ff74c0SDmitry Chagin linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
1325e1ff74c0SDmitry Chagin {
1326e1ff74c0SDmitry Chagin 	struct msghdr bsd_msg;
1327e1ff74c0SDmitry Chagin 
1328e1ff74c0SDmitry Chagin 	return (linux_recvmsg_common(td, args->s, PTRIN(args->msg),
1329e1ff74c0SDmitry Chagin 	    args->flags, &bsd_msg));
1330e1ff74c0SDmitry Chagin }
1331e1ff74c0SDmitry Chagin 
1332e1ff74c0SDmitry Chagin int
1333e1ff74c0SDmitry Chagin linux_recvmmsg(struct thread *td, struct linux_recvmmsg_args *args)
1334e1ff74c0SDmitry Chagin {
1335e1ff74c0SDmitry Chagin 	struct l_mmsghdr *msg;
1336e1ff74c0SDmitry Chagin 	struct msghdr bsd_msg;
1337e1ff74c0SDmitry Chagin 	struct l_timespec lts;
1338e1ff74c0SDmitry Chagin 	struct timespec ts, tts;
1339e1ff74c0SDmitry Chagin 	l_uint retval;
1340e1ff74c0SDmitry Chagin 	int error, datagrams;
1341e1ff74c0SDmitry Chagin 
1342e1ff74c0SDmitry Chagin 	if (args->timeout) {
1343e1ff74c0SDmitry Chagin 		error = copyin(args->timeout, &lts, sizeof(struct l_timespec));
1344e1ff74c0SDmitry Chagin 		if (error != 0)
1345e1ff74c0SDmitry Chagin 			return (error);
1346e1ff74c0SDmitry Chagin 		error = linux_to_native_timespec(&ts, &lts);
1347e1ff74c0SDmitry Chagin 		if (error != 0)
1348e1ff74c0SDmitry Chagin 			return (error);
1349e1ff74c0SDmitry Chagin 		getnanotime(&tts);
13506040822cSAlan Somers 		timespecadd(&tts, &ts, &tts);
1351e1ff74c0SDmitry Chagin 	}
1352e1ff74c0SDmitry Chagin 
1353e1ff74c0SDmitry Chagin 	msg = PTRIN(args->msg);
1354e1ff74c0SDmitry Chagin 	datagrams = 0;
1355e1ff74c0SDmitry Chagin 	while (datagrams < args->vlen) {
1356e1ff74c0SDmitry Chagin 		error = linux_recvmsg_common(td, args->s, &msg->msg_hdr,
1357e1ff74c0SDmitry Chagin 		    args->flags & ~LINUX_MSG_WAITFORONE, &bsd_msg);
1358e1ff74c0SDmitry Chagin 		if (error != 0)
1359e1ff74c0SDmitry Chagin 			break;
1360e1ff74c0SDmitry Chagin 
1361e1ff74c0SDmitry Chagin 		retval = td->td_retval[0];
1362e1ff74c0SDmitry Chagin 		error = copyout(&retval, &msg->msg_len, sizeof(msg->msg_len));
1363e1ff74c0SDmitry Chagin 		if (error != 0)
1364e1ff74c0SDmitry Chagin 			break;
1365e1ff74c0SDmitry Chagin 		++msg;
1366e1ff74c0SDmitry Chagin 		++datagrams;
1367e1ff74c0SDmitry Chagin 
1368e1ff74c0SDmitry Chagin 		/*
1369e1ff74c0SDmitry Chagin 		 * MSG_WAITFORONE turns on MSG_DONTWAIT after one packet.
1370e1ff74c0SDmitry Chagin 		 */
1371e1ff74c0SDmitry Chagin 		if (args->flags & LINUX_MSG_WAITFORONE)
1372e1ff74c0SDmitry Chagin 			args->flags |= LINUX_MSG_DONTWAIT;
1373e1ff74c0SDmitry Chagin 
1374e1ff74c0SDmitry Chagin 		/*
1375e1ff74c0SDmitry Chagin 		 * See BUGS section of recvmmsg(2).
1376e1ff74c0SDmitry Chagin 		 */
1377e1ff74c0SDmitry Chagin 		if (args->timeout) {
1378e1ff74c0SDmitry Chagin 			getnanotime(&ts);
13796040822cSAlan Somers 			timespecsub(&ts, &tts, &ts);
1380e1ff74c0SDmitry Chagin 			if (!timespecisset(&ts) || ts.tv_sec > 0)
1381e1ff74c0SDmitry Chagin 				break;
1382e1ff74c0SDmitry Chagin 		}
1383e1ff74c0SDmitry Chagin 		/* Out of band data, return right away. */
1384e1ff74c0SDmitry Chagin 		if (bsd_msg.msg_flags & MSG_OOB)
1385e1ff74c0SDmitry Chagin 			break;
1386e1ff74c0SDmitry Chagin 	}
1387e1ff74c0SDmitry Chagin 	if (error == 0)
1388e1ff74c0SDmitry Chagin 		td->td_retval[0] = datagrams;
1389e1ff74c0SDmitry Chagin 	return (error);
1390e1ff74c0SDmitry Chagin }
1391e1ff74c0SDmitry Chagin 
1392e1ff74c0SDmitry Chagin int
1393b40ce416SJulian Elischer linux_shutdown(struct thread *td, struct linux_shutdown_args *args)
1394c21dee17SSøren Schmidt {
1395c21dee17SSøren Schmidt 
1396d293f35cSEdward Tomasz Napierala 	return (kern_shutdown(td, args->s, args->how));
1397c21dee17SSøren Schmidt }
1398c21dee17SSøren Schmidt 
1399a12b9b3dSDmitry Chagin int
1400b40ce416SJulian Elischer linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
1401c21dee17SSøren Schmidt {
140203cc95d2SDmitry Chagin 	l_timeval linux_tv;
1403d5368bf3SDmitry Chagin 	struct sockaddr *sa;
140403cc95d2SDmitry Chagin 	struct timeval tv;
1405d5368bf3SDmitry Chagin 	socklen_t len;
14069c6eb0f9SEdward Tomasz Napierala 	int error, level, name;
1407c21dee17SSøren Schmidt 
14089c6eb0f9SEdward Tomasz Napierala 	level = linux_to_bsd_sockopt_level(args->level);
14099c6eb0f9SEdward Tomasz Napierala 	switch (level) {
1410c21dee17SSøren Schmidt 	case SOL_SOCKET:
1411745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
141203cc95d2SDmitry Chagin 		switch (name) {
141303cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
141403cc95d2SDmitry Chagin 			/* FALLTHROUGH */
141503cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
141603cc95d2SDmitry Chagin 			error = copyin(PTRIN(args->optval), &linux_tv,
141703cc95d2SDmitry Chagin 			    sizeof(linux_tv));
1418dddb7e7fSDmitry Chagin 			if (error != 0)
141903cc95d2SDmitry Chagin 				return (error);
142003cc95d2SDmitry Chagin 			tv.tv_sec = linux_tv.tv_sec;
142103cc95d2SDmitry Chagin 			tv.tv_usec = linux_tv.tv_usec;
14229c6eb0f9SEdward Tomasz Napierala 			return (kern_setsockopt(td, args->s, level,
142303cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, sizeof(tv)));
142403cc95d2SDmitry Chagin 			/* NOTREACHED */
142503cc95d2SDmitry Chagin 		default:
142603cc95d2SDmitry Chagin 			break;
142703cc95d2SDmitry Chagin 		}
1428c21dee17SSøren Schmidt 		break;
1429c21dee17SSøren Schmidt 	case IPPROTO_IP:
1430da6d8ae6SEdward Tomasz Napierala 		if (args->optname == LINUX_IP_RECVERR &&
1431da6d8ae6SEdward Tomasz Napierala 		    linux_ignore_ip_recverr) {
1432da6d8ae6SEdward Tomasz Napierala 			/*
1433da6d8ae6SEdward Tomasz Napierala 			 * XXX: This is a hack to unbreak DNS resolution
1434da6d8ae6SEdward Tomasz Napierala 			 *	with glibc 2.30 and above.
1435da6d8ae6SEdward Tomasz Napierala 			 */
1436da6d8ae6SEdward Tomasz Napierala 			return (0);
1437da6d8ae6SEdward Tomasz Napierala 		}
1438745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1439c21dee17SSøren Schmidt 		break;
144086a9058bSAndrey V. Elsukov 	case IPPROTO_IPV6:
144186a9058bSAndrey V. Elsukov 		name = linux_to_bsd_ip6_sockopt(args->optname);
144286a9058bSAndrey V. Elsukov 		break;
1443dad3b88aSMike Smith 	case IPPROTO_TCP:
1444fb709557SJohn Baldwin 		name = linux_to_bsd_tcp_sockopt(args->optname);
1445dad3b88aSMike Smith 		break;
1446c21dee17SSøren Schmidt 	default:
14473f3a4815SMarcel Moolenaar 		name = -1;
14483f3a4815SMarcel Moolenaar 		break;
1449c21dee17SSøren Schmidt 	}
1450c21dee17SSøren Schmidt 	if (name == -1)
1451d4b7423fSAlexander Leidinger 		return (ENOPROTOOPT);
14523f3a4815SMarcel Moolenaar 
1453d5368bf3SDmitry Chagin 
1454d5368bf3SDmitry Chagin 	if (name == IPV6_NEXTHOP) {
1455d5368bf3SDmitry Chagin 		len = args->optlen;
1456d5368bf3SDmitry Chagin 		error = linux_to_bsd_sockaddr(PTRIN(args->optval), &sa, &len);
1457d5368bf3SDmitry Chagin 		if (error != 0)
1458d5368bf3SDmitry Chagin 			return (error);
1459d5368bf3SDmitry Chagin 
14609c6eb0f9SEdward Tomasz Napierala 		error = kern_setsockopt(td, args->s, level,
1461d5368bf3SDmitry Chagin 		    name, sa, UIO_SYSSPACE, len);
1462d5368bf3SDmitry Chagin 		free(sa, M_SONAME);
1463d5368bf3SDmitry Chagin 	} else {
14649c6eb0f9SEdward Tomasz Napierala 		error = kern_setsockopt(td, args->s, level,
14659c6eb0f9SEdward Tomasz Napierala 		    name, PTRIN(args->optval), UIO_USERSPACE, args->optlen);
1466d5368bf3SDmitry Chagin 	}
14675c8919adSAlexander Leidinger 
14685c8919adSAlexander Leidinger 	return (error);
1469c21dee17SSøren Schmidt }
1470c21dee17SSøren Schmidt 
1471a12b9b3dSDmitry Chagin int
1472b40ce416SJulian Elischer linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
1473c21dee17SSøren Schmidt {
147403cc95d2SDmitry Chagin 	l_timeval linux_tv;
147503cc95d2SDmitry Chagin 	struct timeval tv;
1476d56e689eSDmitry Chagin 	socklen_t tv_len, xulen, len;
1477d5368bf3SDmitry Chagin 	struct l_sockaddr *lsa;
1478d5368bf3SDmitry Chagin 	struct sockaddr *sa;
1479d4dd69c4SDmitry Chagin 	struct xucred xu;
1480d4dd69c4SDmitry Chagin 	struct l_ucred lxu;
1481dfd060c0SEdward Tomasz Napierala 	int error, level, name, newval;
1482c21dee17SSøren Schmidt 
1483dfd060c0SEdward Tomasz Napierala 	level = linux_to_bsd_sockopt_level(args->level);
1484dfd060c0SEdward Tomasz Napierala 	switch (level) {
1485c21dee17SSøren Schmidt 	case SOL_SOCKET:
1486745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
148703cc95d2SDmitry Chagin 		switch (name) {
148803cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
148903cc95d2SDmitry Chagin 			/* FALLTHROUGH */
149003cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
149103cc95d2SDmitry Chagin 			tv_len = sizeof(tv);
1492dfd060c0SEdward Tomasz Napierala 			error = kern_getsockopt(td, args->s, level,
149303cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, &tv_len);
1494dddb7e7fSDmitry Chagin 			if (error != 0)
149503cc95d2SDmitry Chagin 				return (error);
149603cc95d2SDmitry Chagin 			linux_tv.tv_sec = tv.tv_sec;
149703cc95d2SDmitry Chagin 			linux_tv.tv_usec = tv.tv_usec;
149803cc95d2SDmitry Chagin 			return (copyout(&linux_tv, PTRIN(args->optval),
149903cc95d2SDmitry Chagin 			    sizeof(linux_tv)));
150003cc95d2SDmitry Chagin 			/* NOTREACHED */
1501d4dd69c4SDmitry Chagin 		case LOCAL_PEERCRED:
1502cd92d27eSDmitry Chagin 			if (args->optlen < sizeof(lxu))
1503d4dd69c4SDmitry Chagin 				return (EINVAL);
1504bb376a99SMark Johnston 			/*
1505bb376a99SMark Johnston 			 * LOCAL_PEERCRED is not served at the SOL_SOCKET level,
1506bb376a99SMark Johnston 			 * but by the Unix socket's level 0.
1507bb376a99SMark Johnston 			 */
1508dfd060c0SEdward Tomasz Napierala 			level = 0;
1509d4dd69c4SDmitry Chagin 			xulen = sizeof(xu);
1510dfd060c0SEdward Tomasz Napierala 			error = kern_getsockopt(td, args->s, level,
1511d4dd69c4SDmitry Chagin 			    name, &xu, UIO_SYSSPACE, &xulen);
1512dddb7e7fSDmitry Chagin 			if (error != 0)
1513d4dd69c4SDmitry Chagin 				return (error);
1514c5afec6eSDmitry Chagin 			lxu.pid = xu.cr_pid;
1515d4dd69c4SDmitry Chagin 			lxu.uid = xu.cr_uid;
1516d4dd69c4SDmitry Chagin 			lxu.gid = xu.cr_gid;
1517d4dd69c4SDmitry Chagin 			return (copyout(&lxu, PTRIN(args->optval), sizeof(lxu)));
1518d4dd69c4SDmitry Chagin 			/* NOTREACHED */
1519d56e689eSDmitry Chagin 		case SO_ERROR:
1520d56e689eSDmitry Chagin 			len = sizeof(newval);
1521dfd060c0SEdward Tomasz Napierala 			error = kern_getsockopt(td, args->s, level,
1522d56e689eSDmitry Chagin 			    name, &newval, UIO_SYSSPACE, &len);
1523dddb7e7fSDmitry Chagin 			if (error != 0)
1524d56e689eSDmitry Chagin 				return (error);
1525d56e689eSDmitry Chagin 			newval = -SV_ABI_ERRNO(td->td_proc, newval);
1526d56e689eSDmitry Chagin 			return (copyout(&newval, PTRIN(args->optval), len));
1527d56e689eSDmitry Chagin 			/* NOTREACHED */
152803cc95d2SDmitry Chagin 		default:
152903cc95d2SDmitry Chagin 			break;
153003cc95d2SDmitry Chagin 		}
1531c21dee17SSøren Schmidt 		break;
1532c21dee17SSøren Schmidt 	case IPPROTO_IP:
1533745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1534c21dee17SSøren Schmidt 		break;
153586a9058bSAndrey V. Elsukov 	case IPPROTO_IPV6:
153686a9058bSAndrey V. Elsukov 		name = linux_to_bsd_ip6_sockopt(args->optname);
153786a9058bSAndrey V. Elsukov 		break;
1538dad3b88aSMike Smith 	case IPPROTO_TCP:
1539fb709557SJohn Baldwin 		name = linux_to_bsd_tcp_sockopt(args->optname);
1540dad3b88aSMike Smith 		break;
1541c21dee17SSøren Schmidt 	default:
15423f3a4815SMarcel Moolenaar 		name = -1;
15433f3a4815SMarcel Moolenaar 		break;
1544c21dee17SSøren Schmidt 	}
1545c21dee17SSøren Schmidt 	if (name == -1)
15463f3a4815SMarcel Moolenaar 		return (EINVAL);
15473f3a4815SMarcel Moolenaar 
15485c8919adSAlexander Leidinger 	if (name == IPV6_NEXTHOP) {
1549d5368bf3SDmitry Chagin 		error = copyin(PTRIN(args->optlen), &len, sizeof(len));
1550d5368bf3SDmitry Chagin                 if (error != 0)
1551d5368bf3SDmitry Chagin                         return (error);
1552d5368bf3SDmitry Chagin 		sa = malloc(len, M_SONAME, M_WAITOK);
1553d5368bf3SDmitry Chagin 
1554dfd060c0SEdward Tomasz Napierala 		error = kern_getsockopt(td, args->s, level,
1555d5368bf3SDmitry Chagin 		    name, sa, UIO_SYSSPACE, &len);
1556d5368bf3SDmitry Chagin 		if (error != 0)
1557d5368bf3SDmitry Chagin 			goto out;
1558d5368bf3SDmitry Chagin 
1559d5368bf3SDmitry Chagin 		error = bsd_to_linux_sockaddr(sa, &lsa, len);
1560d5368bf3SDmitry Chagin 		if (error == 0)
1561d5368bf3SDmitry Chagin 			error = copyout(lsa, PTRIN(args->optval), len);
1562d5368bf3SDmitry Chagin 		free(lsa, M_SONAME);
1563d5368bf3SDmitry Chagin 		if (error == 0)
1564d5368bf3SDmitry Chagin 			error = copyout(&len, PTRIN(args->optlen),
1565d5368bf3SDmitry Chagin 			    sizeof(len));
1566d5368bf3SDmitry Chagin out:
1567d5368bf3SDmitry Chagin 		free(sa, M_SONAME);
1568d5368bf3SDmitry Chagin 	} else {
1569dfd060c0SEdward Tomasz Napierala 		if (args->optval) {
1570dfd060c0SEdward Tomasz Napierala 			error = copyin(PTRIN(args->optlen), &len, sizeof(len));
1571dfd060c0SEdward Tomasz Napierala 			if (error != 0)
1572dfd060c0SEdward Tomasz Napierala 				return (error);
1573dfd060c0SEdward Tomasz Napierala 		}
1574dfd060c0SEdward Tomasz Napierala 		error = kern_getsockopt(td, args->s, level,
1575dfd060c0SEdward Tomasz Napierala 		    name, PTRIN(args->optval), UIO_USERSPACE, &len);
1576dfd060c0SEdward Tomasz Napierala 		if (error == 0)
1577dfd060c0SEdward Tomasz Napierala 			error = copyout(&len, PTRIN(args->optlen),
1578dfd060c0SEdward Tomasz Napierala 			    sizeof(len));
1579d5368bf3SDmitry Chagin 	}
15805c8919adSAlexander Leidinger 
15815c8919adSAlexander Leidinger 	return (error);
1582c21dee17SSøren Schmidt }
1583c21dee17SSøren Schmidt 
15847f8f1d7fSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
15857f8f1d7fSDmitry Chagin 
1586ea7b81d2SDmitry Chagin /* Argument list sizes for linux_socketcall */
1587abf20e93SDmitry Chagin static const unsigned char lxs_args_cnt[] = {
1588abf20e93SDmitry Chagin 	0 /* unused*/,		3 /* socket */,
1589abf20e93SDmitry Chagin 	3 /* bind */,		3 /* connect */,
1590abf20e93SDmitry Chagin 	2 /* listen */,		3 /* accept */,
1591abf20e93SDmitry Chagin 	3 /* getsockname */,	3 /* getpeername */,
1592abf20e93SDmitry Chagin 	4 /* socketpair */,	4 /* send */,
1593abf20e93SDmitry Chagin 	4 /* recv */,		6 /* sendto */,
1594abf20e93SDmitry Chagin 	6 /* recvfrom */,	2 /* shutdown */,
1595abf20e93SDmitry Chagin 	5 /* setsockopt */,	5 /* getsockopt */,
1596abf20e93SDmitry Chagin 	3 /* sendmsg */,	3 /* recvmsg */,
1597abf20e93SDmitry Chagin 	4 /* accept4 */,	5 /* recvmmsg */,
1598abf20e93SDmitry Chagin 	4 /* sendmmsg */
1599ea7b81d2SDmitry Chagin };
1600abf20e93SDmitry Chagin #define	LINUX_ARGS_CNT		(nitems(lxs_args_cnt) - 1)
1601abf20e93SDmitry Chagin #define	LINUX_ARG_SIZE(x)	(lxs_args_cnt[x] * sizeof(l_ulong))
1602ea7b81d2SDmitry Chagin 
1603c21dee17SSøren Schmidt int
1604b40ce416SJulian Elischer linux_socketcall(struct thread *td, struct linux_socketcall_args *args)
1605c21dee17SSøren Schmidt {
1606ea7b81d2SDmitry Chagin 	l_ulong a[6];
1607abf20e93SDmitry Chagin #if defined(__amd64__) && defined(COMPAT_LINUX32)
1608abf20e93SDmitry Chagin 	register_t l_args[6];
1609abf20e93SDmitry Chagin #endif
1610ea7b81d2SDmitry Chagin 	void *arg;
1611ea7b81d2SDmitry Chagin 	int error;
16123f3a4815SMarcel Moolenaar 
1613abf20e93SDmitry Chagin 	if (args->what < LINUX_SOCKET || args->what > LINUX_ARGS_CNT)
1614ea7b81d2SDmitry Chagin 		return (EINVAL);
1615abf20e93SDmitry Chagin 	error = copyin(PTRIN(args->args), a, LINUX_ARG_SIZE(args->what));
1616abf20e93SDmitry Chagin 	if (error != 0)
1617ea7b81d2SDmitry Chagin 		return (error);
1618ea7b81d2SDmitry Chagin 
1619abf20e93SDmitry Chagin #if defined(__amd64__) && defined(COMPAT_LINUX32)
1620abf20e93SDmitry Chagin 	for (int i = 0; i < lxs_args_cnt[args->what]; ++i)
1621abf20e93SDmitry Chagin 		l_args[i] = a[i];
1622abf20e93SDmitry Chagin 	arg = l_args;
1623abf20e93SDmitry Chagin #else
1624ea7b81d2SDmitry Chagin 	arg = a;
1625abf20e93SDmitry Chagin #endif
1626c21dee17SSøren Schmidt 	switch (args->what) {
1627c21dee17SSøren Schmidt 	case LINUX_SOCKET:
1628b40ce416SJulian Elischer 		return (linux_socket(td, arg));
1629c21dee17SSøren Schmidt 	case LINUX_BIND:
1630b40ce416SJulian Elischer 		return (linux_bind(td, arg));
1631c21dee17SSøren Schmidt 	case LINUX_CONNECT:
1632b40ce416SJulian Elischer 		return (linux_connect(td, arg));
1633c21dee17SSøren Schmidt 	case LINUX_LISTEN:
1634b40ce416SJulian Elischer 		return (linux_listen(td, arg));
1635c21dee17SSøren Schmidt 	case LINUX_ACCEPT:
1636b40ce416SJulian Elischer 		return (linux_accept(td, arg));
1637c21dee17SSøren Schmidt 	case LINUX_GETSOCKNAME:
1638b40ce416SJulian Elischer 		return (linux_getsockname(td, arg));
1639c21dee17SSøren Schmidt 	case LINUX_GETPEERNAME:
1640b40ce416SJulian Elischer 		return (linux_getpeername(td, arg));
1641c21dee17SSøren Schmidt 	case LINUX_SOCKETPAIR:
1642b40ce416SJulian Elischer 		return (linux_socketpair(td, arg));
1643c21dee17SSøren Schmidt 	case LINUX_SEND:
1644b40ce416SJulian Elischer 		return (linux_send(td, arg));
1645c21dee17SSøren Schmidt 	case LINUX_RECV:
1646b40ce416SJulian Elischer 		return (linux_recv(td, arg));
1647c21dee17SSøren Schmidt 	case LINUX_SENDTO:
1648b40ce416SJulian Elischer 		return (linux_sendto(td, arg));
1649c21dee17SSøren Schmidt 	case LINUX_RECVFROM:
1650b40ce416SJulian Elischer 		return (linux_recvfrom(td, arg));
1651c21dee17SSøren Schmidt 	case LINUX_SHUTDOWN:
1652b40ce416SJulian Elischer 		return (linux_shutdown(td, arg));
1653c21dee17SSøren Schmidt 	case LINUX_SETSOCKOPT:
1654b40ce416SJulian Elischer 		return (linux_setsockopt(td, arg));
1655c21dee17SSøren Schmidt 	case LINUX_GETSOCKOPT:
1656b40ce416SJulian Elischer 		return (linux_getsockopt(td, arg));
1657e76bba09SSøren Schmidt 	case LINUX_SENDMSG:
1658ca26842eSHajimu UMEMOTO 		return (linux_sendmsg(td, arg));
1659e76bba09SSøren Schmidt 	case LINUX_RECVMSG:
1660b40ce416SJulian Elischer 		return (linux_recvmsg(td, arg));
1661f8cd0af2SDmitry Chagin 	case LINUX_ACCEPT4:
1662f8cd0af2SDmitry Chagin 		return (linux_accept4(td, arg));
1663e1ff74c0SDmitry Chagin 	case LINUX_RECVMMSG:
1664e1ff74c0SDmitry Chagin 		return (linux_recvmmsg(td, arg));
1665e1ff74c0SDmitry Chagin 	case LINUX_SENDMMSG:
1666e1ff74c0SDmitry Chagin 		return (linux_sendmmsg(td, arg));
1667c21dee17SSøren Schmidt 	}
16683f3a4815SMarcel Moolenaar 
16693f3a4815SMarcel Moolenaar 	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
16703f3a4815SMarcel Moolenaar 	return (ENOSYS);
1671c21dee17SSøren Schmidt }
1672a12b9b3dSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1673