xref: /freebsd/sys/compat/linux/linux_socket.c (revision f05531a3921c8305d78561fe794c5fed9d7800dd)
1c21dee17SSøren Schmidt /*-
2c21dee17SSøren Schmidt  * Copyright (c) 1995 S�ren Schmidt
3c21dee17SSøren Schmidt  * All rights reserved.
4c21dee17SSøren Schmidt  *
5c21dee17SSøren Schmidt  * Redistribution and use in source and binary forms, with or without
6c21dee17SSøren Schmidt  * modification, are permitted provided that the following conditions
7c21dee17SSøren Schmidt  * are met:
8c21dee17SSøren Schmidt  * 1. Redistributions of source code must retain the above copyright
9c21dee17SSøren Schmidt  *    notice, this list of conditions and the following disclaimer
10c21dee17SSøren Schmidt  *    in this position and unchanged.
11c21dee17SSøren Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
12c21dee17SSøren Schmidt  *    notice, this list of conditions and the following disclaimer in the
13c21dee17SSøren Schmidt  *    documentation and/or other materials provided with the distribution.
14c21dee17SSøren Schmidt  * 3. The name of the author may not be used to endorse or promote products
1521dc7d4fSJens Schweikhardt  *    derived from this software without specific prior written permission
16c21dee17SSøren Schmidt  *
17c21dee17SSøren Schmidt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18c21dee17SSøren Schmidt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19c21dee17SSøren Schmidt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20c21dee17SSøren Schmidt  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21c21dee17SSøren Schmidt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22c21dee17SSøren Schmidt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23c21dee17SSøren Schmidt  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24c21dee17SSøren Schmidt  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25c21dee17SSøren Schmidt  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26c21dee17SSøren Schmidt  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 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>
40a9d2f8d8SRobert Watson #include <sys/capability.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>
56c21dee17SSøren Schmidt #include <netinet/in.h>
57f2477ae1SMike Smith #include <netinet/in_systm.h>
58f2477ae1SMike Smith #include <netinet/ip.h>
59ca26842eSHajimu UMEMOTO #ifdef INET6
60ca26842eSHajimu UMEMOTO #include <netinet/ip6.h>
61ca26842eSHajimu UMEMOTO #include <netinet6/ip6_var.h>
624b79449eSBjoern A. Zeeb #include <netinet6/in6_var.h>
63ca26842eSHajimu UMEMOTO #endif
64c21dee17SSøren Schmidt 
651997c537SDavid E. O'Brien #ifdef COMPAT_LINUX32
664af27623STim J. Robbins #include <machine/../linux32/linux.h>
674af27623STim J. Robbins #include <machine/../linux32/linux32_proto.h>
681997c537SDavid E. O'Brien #else
691997c537SDavid E. O'Brien #include <machine/../linux/linux.h>
701997c537SDavid E. O'Brien #include <machine/../linux/linux_proto.h>
714af27623STim J. Robbins #endif
7240dbba57SAssar Westerlund #include <compat/linux/linux_socket.h>
73ac951e62SMarcel Moolenaar #include <compat/linux/linux_util.h>
74c21dee17SSøren Schmidt 
75ca26842eSHajimu UMEMOTO static int linux_to_bsd_domain(int);
76ca26842eSHajimu UMEMOTO 
774730796cSBill Fenner /*
78ca26842eSHajimu UMEMOTO  * Reads a linux sockaddr and does any necessary translation.
79ca26842eSHajimu UMEMOTO  * Linux sockaddrs don't have a length field, only a family.
80ca26842eSHajimu UMEMOTO  * Copy the osockaddr structure pointed to by osa to kernel, adjust
81ca26842eSHajimu UMEMOTO  * family and convert to sockaddr.
82ca26842eSHajimu UMEMOTO  */
83ca26842eSHajimu UMEMOTO static int
840007f669SJung-uk Kim linux_getsockaddr(struct sockaddr **sap, const struct osockaddr *osa, int osalen)
85ca26842eSHajimu UMEMOTO {
86ca26842eSHajimu UMEMOTO 	int error=0, bdom;
87ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
88ca26842eSHajimu UMEMOTO 	struct osockaddr *kosa;
89ca26842eSHajimu UMEMOTO #ifdef INET6
90ca26842eSHajimu UMEMOTO 	int oldv6size;
91ca26842eSHajimu UMEMOTO 	struct sockaddr_in6 *sin6;
92ca26842eSHajimu UMEMOTO #endif
93c15cdbf2SJung-uk Kim 	int alloclen, hdrlen, namelen;
94ca26842eSHajimu UMEMOTO 
950007f669SJung-uk Kim 	if (osalen < 2 || osalen > UCHAR_MAX || !osa)
96ca26842eSHajimu UMEMOTO 		return (EINVAL);
97ca26842eSHajimu UMEMOTO 
980007f669SJung-uk Kim 	alloclen = osalen;
99ca26842eSHajimu UMEMOTO #ifdef INET6
100ca26842eSHajimu UMEMOTO 	oldv6size = 0;
101ca26842eSHajimu UMEMOTO 	/*
102ca26842eSHajimu UMEMOTO 	 * Check for old (pre-RFC2553) sockaddr_in6. We may accept it
103ca26842eSHajimu UMEMOTO 	 * if it's a v4-mapped address, so reserve the proper space
104ca26842eSHajimu UMEMOTO 	 * for it.
105ca26842eSHajimu UMEMOTO 	 */
106ca26842eSHajimu UMEMOTO 	if (alloclen == sizeof (struct sockaddr_in6) - sizeof (u_int32_t)) {
107ca26842eSHajimu UMEMOTO 		alloclen = sizeof (struct sockaddr_in6);
108ca26842eSHajimu UMEMOTO 		oldv6size = 1;
109ca26842eSHajimu UMEMOTO 	}
110ca26842eSHajimu UMEMOTO #endif
111ca26842eSHajimu UMEMOTO 
1120007f669SJung-uk Kim 	kosa = malloc(alloclen, M_SONAME, M_WAITOK);
113ca26842eSHajimu UMEMOTO 
1140007f669SJung-uk Kim 	if ((error = copyin(osa, kosa, osalen)))
115ca26842eSHajimu UMEMOTO 		goto out;
116ca26842eSHajimu UMEMOTO 
117ca26842eSHajimu UMEMOTO 	bdom = linux_to_bsd_domain(kosa->sa_family);
118ca26842eSHajimu UMEMOTO 	if (bdom == -1) {
1195cb9c68cSXin LI 		error = EAFNOSUPPORT;
120ca26842eSHajimu UMEMOTO 		goto out;
121ca26842eSHajimu UMEMOTO 	}
122ca26842eSHajimu UMEMOTO 
123ca26842eSHajimu UMEMOTO #ifdef INET6
124ca26842eSHajimu UMEMOTO 	/*
125ca26842eSHajimu UMEMOTO 	 * Older Linux IPv6 code uses obsolete RFC2133 struct sockaddr_in6,
126ca26842eSHajimu UMEMOTO 	 * which lacks the scope id compared with RFC2553 one. If we detect
127ca26842eSHajimu UMEMOTO 	 * the situation, reject the address and write a message to system log.
128ca26842eSHajimu UMEMOTO 	 *
129ca26842eSHajimu UMEMOTO 	 * Still accept addresses for which the scope id is not used.
130ca26842eSHajimu UMEMOTO 	 */
131*f05531a3SJung-uk Kim 	if (oldv6size) {
132*f05531a3SJung-uk Kim 		if (bdom == AF_INET6) {
133ca26842eSHajimu UMEMOTO 			sin6 = (struct sockaddr_in6 *)kosa;
134ca26842eSHajimu UMEMOTO 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
135ca26842eSHajimu UMEMOTO 			    (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
136ca26842eSHajimu UMEMOTO 			     !IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) &&
137ca26842eSHajimu UMEMOTO 			     !IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) &&
138ca26842eSHajimu UMEMOTO 			     !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
139ca26842eSHajimu UMEMOTO 			     !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) {
140ca26842eSHajimu UMEMOTO 				sin6->sin6_scope_id = 0;
141ca26842eSHajimu UMEMOTO 			} else {
142ca26842eSHajimu UMEMOTO 				log(LOG_DEBUG,
1433c616032SGleb Smirnoff 				    "obsolete pre-RFC2553 sockaddr_in6 rejected\n");
144ca26842eSHajimu UMEMOTO 				error = EINVAL;
145ca26842eSHajimu UMEMOTO 				goto out;
146ca26842eSHajimu UMEMOTO 			}
147ca26842eSHajimu UMEMOTO 		} else
148*f05531a3SJung-uk Kim 			alloclen -= sizeof(u_int32_t);
149*f05531a3SJung-uk Kim 	}
150ca26842eSHajimu UMEMOTO #endif
1515cb9c68cSXin LI 	if (bdom == AF_INET) {
152ca26842eSHajimu UMEMOTO 		alloclen = sizeof(struct sockaddr_in);
1530007f669SJung-uk Kim 		if (osalen < alloclen) {
1545cb9c68cSXin LI 			error = EINVAL;
1555cb9c68cSXin LI 			goto out;
1565cb9c68cSXin LI 		}
1575cb9c68cSXin LI 	}
158ca26842eSHajimu UMEMOTO 
1590007f669SJung-uk Kim 	if (bdom == AF_LOCAL && osalen > sizeof(struct sockaddr_un)) {
160c15cdbf2SJung-uk Kim 		hdrlen = offsetof(struct sockaddr_un, sun_path);
161c15cdbf2SJung-uk Kim 		namelen = strnlen(((struct sockaddr_un *)kosa)->sun_path,
1620007f669SJung-uk Kim 		    osalen - hdrlen);
163c15cdbf2SJung-uk Kim 		if (hdrlen + namelen > sizeof(struct sockaddr_un)) {
16443399111SJung-uk Kim 			error = ENAMETOOLONG;
1655da3eb94SColin Percival 			goto out;
1665da3eb94SColin Percival 		}
1675da3eb94SColin Percival 		alloclen = sizeof(struct sockaddr_un);
1685da3eb94SColin Percival 	}
1695da3eb94SColin Percival 
170ca26842eSHajimu UMEMOTO 	sa = (struct sockaddr *) kosa;
171ca26842eSHajimu UMEMOTO 	sa->sa_family = bdom;
172ca26842eSHajimu UMEMOTO 	sa->sa_len = alloclen;
173ca26842eSHajimu UMEMOTO 
174ca26842eSHajimu UMEMOTO 	*sap = sa;
175ca26842eSHajimu UMEMOTO 	return (0);
176ca26842eSHajimu UMEMOTO 
177ca26842eSHajimu UMEMOTO out:
1780007f669SJung-uk Kim 	free(kosa, M_SONAME);
179ca26842eSHajimu UMEMOTO 	return (error);
180ca26842eSHajimu UMEMOTO }
181ca26842eSHajimu UMEMOTO 
182c21dee17SSøren Schmidt static int
183c21dee17SSøren Schmidt linux_to_bsd_domain(int domain)
184c21dee17SSøren Schmidt {
1853f3a4815SMarcel Moolenaar 
186c21dee17SSøren Schmidt 	switch (domain) {
187c21dee17SSøren Schmidt 	case LINUX_AF_UNSPEC:
1883f3a4815SMarcel Moolenaar 		return (AF_UNSPEC);
189c21dee17SSøren Schmidt 	case LINUX_AF_UNIX:
1903f3a4815SMarcel Moolenaar 		return (AF_LOCAL);
191c21dee17SSøren Schmidt 	case LINUX_AF_INET:
1923f3a4815SMarcel Moolenaar 		return (AF_INET);
193ca26842eSHajimu UMEMOTO 	case LINUX_AF_INET6:
194ca26842eSHajimu UMEMOTO 		return (AF_INET6);
195c21dee17SSøren Schmidt 	case LINUX_AF_AX25:
1963f3a4815SMarcel Moolenaar 		return (AF_CCITT);
197c21dee17SSøren Schmidt 	case LINUX_AF_IPX:
1983f3a4815SMarcel Moolenaar 		return (AF_IPX);
199c21dee17SSøren Schmidt 	case LINUX_AF_APPLETALK:
2003f3a4815SMarcel Moolenaar 		return (AF_APPLETALK);
201c21dee17SSøren Schmidt 	}
2023f3a4815SMarcel Moolenaar 	return (-1);
203c21dee17SSøren Schmidt }
204c21dee17SSøren Schmidt 
205ca26842eSHajimu UMEMOTO static int
206ca26842eSHajimu UMEMOTO bsd_to_linux_domain(int domain)
207ca26842eSHajimu UMEMOTO {
208ca26842eSHajimu UMEMOTO 
209ca26842eSHajimu UMEMOTO 	switch (domain) {
210ca26842eSHajimu UMEMOTO 	case AF_UNSPEC:
211ca26842eSHajimu UMEMOTO 		return (LINUX_AF_UNSPEC);
212ca26842eSHajimu UMEMOTO 	case AF_LOCAL:
213ca26842eSHajimu UMEMOTO 		return (LINUX_AF_UNIX);
214ca26842eSHajimu UMEMOTO 	case AF_INET:
215ca26842eSHajimu UMEMOTO 		return (LINUX_AF_INET);
216ca26842eSHajimu UMEMOTO 	case AF_INET6:
217ca26842eSHajimu UMEMOTO 		return (LINUX_AF_INET6);
218ca26842eSHajimu UMEMOTO 	case AF_CCITT:
219ca26842eSHajimu UMEMOTO 		return (LINUX_AF_AX25);
220ca26842eSHajimu UMEMOTO 	case AF_IPX:
221ca26842eSHajimu UMEMOTO 		return (LINUX_AF_IPX);
222ca26842eSHajimu UMEMOTO 	case AF_APPLETALK:
223ca26842eSHajimu UMEMOTO 		return (LINUX_AF_APPLETALK);
224ca26842eSHajimu UMEMOTO 	}
225ca26842eSHajimu UMEMOTO 	return (-1);
226ca26842eSHajimu UMEMOTO }
227ca26842eSHajimu UMEMOTO 
228c21dee17SSøren Schmidt static int
229c21dee17SSøren Schmidt linux_to_bsd_sockopt_level(int level)
230c21dee17SSøren Schmidt {
2313f3a4815SMarcel Moolenaar 
232c21dee17SSøren Schmidt 	switch (level) {
233c21dee17SSøren Schmidt 	case LINUX_SOL_SOCKET:
2343f3a4815SMarcel Moolenaar 		return (SOL_SOCKET);
235c21dee17SSøren Schmidt 	}
2363f3a4815SMarcel Moolenaar 	return (level);
237c21dee17SSøren Schmidt }
238c21dee17SSøren Schmidt 
2393f3a4815SMarcel Moolenaar static int
24084b11cd8SMitsuru IWASAKI bsd_to_linux_sockopt_level(int level)
24184b11cd8SMitsuru IWASAKI {
24284b11cd8SMitsuru IWASAKI 
24384b11cd8SMitsuru IWASAKI 	switch (level) {
24484b11cd8SMitsuru IWASAKI 	case SOL_SOCKET:
24584b11cd8SMitsuru IWASAKI 		return (LINUX_SOL_SOCKET);
24684b11cd8SMitsuru IWASAKI 	}
24784b11cd8SMitsuru IWASAKI 	return (level);
24884b11cd8SMitsuru IWASAKI }
24984b11cd8SMitsuru IWASAKI 
25084b11cd8SMitsuru IWASAKI static int
2513f3a4815SMarcel Moolenaar linux_to_bsd_ip_sockopt(int opt)
252c21dee17SSøren Schmidt {
2533f3a4815SMarcel Moolenaar 
254c21dee17SSøren Schmidt 	switch (opt) {
255c21dee17SSøren Schmidt 	case LINUX_IP_TOS:
2563f3a4815SMarcel Moolenaar 		return (IP_TOS);
257c21dee17SSøren Schmidt 	case LINUX_IP_TTL:
2583f3a4815SMarcel Moolenaar 		return (IP_TTL);
25966ff6a3cSBill Fenner 	case LINUX_IP_OPTIONS:
2603f3a4815SMarcel Moolenaar 		return (IP_OPTIONS);
26166ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_IF:
2623f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_IF);
26366ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_TTL:
2643f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_TTL);
26566ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_LOOP:
2663f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_LOOP);
26766ff6a3cSBill Fenner 	case LINUX_IP_ADD_MEMBERSHIP:
2683f3a4815SMarcel Moolenaar 		return (IP_ADD_MEMBERSHIP);
26966ff6a3cSBill Fenner 	case LINUX_IP_DROP_MEMBERSHIP:
2703f3a4815SMarcel Moolenaar 		return (IP_DROP_MEMBERSHIP);
27166ff6a3cSBill Fenner 	case LINUX_IP_HDRINCL:
2723f3a4815SMarcel Moolenaar 		return (IP_HDRINCL);
273c21dee17SSøren Schmidt 	}
2743f3a4815SMarcel Moolenaar 	return (-1);
275c21dee17SSøren Schmidt }
276c21dee17SSøren Schmidt 
277c21dee17SSøren Schmidt static int
278c21dee17SSøren Schmidt linux_to_bsd_so_sockopt(int opt)
279c21dee17SSøren Schmidt {
2803f3a4815SMarcel Moolenaar 
281c21dee17SSøren Schmidt 	switch (opt) {
282c21dee17SSøren Schmidt 	case LINUX_SO_DEBUG:
2833f3a4815SMarcel Moolenaar 		return (SO_DEBUG);
284c21dee17SSøren Schmidt 	case LINUX_SO_REUSEADDR:
2853f3a4815SMarcel Moolenaar 		return (SO_REUSEADDR);
286c21dee17SSøren Schmidt 	case LINUX_SO_TYPE:
2873f3a4815SMarcel Moolenaar 		return (SO_TYPE);
288c21dee17SSøren Schmidt 	case LINUX_SO_ERROR:
2893f3a4815SMarcel Moolenaar 		return (SO_ERROR);
290c21dee17SSøren Schmidt 	case LINUX_SO_DONTROUTE:
2913f3a4815SMarcel Moolenaar 		return (SO_DONTROUTE);
292c21dee17SSøren Schmidt 	case LINUX_SO_BROADCAST:
2933f3a4815SMarcel Moolenaar 		return (SO_BROADCAST);
294c21dee17SSøren Schmidt 	case LINUX_SO_SNDBUF:
2953f3a4815SMarcel Moolenaar 		return (SO_SNDBUF);
296c21dee17SSøren Schmidt 	case LINUX_SO_RCVBUF:
2973f3a4815SMarcel Moolenaar 		return (SO_RCVBUF);
298c21dee17SSøren Schmidt 	case LINUX_SO_KEEPALIVE:
2993f3a4815SMarcel Moolenaar 		return (SO_KEEPALIVE);
300c21dee17SSøren Schmidt 	case LINUX_SO_OOBINLINE:
3013f3a4815SMarcel Moolenaar 		return (SO_OOBINLINE);
302c21dee17SSøren Schmidt 	case LINUX_SO_LINGER:
3033f3a4815SMarcel Moolenaar 		return (SO_LINGER);
304d0b2365eSKonstantin Belousov 	case LINUX_SO_PEERCRED:
305d0b2365eSKonstantin Belousov 		return (LOCAL_PEERCRED);
306d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVLOWAT:
307d0b2365eSKonstantin Belousov 		return (SO_RCVLOWAT);
308d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDLOWAT:
309d0b2365eSKonstantin Belousov 		return (SO_SNDLOWAT);
310d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVTIMEO:
311d0b2365eSKonstantin Belousov 		return (SO_RCVTIMEO);
312d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDTIMEO:
313d0b2365eSKonstantin Belousov 		return (SO_SNDTIMEO);
314d0b2365eSKonstantin Belousov 	case LINUX_SO_TIMESTAMP:
315d0b2365eSKonstantin Belousov 		return (SO_TIMESTAMP);
316d0b2365eSKonstantin Belousov 	case LINUX_SO_ACCEPTCONN:
317d0b2365eSKonstantin Belousov 		return (SO_ACCEPTCONN);
318c21dee17SSøren Schmidt 	}
3193f3a4815SMarcel Moolenaar 	return (-1);
320c21dee17SSøren Schmidt }
321c21dee17SSøren Schmidt 
32240dbba57SAssar Westerlund static int
32340dbba57SAssar Westerlund linux_to_bsd_msg_flags(int flags)
32440dbba57SAssar Westerlund {
32540dbba57SAssar Westerlund 	int ret_flags = 0;
32640dbba57SAssar Westerlund 
32740dbba57SAssar Westerlund 	if (flags & LINUX_MSG_OOB)
32840dbba57SAssar Westerlund 		ret_flags |= MSG_OOB;
32940dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PEEK)
33040dbba57SAssar Westerlund 		ret_flags |= MSG_PEEK;
33140dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTROUTE)
33240dbba57SAssar Westerlund 		ret_flags |= MSG_DONTROUTE;
33340dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CTRUNC)
33440dbba57SAssar Westerlund 		ret_flags |= MSG_CTRUNC;
33540dbba57SAssar Westerlund 	if (flags & LINUX_MSG_TRUNC)
33640dbba57SAssar Westerlund 		ret_flags |= MSG_TRUNC;
33740dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTWAIT)
33840dbba57SAssar Westerlund 		ret_flags |= MSG_DONTWAIT;
33940dbba57SAssar Westerlund 	if (flags & LINUX_MSG_EOR)
34040dbba57SAssar Westerlund 		ret_flags |= MSG_EOR;
34140dbba57SAssar Westerlund 	if (flags & LINUX_MSG_WAITALL)
34240dbba57SAssar Westerlund 		ret_flags |= MSG_WAITALL;
3438d6e40c3SMaxim Sobolev 	if (flags & LINUX_MSG_NOSIGNAL)
3448d6e40c3SMaxim Sobolev 		ret_flags |= MSG_NOSIGNAL;
34540dbba57SAssar Westerlund #if 0 /* not handled */
34640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PROXY)
34740dbba57SAssar Westerlund 		;
34840dbba57SAssar Westerlund 	if (flags & LINUX_MSG_FIN)
34940dbba57SAssar Westerlund 		;
35040dbba57SAssar Westerlund 	if (flags & LINUX_MSG_SYN)
35140dbba57SAssar Westerlund 		;
35240dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CONFIRM)
35340dbba57SAssar Westerlund 		;
35440dbba57SAssar Westerlund 	if (flags & LINUX_MSG_RST)
35540dbba57SAssar Westerlund 		;
35640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_ERRQUEUE)
35740dbba57SAssar Westerlund 		;
35840dbba57SAssar Westerlund #endif
35940dbba57SAssar Westerlund 	return ret_flags;
36040dbba57SAssar Westerlund }
36140dbba57SAssar Westerlund 
3625c8919adSAlexander Leidinger /*
3635c8919adSAlexander Leidinger * If bsd_to_linux_sockaddr() or linux_to_bsd_sockaddr() faults, then the
3645c8919adSAlexander Leidinger * native syscall will fault.  Thus, we don't really need to check the
3655c8919adSAlexander Leidinger * return values for these functions.
3665c8919adSAlexander Leidinger */
3675c8919adSAlexander Leidinger 
3685c8919adSAlexander Leidinger static int
3695c8919adSAlexander Leidinger bsd_to_linux_sockaddr(struct sockaddr *arg)
3705c8919adSAlexander Leidinger {
3715c8919adSAlexander Leidinger 	struct sockaddr sa;
3725c8919adSAlexander Leidinger 	size_t sa_len = sizeof(struct sockaddr);
3735c8919adSAlexander Leidinger 	int error;
3745c8919adSAlexander Leidinger 
3755c8919adSAlexander Leidinger 	if ((error = copyin(arg, &sa, sa_len)))
3765c8919adSAlexander Leidinger 		return (error);
3775c8919adSAlexander Leidinger 
3785c8919adSAlexander Leidinger 	*(u_short *)&sa = sa.sa_family;
3795c8919adSAlexander Leidinger 
3805c8919adSAlexander Leidinger 	error = copyout(&sa, arg, sa_len);
3815c8919adSAlexander Leidinger 
3825c8919adSAlexander Leidinger 	return (error);
3835c8919adSAlexander Leidinger }
3845c8919adSAlexander Leidinger 
3855c8919adSAlexander Leidinger static int
3865c8919adSAlexander Leidinger linux_to_bsd_sockaddr(struct sockaddr *arg, int len)
3875c8919adSAlexander Leidinger {
3885c8919adSAlexander Leidinger 	struct sockaddr sa;
3895c8919adSAlexander Leidinger 	size_t sa_len = sizeof(struct sockaddr);
3905c8919adSAlexander Leidinger 	int error;
3915c8919adSAlexander Leidinger 
3925c8919adSAlexander Leidinger 	if ((error = copyin(arg, &sa, sa_len)))
3935c8919adSAlexander Leidinger 		return (error);
3945c8919adSAlexander Leidinger 
3955c8919adSAlexander Leidinger 	sa.sa_family = *(sa_family_t *)&sa;
3965c8919adSAlexander Leidinger 	sa.sa_len = len;
3975c8919adSAlexander Leidinger 
3985c8919adSAlexander Leidinger 	error = copyout(&sa, arg, sa_len);
3995c8919adSAlexander Leidinger 
4005c8919adSAlexander Leidinger 	return (error);
4015c8919adSAlexander Leidinger }
4025c8919adSAlexander Leidinger 
4035c8919adSAlexander Leidinger 
404ca26842eSHajimu UMEMOTO static int
405ca26842eSHajimu UMEMOTO linux_sa_put(struct osockaddr *osa)
406ca26842eSHajimu UMEMOTO {
407ca26842eSHajimu UMEMOTO 	struct osockaddr sa;
408ca26842eSHajimu UMEMOTO 	int error, bdom;
409ca26842eSHajimu UMEMOTO 
410ca26842eSHajimu UMEMOTO 	/*
411ca26842eSHajimu UMEMOTO 	 * Only read/write the osockaddr family part, the rest is
412ca26842eSHajimu UMEMOTO 	 * not changed.
413ca26842eSHajimu UMEMOTO 	 */
4144b7ef73dSDag-Erling Smørgrav 	error = copyin(osa, &sa, sizeof(sa.sa_family));
415ca26842eSHajimu UMEMOTO 	if (error)
416ca26842eSHajimu UMEMOTO 		return (error);
417ca26842eSHajimu UMEMOTO 
418ca26842eSHajimu UMEMOTO 	bdom = bsd_to_linux_domain(sa.sa_family);
419ca26842eSHajimu UMEMOTO 	if (bdom == -1)
420ca26842eSHajimu UMEMOTO 		return (EINVAL);
421ca26842eSHajimu UMEMOTO 
422ca26842eSHajimu UMEMOTO 	sa.sa_family = bdom;
423ca26842eSHajimu UMEMOTO 	error = copyout(&sa, osa, sizeof(sa.sa_family));
424ca26842eSHajimu UMEMOTO 	if (error)
425ca26842eSHajimu UMEMOTO 		return (error);
426ca26842eSHajimu UMEMOTO 
427ca26842eSHajimu UMEMOTO 	return (0);
428ca26842eSHajimu UMEMOTO }
429ca26842eSHajimu UMEMOTO 
4305a8a13e0SDavid Malone static int
43174f5d680SKonstantin Belousov linux_to_bsd_cmsg_type(int cmsg_type)
4325a8a13e0SDavid Malone {
43374f5d680SKonstantin Belousov 
43474f5d680SKonstantin Belousov 	switch (cmsg_type) {
43574f5d680SKonstantin Belousov 	case LINUX_SCM_RIGHTS:
43674f5d680SKonstantin Belousov 		return (SCM_RIGHTS);
437605da56bSAndriy Gapon 	case LINUX_SCM_CREDENTIALS:
438605da56bSAndriy Gapon 		return (SCM_CREDS);
43974f5d680SKonstantin Belousov 	}
44074f5d680SKonstantin Belousov 	return (-1);
44174f5d680SKonstantin Belousov }
44274f5d680SKonstantin Belousov 
44374f5d680SKonstantin Belousov static int
44474f5d680SKonstantin Belousov bsd_to_linux_cmsg_type(int cmsg_type)
44574f5d680SKonstantin Belousov {
44674f5d680SKonstantin Belousov 
44774f5d680SKonstantin Belousov 	switch (cmsg_type) {
44874f5d680SKonstantin Belousov 	case SCM_RIGHTS:
44974f5d680SKonstantin Belousov 		return (LINUX_SCM_RIGHTS);
450605da56bSAndriy Gapon 	case SCM_CREDS:
451605da56bSAndriy Gapon 		return (LINUX_SCM_CREDENTIALS);
45274f5d680SKonstantin Belousov 	}
45374f5d680SKonstantin Belousov 	return (-1);
45474f5d680SKonstantin Belousov }
45574f5d680SKonstantin Belousov 
45674f5d680SKonstantin Belousov static int
45774f5d680SKonstantin Belousov linux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
45874f5d680SKonstantin Belousov {
45974f5d680SKonstantin Belousov 	if (lhdr->msg_controllen > INT_MAX)
46074f5d680SKonstantin Belousov 		return (ENOBUFS);
46174f5d680SKonstantin Belousov 
46274f5d680SKonstantin Belousov 	bhdr->msg_name		= PTRIN(lhdr->msg_name);
46374f5d680SKonstantin Belousov 	bhdr->msg_namelen	= lhdr->msg_namelen;
46474f5d680SKonstantin Belousov 	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
46574f5d680SKonstantin Belousov 	bhdr->msg_iovlen	= lhdr->msg_iovlen;
46674f5d680SKonstantin Belousov 	bhdr->msg_control	= PTRIN(lhdr->msg_control);
467605da56bSAndriy Gapon 
468605da56bSAndriy Gapon 	/*
469605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
470605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
471605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
472605da56bSAndriy Gapon 	 *
473605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
474605da56bSAndriy Gapon 	 * control messages.
475605da56bSAndriy Gapon 	 */
476605da56bSAndriy Gapon 
47774f5d680SKonstantin Belousov 	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
47874f5d680SKonstantin Belousov 	return (0);
47974f5d680SKonstantin Belousov }
48074f5d680SKonstantin Belousov 
48174f5d680SKonstantin Belousov static int
48274f5d680SKonstantin Belousov bsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
48374f5d680SKonstantin Belousov {
48474f5d680SKonstantin Belousov 	lhdr->msg_name		= PTROUT(bhdr->msg_name);
48574f5d680SKonstantin Belousov 	lhdr->msg_namelen	= bhdr->msg_namelen;
48674f5d680SKonstantin Belousov 	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
48774f5d680SKonstantin Belousov 	lhdr->msg_iovlen	= bhdr->msg_iovlen;
48874f5d680SKonstantin Belousov 	lhdr->msg_control	= PTROUT(bhdr->msg_control);
489605da56bSAndriy Gapon 
490605da56bSAndriy Gapon 	/*
491605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
492605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
493605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
494605da56bSAndriy Gapon 	 *
495605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
496605da56bSAndriy Gapon 	 * control messages.
497605da56bSAndriy Gapon 	 */
498605da56bSAndriy Gapon 
49974f5d680SKonstantin Belousov 	/* msg_flags skipped */
50074f5d680SKonstantin Belousov 	return (0);
50174f5d680SKonstantin Belousov }
50274f5d680SKonstantin Belousov 
50374f5d680SKonstantin Belousov static int
50438a18e97SDmitry Chagin linux_set_socket_flags(struct thread *td, int s, int flags)
50538a18e97SDmitry Chagin {
50638a18e97SDmitry Chagin 	int error;
50738a18e97SDmitry Chagin 
50838a18e97SDmitry Chagin 	if (flags & LINUX_SOCK_NONBLOCK) {
50938a18e97SDmitry Chagin 		error = kern_fcntl(td, s, F_SETFL, O_NONBLOCK);
51038a18e97SDmitry Chagin 		if (error)
51138a18e97SDmitry Chagin 			return (error);
51238a18e97SDmitry Chagin 	}
51338a18e97SDmitry Chagin 	if (flags & LINUX_SOCK_CLOEXEC) {
51438a18e97SDmitry Chagin 		error = kern_fcntl(td, s, F_SETFD, FD_CLOEXEC);
51538a18e97SDmitry Chagin 		if (error)
51638a18e97SDmitry Chagin 			return (error);
51738a18e97SDmitry Chagin 	}
51838a18e97SDmitry Chagin 	return (0);
51938a18e97SDmitry Chagin }
52038a18e97SDmitry Chagin 
52138a18e97SDmitry Chagin static int
52274f5d680SKonstantin Belousov linux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
52374f5d680SKonstantin Belousov     struct mbuf *control, enum uio_seg segflg)
52474f5d680SKonstantin Belousov {
5255a8a13e0SDavid Malone 	struct sockaddr *to;
5265a8a13e0SDavid Malone 	int error;
5275a8a13e0SDavid Malone 
5285a8a13e0SDavid Malone 	if (mp->msg_name != NULL) {
5295a8a13e0SDavid Malone 		error = linux_getsockaddr(&to, mp->msg_name, mp->msg_namelen);
5305a8a13e0SDavid Malone 		if (error)
5315a8a13e0SDavid Malone 			return (error);
5325a8a13e0SDavid Malone 		mp->msg_name = to;
5335a8a13e0SDavid Malone 	} else
5345a8a13e0SDavid Malone 		to = NULL;
5355a8a13e0SDavid Malone 
536a6886ef1SMaxim Sobolev 	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
537a6886ef1SMaxim Sobolev 	    segflg);
5385a8a13e0SDavid Malone 
5395a8a13e0SDavid Malone 	if (to)
5401ede983cSDag-Erling Smørgrav 		free(to, M_SONAME);
5415a8a13e0SDavid Malone 	return (error);
5425a8a13e0SDavid Malone }
5435a8a13e0SDavid Malone 
5443f3a4815SMarcel Moolenaar /* Return 0 if IP_HDRINCL is set for the given socket. */
545f2477ae1SMike Smith static int
546e140eb43SDavid Malone linux_check_hdrincl(struct thread *td, int s)
547f2477ae1SMike Smith {
5483db2a843SBruce Evans 	int error, optval, size_val;
549f2477ae1SMike Smith 
550e140eb43SDavid Malone 	size_val = sizeof(optval);
551e140eb43SDavid Malone 	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
552e140eb43SDavid Malone 	    &optval, UIO_SYSSPACE, &size_val);
553e140eb43SDavid Malone 	if (error)
5543f3a4815SMarcel Moolenaar 		return (error);
5553f3a4815SMarcel Moolenaar 
5563f3a4815SMarcel Moolenaar 	return (optval == 0);
557f2477ae1SMike Smith }
558f2477ae1SMike Smith 
5595a8a13e0SDavid Malone struct linux_sendto_args {
5605a8a13e0SDavid Malone 	int s;
5614af27623STim J. Robbins 	l_uintptr_t msg;
5625a8a13e0SDavid Malone 	int len;
5635a8a13e0SDavid Malone 	int flags;
5644af27623STim J. Robbins 	l_uintptr_t to;
5655a8a13e0SDavid Malone 	int tolen;
5665a8a13e0SDavid Malone };
5675a8a13e0SDavid Malone 
568f2477ae1SMike Smith /*
569f2477ae1SMike Smith  * Updated sendto() when IP_HDRINCL is set:
570f2477ae1SMike Smith  * tweak endian-dependent fields in the IP packet.
571f2477ae1SMike Smith  */
572f2477ae1SMike Smith static int
57338da2381SRobert Watson linux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
574f2477ae1SMike Smith {
575f2477ae1SMike Smith /*
576f2477ae1SMike Smith  * linux_ip_copysize defines how many bytes we should copy
577f2477ae1SMike Smith  * from the beginning of the IP packet before we customize it for BSD.
578a6886ef1SMaxim Sobolev  * It should include all the fields we modify (ip_len and ip_off).
579f2477ae1SMike Smith  */
580f2477ae1SMike Smith #define linux_ip_copysize	8
581f2477ae1SMike Smith 
582f2477ae1SMike Smith 	struct ip *packet;
5835a8a13e0SDavid Malone 	struct msghdr msg;
584a6886ef1SMaxim Sobolev 	struct iovec aiov[1];
585f2477ae1SMike Smith 	int error;
586f2477ae1SMike Smith 
587aa675b57SDavid Schultz 	/* Check that the packet isn't too big or too small. */
588aa675b57SDavid Schultz 	if (linux_args->len < linux_ip_copysize ||
589aa675b57SDavid Schultz 	    linux_args->len > IP_MAXPACKET)
5903f3a4815SMarcel Moolenaar 		return (EINVAL);
591f2477ae1SMike Smith 
592a6886ef1SMaxim Sobolev 	packet = (struct ip *)malloc(linux_args->len, M_TEMP, M_WAITOK);
593f2477ae1SMike Smith 
594a6886ef1SMaxim Sobolev 	/* Make kernel copy of the packet to be sent */
5954af27623STim J. Robbins 	if ((error = copyin(PTRIN(linux_args->msg), packet,
596a6886ef1SMaxim Sobolev 	    linux_args->len)))
597a6886ef1SMaxim Sobolev 		goto goout;
598f2477ae1SMike Smith 
599f2477ae1SMike Smith 	/* Convert fields from Linux to BSD raw IP socket format */
6005a8a13e0SDavid Malone 	packet->ip_len = linux_args->len;
601f2477ae1SMike Smith 	packet->ip_off = ntohs(packet->ip_off);
602f2477ae1SMike Smith 
603f2477ae1SMike Smith 	/* Prepare the msghdr and iovec structures describing the new packet */
6044af27623STim J. Robbins 	msg.msg_name = PTRIN(linux_args->to);
6055a8a13e0SDavid Malone 	msg.msg_namelen = linux_args->tolen;
6065a8a13e0SDavid Malone 	msg.msg_iov = aiov;
607a6886ef1SMaxim Sobolev 	msg.msg_iovlen = 1;
6085a8a13e0SDavid Malone 	msg.msg_control = NULL;
6095a8a13e0SDavid Malone 	msg.msg_flags = 0;
6105a8a13e0SDavid Malone 	aiov[0].iov_base = (char *)packet;
611a6886ef1SMaxim Sobolev 	aiov[0].iov_len = linux_args->len;
612a6886ef1SMaxim Sobolev 	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
61374f5d680SKonstantin Belousov 	    NULL, UIO_SYSSPACE);
614a6886ef1SMaxim Sobolev goout:
615a6886ef1SMaxim Sobolev 	free(packet, M_TEMP);
6165a8a13e0SDavid Malone 	return (error);
617f2477ae1SMike Smith }
618f2477ae1SMike Smith 
619c21dee17SSøren Schmidt struct linux_socket_args {
620c21dee17SSøren Schmidt 	int domain;
621c21dee17SSøren Schmidt 	int type;
622c21dee17SSøren Schmidt 	int protocol;
623c21dee17SSøren Schmidt };
624c21dee17SSøren Schmidt 
625c21dee17SSøren Schmidt static int
626b40ce416SJulian Elischer linux_socket(struct thread *td, struct linux_socket_args *args)
627c21dee17SSøren Schmidt {
628ef04503dSPeter Wemm 	struct socket_args /* {
629c21dee17SSøren Schmidt 		int domain;
630c21dee17SSøren Schmidt 		int type;
631c21dee17SSøren Schmidt 		int protocol;
632ef04503dSPeter Wemm 	} */ bsd_args;
6333933bde2SDmitry Chagin 	int retval_socket, socket_flags;
634c21dee17SSøren Schmidt 
635745aaef5SKonstantin Belousov 	bsd_args.protocol = args->protocol;
6363933bde2SDmitry Chagin 	socket_flags = args->type & ~LINUX_SOCK_TYPE_MASK;
6373933bde2SDmitry Chagin 	if (socket_flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
6383933bde2SDmitry Chagin 		return (EINVAL);
6393933bde2SDmitry Chagin 	bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
640eeb63e51SDmitry Chagin 	if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
641eeb63e51SDmitry Chagin 		return (EINVAL);
642745aaef5SKonstantin Belousov 	bsd_args.domain = linux_to_bsd_domain(args->domain);
643c21dee17SSøren Schmidt 	if (bsd_args.domain == -1)
644d9b063ccSDmitry Chagin 		return (EAFNOSUPPORT);
645f2477ae1SMike Smith 
6468451d0ddSKip Macy 	retval_socket = sys_socket(td, &bsd_args);
6476994ea54SDmitry Chagin 	if (retval_socket)
6486994ea54SDmitry Chagin 		return (retval_socket);
6496994ea54SDmitry Chagin 
65038a18e97SDmitry Chagin 	retval_socket = linux_set_socket_flags(td, td->td_retval[0],
65138a18e97SDmitry Chagin 	    socket_flags);
6523933bde2SDmitry Chagin 	if (retval_socket) {
6533933bde2SDmitry Chagin 		(void)kern_close(td, td->td_retval[0]);
6543933bde2SDmitry Chagin 		goto out;
6553933bde2SDmitry Chagin 	}
6563933bde2SDmitry Chagin 
657f2477ae1SMike Smith 	if (bsd_args.type == SOCK_RAW
658f2477ae1SMike Smith 	    && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0)
6596994ea54SDmitry Chagin 	    && bsd_args.domain == PF_INET) {
660f2477ae1SMike Smith 		/* It's a raw IP socket: set the IP_HDRINCL option. */
661e140eb43SDavid Malone 		int hdrincl;
662f2477ae1SMike Smith 
663e140eb43SDavid Malone 		hdrincl = 1;
664e140eb43SDavid Malone 		/* We ignore any error returned by kern_setsockopt() */
665e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
666e140eb43SDavid Malone 		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
667f2477ae1SMike Smith 	}
668ca26842eSHajimu UMEMOTO #ifdef INET6
669ca26842eSHajimu UMEMOTO 	/*
670d97bee3eSBjoern A. Zeeb 	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by default
671d97bee3eSBjoern A. Zeeb 	 * and some apps depend on this. So, set V6ONLY to 0 for Linux apps.
672d97bee3eSBjoern A. Zeeb 	 * For simplicity we do this unconditionally of the net.inet6.ip6.v6only
673d97bee3eSBjoern A. Zeeb 	 * sysctl value.
674ca26842eSHajimu UMEMOTO 	 */
675d97bee3eSBjoern A. Zeeb 	if (bsd_args.domain == PF_INET6) {
676e140eb43SDavid Malone 		int v6only;
677ca26842eSHajimu UMEMOTO 
678e140eb43SDavid Malone 		v6only = 0;
679ca26842eSHajimu UMEMOTO 		/* We ignore any error returned by setsockopt() */
680e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
681e140eb43SDavid Malone 		    &v6only, UIO_SYSSPACE, sizeof(v6only));
682ca26842eSHajimu UMEMOTO 	}
683ca26842eSHajimu UMEMOTO #endif
6843f3a4815SMarcel Moolenaar 
6853933bde2SDmitry Chagin out:
6863f3a4815SMarcel Moolenaar 	return (retval_socket);
687c21dee17SSøren Schmidt }
688c21dee17SSøren Schmidt 
689c21dee17SSøren Schmidt struct linux_bind_args {
690c21dee17SSøren Schmidt 	int s;
6914af27623STim J. Robbins 	l_uintptr_t name;
692c21dee17SSøren Schmidt 	int namelen;
693c21dee17SSøren Schmidt };
694c21dee17SSøren Schmidt 
695c21dee17SSøren Schmidt static int
696b40ce416SJulian Elischer linux_bind(struct thread *td, struct linux_bind_args *args)
697c21dee17SSøren Schmidt {
698ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
699c21dee17SSøren Schmidt 	int error;
700c21dee17SSøren Schmidt 
701745aaef5SKonstantin Belousov 	error = linux_getsockaddr(&sa, PTRIN(args->name),
702745aaef5SKonstantin Belousov 	    args->namelen);
703ca26842eSHajimu UMEMOTO 	if (error)
704ca26842eSHajimu UMEMOTO 		return (error);
705ca26842eSHajimu UMEMOTO 
706745aaef5SKonstantin Belousov 	error = kern_bind(td, args->s, sa);
707b33887eaSJohn Baldwin 	free(sa, M_SONAME);
708745aaef5SKonstantin Belousov 	if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
709d4b7423fSAlexander Leidinger 	   	return (EINVAL);
710b33887eaSJohn Baldwin 	return (error);
711c21dee17SSøren Schmidt }
712c21dee17SSøren Schmidt 
71301e0ffbaSAlexander Leidinger struct linux_connect_args {
714c21dee17SSøren Schmidt 	int s;
7154af27623STim J. Robbins 	l_uintptr_t name;
716c21dee17SSøren Schmidt 	int namelen;
717c21dee17SSøren Schmidt };
718b40ce416SJulian Elischer int linux_connect(struct thread *, struct linux_connect_args *);
719c21dee17SSøren Schmidt 
720930a65feSAndrew Gallatin int
721b40ce416SJulian Elischer linux_connect(struct thread *td, struct linux_connect_args *args)
722c21dee17SSøren Schmidt {
7230bf301c0SJonathan Lemon 	struct socket *so;
724ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
72539c95b83SMatthew Dillon 	u_int fflag;
726c21dee17SSøren Schmidt 	int error;
727c21dee17SSøren Schmidt 
728745aaef5SKonstantin Belousov 	error = linux_getsockaddr(&sa, (struct osockaddr *)PTRIN(args->name),
729745aaef5SKonstantin Belousov 	    args->namelen);
730ca26842eSHajimu UMEMOTO 	if (error)
731ca26842eSHajimu UMEMOTO 		return (error);
732ca26842eSHajimu UMEMOTO 
733745aaef5SKonstantin Belousov 	error = kern_connect(td, args->s, sa);
734b33887eaSJohn Baldwin 	free(sa, M_SONAME);
7350bf301c0SJonathan Lemon 	if (error != EISCONN)
7360bf301c0SJonathan Lemon 		return (error);
7370bf301c0SJonathan Lemon 
738dad3b88aSMike Smith 	/*
739dad3b88aSMike Smith 	 * Linux doesn't return EISCONN the first time it occurs,
740dad3b88aSMike Smith 	 * when on a non-blocking socket. Instead it returns the
741dad3b88aSMike Smith 	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
742f7f45ac8SRobert Watson 	 *
743f7f45ac8SRobert Watson 	 * XXXRW: Instead of using fgetsock(), check that it is a
744f7f45ac8SRobert Watson 	 * socket and use the file descriptor reference instead of
745f7f45ac8SRobert Watson 	 * creating a new one.
746dad3b88aSMike Smith 	 */
747a9d2f8d8SRobert Watson 	error = fgetsock(td, args->s, CAP_CONNECT, &so, &fflag);
7484641373fSJohn Baldwin 	if (error == 0) {
7490bf301c0SJonathan Lemon 		error = EISCONN;
75039c95b83SMatthew Dillon 		if (fflag & FNONBLOCK) {
7514641373fSJohn Baldwin 			SOCK_LOCK(so);
7525002a60fSMarcel Moolenaar 			if (so->so_emuldata == 0)
7530bf301c0SJonathan Lemon 				error = so->so_error;
7540bf301c0SJonathan Lemon 			so->so_emuldata = (void *)1;
7554641373fSJohn Baldwin 			SOCK_UNLOCK(so);
756dad3b88aSMike Smith 		}
75739c95b83SMatthew Dillon 		fputsock(so);
7584641373fSJohn Baldwin 	}
7593f3a4815SMarcel Moolenaar 	return (error);
760c21dee17SSøren Schmidt }
761c21dee17SSøren Schmidt 
762c21dee17SSøren Schmidt struct linux_listen_args {
763c21dee17SSøren Schmidt 	int s;
764c21dee17SSøren Schmidt 	int backlog;
765c21dee17SSøren Schmidt };
766c21dee17SSøren Schmidt 
767c21dee17SSøren Schmidt static int
768b40ce416SJulian Elischer linux_listen(struct thread *td, struct linux_listen_args *args)
769c21dee17SSøren Schmidt {
770ef04503dSPeter Wemm 	struct listen_args /* {
771c21dee17SSøren Schmidt 		int s;
772c21dee17SSøren Schmidt 		int backlog;
773ef04503dSPeter Wemm 	} */ bsd_args;
774c21dee17SSøren Schmidt 
775745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
776745aaef5SKonstantin Belousov 	bsd_args.backlog = args->backlog;
7778451d0ddSKip Macy 	return (sys_listen(td, &bsd_args));
778c21dee17SSøren Schmidt }
779c21dee17SSøren Schmidt 
78001e0ffbaSAlexander Leidinger static int
781c8f37d61SDmitry Chagin linux_accept_common(struct thread *td, int s, l_uintptr_t addr,
782f83427b8SDmitry Chagin     l_uintptr_t namelen, int flags)
783c21dee17SSøren Schmidt {
784ef04503dSPeter Wemm 	struct accept_args /* {
785c21dee17SSøren Schmidt 		int	s;
7863db2a843SBruce Evans 		struct sockaddr * __restrict name;
7873db2a843SBruce Evans 		socklen_t * __restrict anamelen;
788ef04503dSPeter Wemm 	} */ bsd_args;
78993e694c9SDmitry Chagin 	int error;
79093e694c9SDmitry Chagin 
79193e694c9SDmitry Chagin 	if (flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
79293e694c9SDmitry Chagin 		return (EINVAL);
793c21dee17SSøren Schmidt 
794c8f37d61SDmitry Chagin 	bsd_args.s = s;
7953db2a843SBruce Evans 	/* XXX: */
796c8f37d61SDmitry Chagin 	bsd_args.name = (struct sockaddr * __restrict)PTRIN(addr);
797c8f37d61SDmitry Chagin 	bsd_args.anamelen = PTRIN(namelen);/* XXX */
7988451d0ddSKip Macy 	error = sys_accept(td, &bsd_args);
7995c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.name);
800d4b7423fSAlexander Leidinger 	if (error) {
801c8f37d61SDmitry Chagin 		if (error == EFAULT && namelen != sizeof(struct sockaddr_in))
802d4b7423fSAlexander Leidinger 			return (EINVAL);
803dba5ab66SMarcel Moolenaar 		return (error);
804d4b7423fSAlexander Leidinger 	}
805dba5ab66SMarcel Moolenaar 
806dba5ab66SMarcel Moolenaar 	/*
807dba5ab66SMarcel Moolenaar 	 * linux appears not to copy flags from the parent socket to the
80893e694c9SDmitry Chagin 	 * accepted one, so we must clear the flags in the new descriptor
80993e694c9SDmitry Chagin 	 * and apply the requested flags.
810dba5ab66SMarcel Moolenaar 	 */
81193e694c9SDmitry Chagin 	error = kern_fcntl(td, td->td_retval[0], F_SETFL, 0);
81293e694c9SDmitry Chagin 	if (error)
81393e694c9SDmitry Chagin 		goto out;
81493e694c9SDmitry Chagin 	error = linux_set_socket_flags(td, td->td_retval[0], flags);
81593e694c9SDmitry Chagin 	if (error)
81693e694c9SDmitry Chagin 		goto out;
81793e694c9SDmitry Chagin 	if (addr)
81893e694c9SDmitry Chagin 		error = linux_sa_put(PTRIN(addr));
81993e694c9SDmitry Chagin 
82093e694c9SDmitry Chagin out:
82193e694c9SDmitry Chagin 	if (error) {
82293e694c9SDmitry Chagin 		(void)kern_close(td, td->td_retval[0]);
82393e694c9SDmitry Chagin 		td->td_retval[0] = 0;
82493e694c9SDmitry Chagin 	}
82593e694c9SDmitry Chagin 	return (error);
826c21dee17SSøren Schmidt }
827c21dee17SSøren Schmidt 
828c8f37d61SDmitry Chagin struct linux_accept_args {
829c8f37d61SDmitry Chagin 	int s;
830c8f37d61SDmitry Chagin 	l_uintptr_t addr;
831c8f37d61SDmitry Chagin 	l_uintptr_t namelen;
832c8f37d61SDmitry Chagin };
833c8f37d61SDmitry Chagin 
834c8f37d61SDmitry Chagin static int
835c8f37d61SDmitry Chagin linux_accept(struct thread *td, struct linux_accept_args *args)
836c8f37d61SDmitry Chagin {
837c8f37d61SDmitry Chagin 
838c8f37d61SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
839f83427b8SDmitry Chagin 	    args->namelen, 0));
840c8f37d61SDmitry Chagin }
841c8f37d61SDmitry Chagin 
842f8cd0af2SDmitry Chagin struct linux_accept4_args {
843f8cd0af2SDmitry Chagin 	int s;
844f8cd0af2SDmitry Chagin 	l_uintptr_t addr;
845f8cd0af2SDmitry Chagin 	l_uintptr_t namelen;
846f8cd0af2SDmitry Chagin 	int flags;
847f8cd0af2SDmitry Chagin };
848f8cd0af2SDmitry Chagin 
849f8cd0af2SDmitry Chagin static int
850f8cd0af2SDmitry Chagin linux_accept4(struct thread *td, struct linux_accept4_args *args)
851f8cd0af2SDmitry Chagin {
852f8cd0af2SDmitry Chagin 
853f8cd0af2SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
854f8cd0af2SDmitry Chagin 	    args->namelen, args->flags));
855f8cd0af2SDmitry Chagin }
856f8cd0af2SDmitry Chagin 
85701e0ffbaSAlexander Leidinger struct linux_getsockname_args {
858c21dee17SSøren Schmidt 	int s;
8594af27623STim J. Robbins 	l_uintptr_t addr;
8604af27623STim J. Robbins 	l_uintptr_t namelen;
861c21dee17SSøren Schmidt };
862c21dee17SSøren Schmidt 
86301e0ffbaSAlexander Leidinger static int
864b40ce416SJulian Elischer linux_getsockname(struct thread *td, struct linux_getsockname_args *args)
865c21dee17SSøren Schmidt {
866ef04503dSPeter Wemm 	struct getsockname_args /* {
867c21dee17SSøren Schmidt 		int	fdes;
8683db2a843SBruce Evans 		struct sockaddr * __restrict asa;
8693db2a843SBruce Evans 		socklen_t * __restrict alen;
870ef04503dSPeter Wemm 	} */ bsd_args;
871c21dee17SSøren Schmidt 	int error;
872c21dee17SSøren Schmidt 
873745aaef5SKonstantin Belousov 	bsd_args.fdes = args->s;
8743db2a843SBruce Evans 	/* XXX: */
875745aaef5SKonstantin Belousov 	bsd_args.asa = (struct sockaddr * __restrict)PTRIN(args->addr);
876745aaef5SKonstantin Belousov 	bsd_args.alen = PTRIN(args->namelen);	/* XXX */
8778451d0ddSKip Macy 	error = sys_getsockname(td, &bsd_args);
8785c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
879ca26842eSHajimu UMEMOTO 	if (error)
880ca26842eSHajimu UMEMOTO 		return (error);
881745aaef5SKonstantin Belousov 	error = linux_sa_put(PTRIN(args->addr));
882ca26842eSHajimu UMEMOTO 	if (error)
883ca26842eSHajimu UMEMOTO 		return (error);
884ca26842eSHajimu UMEMOTO 	return (0);
885c21dee17SSøren Schmidt }
886c21dee17SSøren Schmidt 
88701e0ffbaSAlexander Leidinger struct linux_getpeername_args {
888c21dee17SSøren Schmidt 	int s;
8894af27623STim J. Robbins 	l_uintptr_t addr;
8904af27623STim J. Robbins 	l_uintptr_t namelen;
891c21dee17SSøren Schmidt };
892c21dee17SSøren Schmidt 
89301e0ffbaSAlexander Leidinger static int
894b40ce416SJulian Elischer linux_getpeername(struct thread *td, struct linux_getpeername_args *args)
895c21dee17SSøren Schmidt {
8965c8919adSAlexander Leidinger 	struct getpeername_args /* {
897c21dee17SSøren Schmidt 		int fdes;
898c21dee17SSøren Schmidt 		caddr_t asa;
899c21dee17SSøren Schmidt 		int *alen;
900ef04503dSPeter Wemm 	} */ bsd_args;
901c21dee17SSøren Schmidt 	int error;
902c21dee17SSøren Schmidt 
903745aaef5SKonstantin Belousov 	bsd_args.fdes = args->s;
904745aaef5SKonstantin Belousov 	bsd_args.asa = (struct sockaddr *)PTRIN(args->addr);
905745aaef5SKonstantin Belousov 	bsd_args.alen = (int *)PTRIN(args->namelen);
9068451d0ddSKip Macy 	error = sys_getpeername(td, &bsd_args);
9075c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
908ca26842eSHajimu UMEMOTO 	if (error)
909ca26842eSHajimu UMEMOTO 		return (error);
910745aaef5SKonstantin Belousov 	error = linux_sa_put(PTRIN(args->addr));
911ca26842eSHajimu UMEMOTO 	if (error)
912ca26842eSHajimu UMEMOTO 		return (error);
913ca26842eSHajimu UMEMOTO 	return (0);
914c21dee17SSøren Schmidt }
915c21dee17SSøren Schmidt 
91601e0ffbaSAlexander Leidinger struct linux_socketpair_args {
917c21dee17SSøren Schmidt 	int domain;
918c21dee17SSøren Schmidt 	int type;
919c21dee17SSøren Schmidt 	int protocol;
9204af27623STim J. Robbins 	l_uintptr_t rsv;
921c21dee17SSøren Schmidt };
922c21dee17SSøren Schmidt 
92301e0ffbaSAlexander Leidinger static int
924b40ce416SJulian Elischer linux_socketpair(struct thread *td, struct linux_socketpair_args *args)
925c21dee17SSøren Schmidt {
926ef04503dSPeter Wemm 	struct socketpair_args /* {
927c21dee17SSøren Schmidt 		int domain;
928c21dee17SSøren Schmidt 		int type;
929c21dee17SSøren Schmidt 		int protocol;
930c21dee17SSøren Schmidt 		int *rsv;
931ef04503dSPeter Wemm 	} */ bsd_args;
93239253cf9SDmitry Chagin 	int error, socket_flags;
93339253cf9SDmitry Chagin 	int sv[2];
934c21dee17SSøren Schmidt 
935745aaef5SKonstantin Belousov 	bsd_args.domain = linux_to_bsd_domain(args->domain);
9361a52a4abSDmitry Chagin 	if (bsd_args.domain != PF_LOCAL)
9371a52a4abSDmitry Chagin 		return (EAFNOSUPPORT);
9383f3a4815SMarcel Moolenaar 
93939253cf9SDmitry Chagin 	socket_flags = args->type & ~LINUX_SOCK_TYPE_MASK;
94039253cf9SDmitry Chagin 	if (socket_flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
94139253cf9SDmitry Chagin 		return (EINVAL);
94239253cf9SDmitry Chagin 	bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
94339253cf9SDmitry Chagin 	if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
94439253cf9SDmitry Chagin 		return (EINVAL);
94539253cf9SDmitry Chagin 
9461a52a4abSDmitry Chagin 	if (args->protocol != 0 && args->protocol != PF_UNIX)
9471a52a4abSDmitry Chagin 
9481a52a4abSDmitry Chagin 		/*
9491a52a4abSDmitry Chagin 		 * Use of PF_UNIX as protocol argument is not right,
9501a52a4abSDmitry Chagin 		 * but Linux does it.
9511a52a4abSDmitry Chagin 		 * Do not map PF_UNIX as its Linux value is identical
9521a52a4abSDmitry Chagin 		 * to FreeBSD one.
9531a52a4abSDmitry Chagin 		 */
9541a52a4abSDmitry Chagin 		return (EPROTONOSUPPORT);
95540092d93SDmitry Chagin 	else
9561a52a4abSDmitry Chagin 		bsd_args.protocol = 0;
957745aaef5SKonstantin Belousov 	bsd_args.rsv = (int *)PTRIN(args->rsv);
95839253cf9SDmitry Chagin 	error = kern_socketpair(td, bsd_args.domain, bsd_args.type,
95939253cf9SDmitry Chagin 	    bsd_args.protocol, sv);
96039253cf9SDmitry Chagin 	if (error)
96139253cf9SDmitry Chagin 		return (error);
96239253cf9SDmitry Chagin 	error = linux_set_socket_flags(td, sv[0], socket_flags);
96339253cf9SDmitry Chagin 	if (error)
96439253cf9SDmitry Chagin 		goto out;
96539253cf9SDmitry Chagin 	error = linux_set_socket_flags(td, sv[1], socket_flags);
96639253cf9SDmitry Chagin 	if (error)
96739253cf9SDmitry Chagin 		goto out;
96839253cf9SDmitry Chagin 
96939253cf9SDmitry Chagin 	error = copyout(sv, bsd_args.rsv, 2 * sizeof(int));
97039253cf9SDmitry Chagin 
97139253cf9SDmitry Chagin out:
97239253cf9SDmitry Chagin 	if (error) {
97339253cf9SDmitry Chagin 		(void)kern_close(td, sv[0]);
97439253cf9SDmitry Chagin 		(void)kern_close(td, sv[1]);
97539253cf9SDmitry Chagin 	}
97639253cf9SDmitry Chagin 	return (error);
977c21dee17SSøren Schmidt }
978c21dee17SSøren Schmidt 
97901e0ffbaSAlexander Leidinger struct linux_send_args {
980c21dee17SSøren Schmidt 	int s;
9814af27623STim J. Robbins 	l_uintptr_t msg;
982c21dee17SSøren Schmidt 	int len;
983c21dee17SSøren Schmidt 	int flags;
984c21dee17SSøren Schmidt };
985c21dee17SSøren Schmidt 
98601e0ffbaSAlexander Leidinger static int
987b40ce416SJulian Elischer linux_send(struct thread *td, struct linux_send_args *args)
988c21dee17SSøren Schmidt {
98987d72a8fSPoul-Henning Kamp 	struct sendto_args /* {
990c21dee17SSøren Schmidt 		int s;
991c21dee17SSøren Schmidt 		caddr_t buf;
992044af7c3SJonathan Mini 		int len;
993c21dee17SSøren Schmidt 		int flags;
99487d72a8fSPoul-Henning Kamp 		caddr_t to;
99587d72a8fSPoul-Henning Kamp 		int tolen;
996ef04503dSPeter Wemm 	} */ bsd_args;
997c21dee17SSøren Schmidt 
998745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
999745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
1000745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
1001745aaef5SKonstantin Belousov 	bsd_args.flags = args->flags;
100287d72a8fSPoul-Henning Kamp 	bsd_args.to = NULL;
100387d72a8fSPoul-Henning Kamp 	bsd_args.tolen = 0;
10048451d0ddSKip Macy 	return sys_sendto(td, &bsd_args);
1005c21dee17SSøren Schmidt }
1006c21dee17SSøren Schmidt 
100701e0ffbaSAlexander Leidinger struct linux_recv_args {
1008c21dee17SSøren Schmidt 	int s;
10094af27623STim J. Robbins 	l_uintptr_t msg;
1010c21dee17SSøren Schmidt 	int len;
1011c21dee17SSøren Schmidt 	int flags;
1012c21dee17SSøren Schmidt };
1013c21dee17SSøren Schmidt 
101401e0ffbaSAlexander Leidinger static int
1015b40ce416SJulian Elischer linux_recv(struct thread *td, struct linux_recv_args *args)
1016c21dee17SSøren Schmidt {
101787d72a8fSPoul-Henning Kamp 	struct recvfrom_args /* {
1018c21dee17SSøren Schmidt 		int s;
1019c21dee17SSøren Schmidt 		caddr_t buf;
1020c21dee17SSøren Schmidt 		int len;
1021c21dee17SSøren Schmidt 		int flags;
102287d72a8fSPoul-Henning Kamp 		struct sockaddr *from;
102387d72a8fSPoul-Henning Kamp 		socklen_t fromlenaddr;
1024ef04503dSPeter Wemm 	} */ bsd_args;
1025c21dee17SSøren Schmidt 
1026745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1027745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
1028745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
10293980a435SDmitry Chagin 	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
103087d72a8fSPoul-Henning Kamp 	bsd_args.from = NULL;
103187d72a8fSPoul-Henning Kamp 	bsd_args.fromlenaddr = 0;
10328451d0ddSKip Macy 	return (sys_recvfrom(td, &bsd_args));
1033c21dee17SSøren Schmidt }
1034c21dee17SSøren Schmidt 
1035c21dee17SSøren Schmidt static int
1036b40ce416SJulian Elischer linux_sendto(struct thread *td, struct linux_sendto_args *args)
1037c21dee17SSøren Schmidt {
10385a8a13e0SDavid Malone 	struct msghdr msg;
10395a8a13e0SDavid Malone 	struct iovec aiov;
10405a8a13e0SDavid Malone 	int error;
1041c21dee17SSøren Schmidt 
1042745aaef5SKonstantin Belousov 	if (linux_check_hdrincl(td, args->s) == 0)
1043f2477ae1SMike Smith 		/* IP_HDRINCL set, tweak the packet before sending */
1044745aaef5SKonstantin Belousov 		return (linux_sendto_hdrincl(td, args));
1045f2477ae1SMike Smith 
1046745aaef5SKonstantin Belousov 	msg.msg_name = PTRIN(args->to);
1047745aaef5SKonstantin Belousov 	msg.msg_namelen = args->tolen;
10485a8a13e0SDavid Malone 	msg.msg_iov = &aiov;
10495a8a13e0SDavid Malone 	msg.msg_iovlen = 1;
10505a8a13e0SDavid Malone 	msg.msg_control = NULL;
10515a8a13e0SDavid Malone 	msg.msg_flags = 0;
1052745aaef5SKonstantin Belousov 	aiov.iov_base = PTRIN(args->msg);
1053745aaef5SKonstantin Belousov 	aiov.iov_len = args->len;
105474f5d680SKonstantin Belousov 	error = linux_sendit(td, args->s, &msg, args->flags, NULL,
105574f5d680SKonstantin Belousov 	    UIO_USERSPACE);
10565a8a13e0SDavid Malone 	return (error);
1057c21dee17SSøren Schmidt }
1058c21dee17SSøren Schmidt 
105901e0ffbaSAlexander Leidinger struct linux_recvfrom_args {
1060c21dee17SSøren Schmidt 	int s;
10614af27623STim J. Robbins 	l_uintptr_t buf;
1062c21dee17SSøren Schmidt 	int len;
1063c21dee17SSøren Schmidt 	int flags;
10644af27623STim J. Robbins 	l_uintptr_t from;
10654af27623STim J. Robbins 	l_uintptr_t fromlen;
1066c21dee17SSøren Schmidt };
1067c21dee17SSøren Schmidt 
106801e0ffbaSAlexander Leidinger static int
1069b40ce416SJulian Elischer linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
1070c21dee17SSøren Schmidt {
1071ef04503dSPeter Wemm 	struct recvfrom_args /* {
1072c21dee17SSøren Schmidt 		int	s;
1073c21dee17SSøren Schmidt 		caddr_t	buf;
1074c21dee17SSøren Schmidt 		size_t	len;
1075c21dee17SSøren Schmidt 		int	flags;
10763db2a843SBruce Evans 		struct sockaddr * __restrict from;
10773db2a843SBruce Evans 		socklen_t * __restrict fromlenaddr;
1078ef04503dSPeter Wemm 	} */ bsd_args;
10795c8919adSAlexander Leidinger 	size_t len;
1080c21dee17SSøren Schmidt 	int error;
1081c21dee17SSøren Schmidt 
1082745aaef5SKonstantin Belousov 	if ((error = copyin(PTRIN(args->fromlen), &len, sizeof(size_t))))
10833f3a4815SMarcel Moolenaar 		return (error);
10843f3a4815SMarcel Moolenaar 
1085745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1086745aaef5SKonstantin Belousov 	bsd_args.buf = PTRIN(args->buf);
1087745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
1088745aaef5SKonstantin Belousov 	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
10893db2a843SBruce Evans 	/* XXX: */
1090745aaef5SKonstantin Belousov 	bsd_args.from = (struct sockaddr * __restrict)PTRIN(args->from);
1091745aaef5SKonstantin Belousov 	bsd_args.fromlenaddr = PTRIN(args->fromlen);/* XXX */
10925c8919adSAlexander Leidinger 
10935c8919adSAlexander Leidinger 	linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.from, len);
10948451d0ddSKip Macy 	error = sys_recvfrom(td, &bsd_args);
10955c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.from);
10965c8919adSAlexander Leidinger 
1097ca26842eSHajimu UMEMOTO 	if (error)
1098ca26842eSHajimu UMEMOTO 		return (error);
1099745aaef5SKonstantin Belousov 	if (args->from) {
11004af27623STim J. Robbins 		error = linux_sa_put((struct osockaddr *)
1101745aaef5SKonstantin Belousov 		    PTRIN(args->from));
1102ca26842eSHajimu UMEMOTO 		if (error)
1103ca26842eSHajimu UMEMOTO 			return (error);
1104ca26842eSHajimu UMEMOTO 	}
1105ca26842eSHajimu UMEMOTO 	return (0);
1106ca26842eSHajimu UMEMOTO }
1107ca26842eSHajimu UMEMOTO 
110801e0ffbaSAlexander Leidinger struct linux_sendmsg_args {
1109ca26842eSHajimu UMEMOTO 	int s;
11104af27623STim J. Robbins 	l_uintptr_t msg;
1111ca26842eSHajimu UMEMOTO 	int flags;
1112ca26842eSHajimu UMEMOTO };
1113ca26842eSHajimu UMEMOTO 
111401e0ffbaSAlexander Leidinger static int
1115ca26842eSHajimu UMEMOTO linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
1116ca26842eSHajimu UMEMOTO {
111774f5d680SKonstantin Belousov 	struct cmsghdr *cmsg;
1118605da56bSAndriy Gapon 	struct cmsgcred cmcred;
111974f5d680SKonstantin Belousov 	struct mbuf *control;
1120ca26842eSHajimu UMEMOTO 	struct msghdr msg;
112174f5d680SKonstantin Belousov 	struct l_cmsghdr linux_cmsg;
112274f5d680SKonstantin Belousov 	struct l_cmsghdr *ptr_cmsg;
112374f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
1124552afd9cSPoul-Henning Kamp 	struct iovec *iov;
112574f5d680SKonstantin Belousov 	socklen_t datalen;
1126605da56bSAndriy Gapon 	struct sockaddr *sa;
1127605da56bSAndriy Gapon 	sa_family_t sa_family;
112874f5d680SKonstantin Belousov 	void *data;
1129ca26842eSHajimu UMEMOTO 	int error;
1130ca26842eSHajimu UMEMOTO 
113174f5d680SKonstantin Belousov 	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
113274f5d680SKonstantin Belousov 	if (error)
113374f5d680SKonstantin Belousov 		return (error);
1134d72a6158SRobert Watson 
1135d72a6158SRobert Watson 	/*
1136d72a6158SRobert Watson 	 * Some Linux applications (ping) define a non-NULL control data
1137d72a6158SRobert Watson 	 * pointer, but a msg_controllen of 0, which is not allowed in the
1138d72a6158SRobert Watson 	 * FreeBSD system call interface.  NULL the msg_control pointer in
1139d72a6158SRobert Watson 	 * order to handle this case.  This should be checked, but allows the
1140d72a6158SRobert Watson 	 * Linux ping to work.
1141d72a6158SRobert Watson 	 */
1142605da56bSAndriy Gapon 	if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0)
1143605da56bSAndriy Gapon 		linux_msg.msg_control = PTROUT(NULL);
1144605da56bSAndriy Gapon 
1145605da56bSAndriy Gapon 	error = linux_to_bsd_msghdr(&msg, &linux_msg);
1146605da56bSAndriy Gapon 	if (error)
1147605da56bSAndriy Gapon 		return (error);
114874f5d680SKonstantin Belousov 
114974f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
115074f5d680SKonstantin Belousov 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
115174f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
115274f5d680SKonstantin Belousov #else
1153552afd9cSPoul-Henning Kamp 	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
115474f5d680SKonstantin Belousov #endif
1155552afd9cSPoul-Henning Kamp 	if (error)
1156552afd9cSPoul-Henning Kamp 		return (error);
115774f5d680SKonstantin Belousov 
1158605da56bSAndriy Gapon 	control = NULL;
1159605da56bSAndriy Gapon 	cmsg = NULL;
1160605da56bSAndriy Gapon 
1161605da56bSAndriy Gapon 	if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) {
1162605da56bSAndriy Gapon 		error = kern_getsockname(td, args->s, &sa, &datalen);
1163605da56bSAndriy Gapon 		if (error)
1164605da56bSAndriy Gapon 			goto bad;
1165605da56bSAndriy Gapon 		sa_family = sa->sa_family;
1166605da56bSAndriy Gapon 		free(sa, M_SONAME);
1167605da56bSAndriy Gapon 
116874f5d680SKonstantin Belousov 		error = ENOBUFS;
116974f5d680SKonstantin Belousov 		cmsg = malloc(CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
117074f5d680SKonstantin Belousov 		control = m_get(M_WAIT, MT_CONTROL);
117174f5d680SKonstantin Belousov 		if (control == NULL)
117274f5d680SKonstantin Belousov 			goto bad;
117374f5d680SKonstantin Belousov 
117474f5d680SKonstantin Belousov 		do {
117574f5d680SKonstantin Belousov 			error = copyin(ptr_cmsg, &linux_cmsg,
117674f5d680SKonstantin Belousov 			    sizeof(struct l_cmsghdr));
117774f5d680SKonstantin Belousov 			if (error)
117874f5d680SKonstantin Belousov 				goto bad;
117974f5d680SKonstantin Belousov 
118074f5d680SKonstantin Belousov 			error = EINVAL;
118174f5d680SKonstantin Belousov 			if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr))
118274f5d680SKonstantin Belousov 				goto bad;
118374f5d680SKonstantin Belousov 
118474f5d680SKonstantin Belousov 			/*
1185605da56bSAndriy Gapon 			 * Now we support only SCM_RIGHTS and SCM_CRED,
1186605da56bSAndriy Gapon 			 * so return EINVAL in any other cmsg_type
118774f5d680SKonstantin Belousov 			 */
1188605da56bSAndriy Gapon 			cmsg->cmsg_type =
1189605da56bSAndriy Gapon 			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
119074f5d680SKonstantin Belousov 			cmsg->cmsg_level =
119174f5d680SKonstantin Belousov 			    linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
1192605da56bSAndriy Gapon 			if (cmsg->cmsg_type == -1
1193605da56bSAndriy Gapon 			    || cmsg->cmsg_level != SOL_SOCKET)
1194605da56bSAndriy Gapon 				goto bad;
119574f5d680SKonstantin Belousov 
1196605da56bSAndriy Gapon 			/*
1197605da56bSAndriy Gapon 			 * Some applications (e.g. pulseaudio) attempt to
1198605da56bSAndriy Gapon 			 * send ancillary data even if the underlying protocol
1199605da56bSAndriy Gapon 			 * doesn't support it which is not allowed in the
1200605da56bSAndriy Gapon 			 * FreeBSD system call interface.
1201605da56bSAndriy Gapon 			 */
1202605da56bSAndriy Gapon 			if (sa_family != AF_UNIX)
1203605da56bSAndriy Gapon 				continue;
1204605da56bSAndriy Gapon 
120574f5d680SKonstantin Belousov 			data = LINUX_CMSG_DATA(ptr_cmsg);
1206605da56bSAndriy Gapon 			datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
1207605da56bSAndriy Gapon 
1208605da56bSAndriy Gapon 			switch (cmsg->cmsg_type)
1209605da56bSAndriy Gapon 			{
1210605da56bSAndriy Gapon 			case SCM_RIGHTS:
1211605da56bSAndriy Gapon 				break;
1212605da56bSAndriy Gapon 
1213605da56bSAndriy Gapon 			case SCM_CREDS:
1214605da56bSAndriy Gapon 				data = &cmcred;
1215605da56bSAndriy Gapon 				datalen = sizeof(cmcred);
1216605da56bSAndriy Gapon 
1217605da56bSAndriy Gapon 				/*
1218605da56bSAndriy Gapon 				 * The lower levels will fill in the structure
1219605da56bSAndriy Gapon 				 */
1220605da56bSAndriy Gapon 				bzero(data, datalen);
1221605da56bSAndriy Gapon 				break;
1222605da56bSAndriy Gapon 			}
1223605da56bSAndriy Gapon 
1224605da56bSAndriy Gapon 			cmsg->cmsg_len = CMSG_LEN(datalen);
122574f5d680SKonstantin Belousov 
122674f5d680SKonstantin Belousov 			error = ENOBUFS;
122774f5d680SKonstantin Belousov 			if (!m_append(control, CMSG_HDRSZ, (c_caddr_t) cmsg))
122874f5d680SKonstantin Belousov 				goto bad;
122974f5d680SKonstantin Belousov 			if (!m_append(control, datalen, (c_caddr_t) data))
123074f5d680SKonstantin Belousov 				goto bad;
1231605da56bSAndriy Gapon 		} while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg)));
1232605da56bSAndriy Gapon 
1233605da56bSAndriy Gapon 		if (m_length(control, NULL) == 0) {
1234605da56bSAndriy Gapon 			m_freem(control);
123574f5d680SKonstantin Belousov 			control = NULL;
1236605da56bSAndriy Gapon 		}
123774f5d680SKonstantin Belousov 	}
123874f5d680SKonstantin Belousov 
12395a8a13e0SDavid Malone 	msg.msg_iov = iov;
12405a8a13e0SDavid Malone 	msg.msg_flags = 0;
124174f5d680SKonstantin Belousov 	error = linux_sendit(td, args->s, &msg, args->flags, control,
124274f5d680SKonstantin Belousov 	    UIO_USERSPACE);
124374f5d680SKonstantin Belousov 
124474f5d680SKonstantin Belousov bad:
1245552afd9cSPoul-Henning Kamp 	free(iov, M_IOV);
124674f5d680SKonstantin Belousov 	if (cmsg)
124774f5d680SKonstantin Belousov 		free(cmsg, M_TEMP);
1248ca26842eSHajimu UMEMOTO 	return (error);
1249c21dee17SSøren Schmidt }
1250c21dee17SSøren Schmidt 
125101e0ffbaSAlexander Leidinger struct linux_recvmsg_args {
125240dbba57SAssar Westerlund 	int s;
12534af27623STim J. Robbins 	l_uintptr_t msg;
125440dbba57SAssar Westerlund 	int flags;
125540dbba57SAssar Westerlund };
125640dbba57SAssar Westerlund 
125701e0ffbaSAlexander Leidinger static int
1258b40ce416SJulian Elischer linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
125940dbba57SAssar Westerlund {
126074f5d680SKonstantin Belousov 	struct cmsghdr *cm;
1261605da56bSAndriy Gapon 	struct cmsgcred *cmcred;
1262ca26842eSHajimu UMEMOTO 	struct msghdr msg;
126374f5d680SKonstantin Belousov 	struct l_cmsghdr *linux_cmsg = NULL;
1264605da56bSAndriy Gapon 	struct l_ucred linux_ucred;
1265605da56bSAndriy Gapon 	socklen_t datalen, outlen;
126674f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
126774f5d680SKonstantin Belousov 	struct iovec *iov, *uiov;
126874f5d680SKonstantin Belousov 	struct mbuf *control = NULL;
126974f5d680SKonstantin Belousov 	struct mbuf **controlp;
127074f5d680SKonstantin Belousov 	caddr_t outbuf;
127174f5d680SKonstantin Belousov 	void *data;
12723a72bf04SDmitry Chagin 	int error, i, fd, fds, *fdp;
127340dbba57SAssar Westerlund 
127474f5d680SKonstantin Belousov 	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
1275ca26842eSHajimu UMEMOTO 	if (error)
1276ca26842eSHajimu UMEMOTO 		return (error);
1277ca26842eSHajimu UMEMOTO 
127874f5d680SKonstantin Belousov 	error = linux_to_bsd_msghdr(&msg, &linux_msg);
127974f5d680SKonstantin Belousov 	if (error)
128074f5d680SKonstantin Belousov 		return (error);
128174f5d680SKonstantin Belousov 
128274f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
128374f5d680SKonstantin Belousov 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
128474f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
128574f5d680SKonstantin Belousov #else
128674f5d680SKonstantin Belousov 	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
128774f5d680SKonstantin Belousov #endif
128874f5d680SKonstantin Belousov 	if (error)
128974f5d680SKonstantin Belousov 		return (error);
129074f5d680SKonstantin Belousov 
129174f5d680SKonstantin Belousov 	if (msg.msg_name) {
129274f5d680SKonstantin Belousov 		error = linux_to_bsd_sockaddr((struct sockaddr *)msg.msg_name,
129374f5d680SKonstantin Belousov 		    msg.msg_namelen);
129474f5d680SKonstantin Belousov 		if (error)
129574f5d680SKonstantin Belousov 			goto bad;
129684b11cd8SMitsuru IWASAKI 	}
129784b11cd8SMitsuru IWASAKI 
129874f5d680SKonstantin Belousov 	uiov = msg.msg_iov;
129974f5d680SKonstantin Belousov 	msg.msg_iov = iov;
130074f5d680SKonstantin Belousov 	controlp = (msg.msg_control != NULL) ? &control : NULL;
130174f5d680SKonstantin Belousov 	error = kern_recvit(td, args->s, &msg, UIO_USERSPACE, controlp);
130274f5d680SKonstantin Belousov 	msg.msg_iov = uiov;
1303ca26842eSHajimu UMEMOTO 	if (error)
130474f5d680SKonstantin Belousov 		goto bad;
130574f5d680SKonstantin Belousov 
130674f5d680SKonstantin Belousov 	error = bsd_to_linux_msghdr(&msg, &linux_msg);
130774f5d680SKonstantin Belousov 	if (error)
130874f5d680SKonstantin Belousov 		goto bad;
130974f5d680SKonstantin Belousov 
131074f5d680SKonstantin Belousov 	if (linux_msg.msg_name) {
131174f5d680SKonstantin Belousov 		error = bsd_to_linux_sockaddr((struct sockaddr *)
131274f5d680SKonstantin Belousov 		    PTRIN(linux_msg.msg_name));
131374f5d680SKonstantin Belousov 		if (error)
131474f5d680SKonstantin Belousov 			goto bad;
131574f5d680SKonstantin Belousov 	}
131674f5d680SKonstantin Belousov 	if (linux_msg.msg_name && linux_msg.msg_namelen > 2) {
131774f5d680SKonstantin Belousov 		error = linux_sa_put(PTRIN(linux_msg.msg_name));
131874f5d680SKonstantin Belousov 		if (error)
131974f5d680SKonstantin Belousov 			goto bad;
132074f5d680SKonstantin Belousov 	}
132174f5d680SKonstantin Belousov 
132274f5d680SKonstantin Belousov 	outbuf = PTRIN(linux_msg.msg_control);
132374f5d680SKonstantin Belousov 	outlen = 0;
1324605da56bSAndriy Gapon 
1325605da56bSAndriy Gapon 	if (control) {
1326605da56bSAndriy Gapon 		linux_cmsg = malloc(L_CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
1327605da56bSAndriy Gapon 
1328605da56bSAndriy Gapon 		msg.msg_control = mtod(control, struct cmsghdr *);
1329605da56bSAndriy Gapon 		msg.msg_controllen = control->m_len;
1330605da56bSAndriy Gapon 
1331605da56bSAndriy Gapon 		cm = CMSG_FIRSTHDR(&msg);
133274f5d680SKonstantin Belousov 
133374f5d680SKonstantin Belousov 		while (cm != NULL) {
1334605da56bSAndriy Gapon 			linux_cmsg->cmsg_type =
1335605da56bSAndriy Gapon 			    bsd_to_linux_cmsg_type(cm->cmsg_type);
1336605da56bSAndriy Gapon 			linux_cmsg->cmsg_level =
1337605da56bSAndriy Gapon 			    bsd_to_linux_sockopt_level(cm->cmsg_level);
1338605da56bSAndriy Gapon 			if (linux_cmsg->cmsg_type == -1
1339605da56bSAndriy Gapon 			    || cm->cmsg_level != SOL_SOCKET)
134074f5d680SKonstantin Belousov 			{
134174f5d680SKonstantin Belousov 				error = EINVAL;
134274f5d680SKonstantin Belousov 				goto bad;
134374f5d680SKonstantin Belousov 			}
1344605da56bSAndriy Gapon 
134574f5d680SKonstantin Belousov 			data = CMSG_DATA(cm);
134674f5d680SKonstantin Belousov 			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
134774f5d680SKonstantin Belousov 
1348605da56bSAndriy Gapon 			switch (cm->cmsg_type)
13493a72bf04SDmitry Chagin 			{
1350605da56bSAndriy Gapon 			case SCM_RIGHTS:
1351605da56bSAndriy Gapon 				if (args->flags & LINUX_MSG_CMSG_CLOEXEC) {
1352605da56bSAndriy Gapon 					fds = datalen / sizeof(int);
1353605da56bSAndriy Gapon 					fdp = data;
1354605da56bSAndriy Gapon 					for (i = 0; i < fds; i++) {
1355605da56bSAndriy Gapon 						fd = *fdp++;
1356605da56bSAndriy Gapon 						(void)kern_fcntl(td, fd,
1357605da56bSAndriy Gapon 						    F_SETFD, FD_CLOEXEC);
1358605da56bSAndriy Gapon 					}
1359605da56bSAndriy Gapon 				}
1360605da56bSAndriy Gapon 				break;
1361605da56bSAndriy Gapon 
1362605da56bSAndriy Gapon 			case SCM_CREDS:
1363605da56bSAndriy Gapon 				/*
1364605da56bSAndriy Gapon 				 * Currently LOCAL_CREDS is never in
1365605da56bSAndriy Gapon 				 * effect for Linux so no need to worry
1366605da56bSAndriy Gapon 				 * about sockcred
1367605da56bSAndriy Gapon 				 */
1368605da56bSAndriy Gapon 				if (datalen != sizeof (*cmcred)) {
1369605da56bSAndriy Gapon 					error = EMSGSIZE;
1370605da56bSAndriy Gapon 					goto bad;
1371605da56bSAndriy Gapon 				}
1372605da56bSAndriy Gapon 				cmcred = (struct cmsgcred *)data;
1373605da56bSAndriy Gapon 				bzero(&linux_ucred, sizeof(linux_ucred));
1374605da56bSAndriy Gapon 				linux_ucred.pid = cmcred->cmcred_pid;
1375605da56bSAndriy Gapon 				linux_ucred.uid = cmcred->cmcred_uid;
1376605da56bSAndriy Gapon 				linux_ucred.gid = cmcred->cmcred_gid;
1377605da56bSAndriy Gapon 				data = &linux_ucred;
1378605da56bSAndriy Gapon 				datalen = sizeof(linux_ucred);
1379605da56bSAndriy Gapon 				break;
1380605da56bSAndriy Gapon 			}
1381605da56bSAndriy Gapon 
138274f5d680SKonstantin Belousov 			if (outlen + LINUX_CMSG_LEN(datalen) >
138374f5d680SKonstantin Belousov 			    linux_msg.msg_controllen) {
138474f5d680SKonstantin Belousov 				if (outlen == 0) {
138574f5d680SKonstantin Belousov 					error = EMSGSIZE;
138674f5d680SKonstantin Belousov 					goto bad;
138774f5d680SKonstantin Belousov 				} else {
13883a72bf04SDmitry Chagin 					linux_msg.msg_flags |=
13893a72bf04SDmitry Chagin 					    LINUX_MSG_CTRUNC;
139074f5d680SKonstantin Belousov 					goto out;
139174f5d680SKonstantin Belousov 				}
139274f5d680SKonstantin Belousov 			}
139374f5d680SKonstantin Belousov 
139474f5d680SKonstantin Belousov 			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
139574f5d680SKonstantin Belousov 
139674f5d680SKonstantin Belousov 			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
139774f5d680SKonstantin Belousov 			if (error)
139874f5d680SKonstantin Belousov 				goto bad;
139974f5d680SKonstantin Belousov 			outbuf += L_CMSG_HDRSZ;
140074f5d680SKonstantin Belousov 
140174f5d680SKonstantin Belousov 			error = copyout(data, outbuf, datalen);
140274f5d680SKonstantin Belousov 			if (error)
140374f5d680SKonstantin Belousov 				goto bad;
140474f5d680SKonstantin Belousov 
140574f5d680SKonstantin Belousov 			outbuf += LINUX_CMSG_ALIGN(datalen);
140674f5d680SKonstantin Belousov 			outlen += LINUX_CMSG_LEN(datalen);
140774f5d680SKonstantin Belousov 
1408605da56bSAndriy Gapon 			cm = CMSG_NXTHDR(&msg, cm);
140974f5d680SKonstantin Belousov 		}
141074f5d680SKonstantin Belousov 	}
141174f5d680SKonstantin Belousov 
141274f5d680SKonstantin Belousov out:
1413605da56bSAndriy Gapon 	linux_msg.msg_controllen = outlen;
141474f5d680SKonstantin Belousov 	error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg));
141574f5d680SKonstantin Belousov 
141674f5d680SKonstantin Belousov bad:
141774f5d680SKonstantin Belousov 	free(iov, M_IOV);
141874f5d680SKonstantin Belousov 	if (control != NULL)
141974f5d680SKonstantin Belousov 		m_freem(control);
142074f5d680SKonstantin Belousov 	if (linux_cmsg != NULL)
142174f5d680SKonstantin Belousov 		free(linux_cmsg, M_TEMP);
142274f5d680SKonstantin Belousov 
1423ca26842eSHajimu UMEMOTO 	return (error);
142440dbba57SAssar Westerlund }
142540dbba57SAssar Westerlund 
1426c21dee17SSøren Schmidt struct linux_shutdown_args {
1427c21dee17SSøren Schmidt 	int s;
1428c21dee17SSøren Schmidt 	int how;
1429c21dee17SSøren Schmidt };
1430c21dee17SSøren Schmidt 
1431c21dee17SSøren Schmidt static int
1432b40ce416SJulian Elischer linux_shutdown(struct thread *td, struct linux_shutdown_args *args)
1433c21dee17SSøren Schmidt {
1434ef04503dSPeter Wemm 	struct shutdown_args /* {
1435c21dee17SSøren Schmidt 		int s;
1436c21dee17SSøren Schmidt 		int how;
1437ef04503dSPeter Wemm 	} */ bsd_args;
1438c21dee17SSøren Schmidt 
1439745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1440745aaef5SKonstantin Belousov 	bsd_args.how = args->how;
14418451d0ddSKip Macy 	return (sys_shutdown(td, &bsd_args));
1442c21dee17SSøren Schmidt }
1443c21dee17SSøren Schmidt 
1444c21dee17SSøren Schmidt struct linux_setsockopt_args {
1445c21dee17SSøren Schmidt 	int s;
1446c21dee17SSøren Schmidt 	int level;
1447c21dee17SSøren Schmidt 	int optname;
14484af27623STim J. Robbins 	l_uintptr_t optval;
1449c21dee17SSøren Schmidt 	int optlen;
1450c21dee17SSøren Schmidt };
1451c21dee17SSøren Schmidt 
1452c21dee17SSøren Schmidt static int
1453b40ce416SJulian Elischer linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
1454c21dee17SSøren Schmidt {
1455ef04503dSPeter Wemm 	struct setsockopt_args /* {
1456c21dee17SSøren Schmidt 		int s;
1457c21dee17SSøren Schmidt 		int level;
1458c21dee17SSøren Schmidt 		int name;
1459c21dee17SSøren Schmidt 		caddr_t val;
1460c21dee17SSøren Schmidt 		int valsize;
1461ef04503dSPeter Wemm 	} */ bsd_args;
146203cc95d2SDmitry Chagin 	l_timeval linux_tv;
146303cc95d2SDmitry Chagin 	struct timeval tv;
1464c21dee17SSøren Schmidt 	int error, name;
1465c21dee17SSøren Schmidt 
1466745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1467745aaef5SKonstantin Belousov 	bsd_args.level = linux_to_bsd_sockopt_level(args->level);
1468c21dee17SSøren Schmidt 	switch (bsd_args.level) {
1469c21dee17SSøren Schmidt 	case SOL_SOCKET:
1470745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
147103cc95d2SDmitry Chagin 		switch (name) {
147203cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
147303cc95d2SDmitry Chagin 			/* FALLTHROUGH */
147403cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
147503cc95d2SDmitry Chagin 			error = copyin(PTRIN(args->optval), &linux_tv,
147603cc95d2SDmitry Chagin 			    sizeof(linux_tv));
147703cc95d2SDmitry Chagin 			if (error)
147803cc95d2SDmitry Chagin 				return (error);
147903cc95d2SDmitry Chagin 			tv.tv_sec = linux_tv.tv_sec;
148003cc95d2SDmitry Chagin 			tv.tv_usec = linux_tv.tv_usec;
148103cc95d2SDmitry Chagin 			return (kern_setsockopt(td, args->s, bsd_args.level,
148203cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, sizeof(tv)));
148303cc95d2SDmitry Chagin 			/* NOTREACHED */
148403cc95d2SDmitry Chagin 			break;
148503cc95d2SDmitry Chagin 		default:
148603cc95d2SDmitry Chagin 			break;
148703cc95d2SDmitry Chagin 		}
1488c21dee17SSøren Schmidt 		break;
1489c21dee17SSøren Schmidt 	case IPPROTO_IP:
1490745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1491c21dee17SSøren Schmidt 		break;
1492dad3b88aSMike Smith 	case IPPROTO_TCP:
1493dad3b88aSMike Smith 		/* Linux TCP option values match BSD's */
1494745aaef5SKonstantin Belousov 		name = args->optname;
1495dad3b88aSMike Smith 		break;
1496c21dee17SSøren Schmidt 	default:
14973f3a4815SMarcel Moolenaar 		name = -1;
14983f3a4815SMarcel Moolenaar 		break;
1499c21dee17SSøren Schmidt 	}
1500c21dee17SSøren Schmidt 	if (name == -1)
1501d4b7423fSAlexander Leidinger 		return (ENOPROTOOPT);
15023f3a4815SMarcel Moolenaar 
1503c21dee17SSøren Schmidt 	bsd_args.name = name;
1504745aaef5SKonstantin Belousov 	bsd_args.val = PTRIN(args->optval);
1505745aaef5SKonstantin Belousov 	bsd_args.valsize = args->optlen;
15065c8919adSAlexander Leidinger 
15075c8919adSAlexander Leidinger 	if (name == IPV6_NEXTHOP) {
15085c8919adSAlexander Leidinger 		linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.val,
15095c8919adSAlexander Leidinger 			bsd_args.valsize);
15108451d0ddSKip Macy 		error = sys_setsockopt(td, &bsd_args);
15115c8919adSAlexander Leidinger 		bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
15125c8919adSAlexander Leidinger 	} else
15138451d0ddSKip Macy 		error = sys_setsockopt(td, &bsd_args);
15145c8919adSAlexander Leidinger 
15155c8919adSAlexander Leidinger 	return (error);
1516c21dee17SSøren Schmidt }
1517c21dee17SSøren Schmidt 
1518c21dee17SSøren Schmidt struct linux_getsockopt_args {
1519c21dee17SSøren Schmidt 	int s;
1520c21dee17SSøren Schmidt 	int level;
1521c21dee17SSøren Schmidt 	int optname;
15224af27623STim J. Robbins 	l_uintptr_t optval;
15234af27623STim J. Robbins 	l_uintptr_t optlen;
1524c21dee17SSøren Schmidt };
1525c21dee17SSøren Schmidt 
1526c21dee17SSøren Schmidt static int
1527b40ce416SJulian Elischer linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
1528c21dee17SSøren Schmidt {
1529ef04503dSPeter Wemm 	struct getsockopt_args /* {
1530c21dee17SSøren Schmidt 		int s;
1531c21dee17SSøren Schmidt 		int level;
1532c21dee17SSøren Schmidt 		int name;
1533c21dee17SSøren Schmidt 		caddr_t val;
1534c21dee17SSøren Schmidt 		int *avalsize;
1535ef04503dSPeter Wemm 	} */ bsd_args;
153603cc95d2SDmitry Chagin 	l_timeval linux_tv;
153703cc95d2SDmitry Chagin 	struct timeval tv;
1538d4dd69c4SDmitry Chagin 	socklen_t tv_len, xulen;
1539d4dd69c4SDmitry Chagin 	struct xucred xu;
1540d4dd69c4SDmitry Chagin 	struct l_ucred lxu;
1541c21dee17SSøren Schmidt 	int error, name;
1542c21dee17SSøren Schmidt 
1543745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1544745aaef5SKonstantin Belousov 	bsd_args.level = linux_to_bsd_sockopt_level(args->level);
1545c21dee17SSøren Schmidt 	switch (bsd_args.level) {
1546c21dee17SSøren Schmidt 	case SOL_SOCKET:
1547745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
154803cc95d2SDmitry Chagin 		switch (name) {
154903cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
155003cc95d2SDmitry Chagin 			/* FALLTHROUGH */
155103cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
155203cc95d2SDmitry Chagin 			tv_len = sizeof(tv);
155303cc95d2SDmitry Chagin 			error = kern_getsockopt(td, args->s, bsd_args.level,
155403cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, &tv_len);
155503cc95d2SDmitry Chagin 			if (error)
155603cc95d2SDmitry Chagin 				return (error);
155703cc95d2SDmitry Chagin 			linux_tv.tv_sec = tv.tv_sec;
155803cc95d2SDmitry Chagin 			linux_tv.tv_usec = tv.tv_usec;
155903cc95d2SDmitry Chagin 			return (copyout(&linux_tv, PTRIN(args->optval),
156003cc95d2SDmitry Chagin 			    sizeof(linux_tv)));
156103cc95d2SDmitry Chagin 			/* NOTREACHED */
156203cc95d2SDmitry Chagin 			break;
1563d4dd69c4SDmitry Chagin 		case LOCAL_PEERCRED:
1564d4dd69c4SDmitry Chagin 			if (args->optlen != sizeof(lxu))
1565d4dd69c4SDmitry Chagin 				return (EINVAL);
1566d4dd69c4SDmitry Chagin 			xulen = sizeof(xu);
1567d4dd69c4SDmitry Chagin 			error = kern_getsockopt(td, args->s, bsd_args.level,
1568d4dd69c4SDmitry Chagin 			    name, &xu, UIO_SYSSPACE, &xulen);
1569d4dd69c4SDmitry Chagin 			if (error)
1570d4dd69c4SDmitry Chagin 				return (error);
1571d4dd69c4SDmitry Chagin 			/*
1572d4dd69c4SDmitry Chagin 			 * XXX Use 0 for pid as the FreeBSD does not cache peer pid.
1573d4dd69c4SDmitry Chagin 			 */
1574d4dd69c4SDmitry Chagin 			lxu.pid = 0;
1575d4dd69c4SDmitry Chagin 			lxu.uid = xu.cr_uid;
1576d4dd69c4SDmitry Chagin 			lxu.gid = xu.cr_gid;
1577d4dd69c4SDmitry Chagin 			return (copyout(&lxu, PTRIN(args->optval), sizeof(lxu)));
1578d4dd69c4SDmitry Chagin 			/* NOTREACHED */
1579d4dd69c4SDmitry Chagin 			break;
158003cc95d2SDmitry Chagin 		default:
158103cc95d2SDmitry Chagin 			break;
158203cc95d2SDmitry Chagin 		}
1583c21dee17SSøren Schmidt 		break;
1584c21dee17SSøren Schmidt 	case IPPROTO_IP:
1585745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1586c21dee17SSøren Schmidt 		break;
1587dad3b88aSMike Smith 	case IPPROTO_TCP:
1588dad3b88aSMike Smith 		/* Linux TCP option values match BSD's */
1589745aaef5SKonstantin Belousov 		name = args->optname;
1590dad3b88aSMike Smith 		break;
1591c21dee17SSøren Schmidt 	default:
15923f3a4815SMarcel Moolenaar 		name = -1;
15933f3a4815SMarcel Moolenaar 		break;
1594c21dee17SSøren Schmidt 	}
1595c21dee17SSøren Schmidt 	if (name == -1)
15963f3a4815SMarcel Moolenaar 		return (EINVAL);
15973f3a4815SMarcel Moolenaar 
1598f2477ae1SMike Smith 	bsd_args.name = name;
1599745aaef5SKonstantin Belousov 	bsd_args.val = PTRIN(args->optval);
1600745aaef5SKonstantin Belousov 	bsd_args.avalsize = PTRIN(args->optlen);
16015c8919adSAlexander Leidinger 
16025c8919adSAlexander Leidinger 	if (name == IPV6_NEXTHOP) {
16038451d0ddSKip Macy 		error = sys_getsockopt(td, &bsd_args);
16045c8919adSAlexander Leidinger 		bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
16055c8919adSAlexander Leidinger 	} else
16068451d0ddSKip Macy 		error = sys_getsockopt(td, &bsd_args);
16075c8919adSAlexander Leidinger 
16085c8919adSAlexander Leidinger 	return (error);
1609c21dee17SSøren Schmidt }
1610c21dee17SSøren Schmidt 
1611ea7b81d2SDmitry Chagin /* Argument list sizes for linux_socketcall */
1612ea7b81d2SDmitry Chagin 
1613ea7b81d2SDmitry Chagin #define LINUX_AL(x) ((x) * sizeof(l_ulong))
1614ea7b81d2SDmitry Chagin 
1615ea7b81d2SDmitry Chagin static const unsigned char lxs_args[] = {
1616ea7b81d2SDmitry Chagin 	LINUX_AL(0) /* unused*/,	LINUX_AL(3) /* socket */,
1617ea7b81d2SDmitry Chagin 	LINUX_AL(3) /* bind */,		LINUX_AL(3) /* connect */,
1618ea7b81d2SDmitry Chagin 	LINUX_AL(2) /* listen */,	LINUX_AL(3) /* accept */,
1619ea7b81d2SDmitry Chagin 	LINUX_AL(3) /* getsockname */,	LINUX_AL(3) /* getpeername */,
1620ea7b81d2SDmitry Chagin 	LINUX_AL(4) /* socketpair */,	LINUX_AL(4) /* send */,
1621ea7b81d2SDmitry Chagin 	LINUX_AL(4) /* recv */,		LINUX_AL(6) /* sendto */,
1622ea7b81d2SDmitry Chagin 	LINUX_AL(6) /* recvfrom */,	LINUX_AL(2) /* shutdown */,
1623ea7b81d2SDmitry Chagin 	LINUX_AL(5) /* setsockopt */,	LINUX_AL(5) /* getsockopt */,
1624f8cd0af2SDmitry Chagin 	LINUX_AL(3) /* sendmsg */,	LINUX_AL(3) /* recvmsg */,
1625f8cd0af2SDmitry Chagin 	LINUX_AL(4) /* accept4 */
1626ea7b81d2SDmitry Chagin };
1627ea7b81d2SDmitry Chagin 
1628ea7b81d2SDmitry Chagin #define	LINUX_AL_SIZE	sizeof(lxs_args) / sizeof(lxs_args[0]) - 1
1629ea7b81d2SDmitry Chagin 
1630c21dee17SSøren Schmidt int
1631b40ce416SJulian Elischer linux_socketcall(struct thread *td, struct linux_socketcall_args *args)
1632c21dee17SSøren Schmidt {
1633ea7b81d2SDmitry Chagin 	l_ulong a[6];
1634ea7b81d2SDmitry Chagin 	void *arg;
1635ea7b81d2SDmitry Chagin 	int error;
16363f3a4815SMarcel Moolenaar 
1637ea7b81d2SDmitry Chagin 	if (args->what < LINUX_SOCKET || args->what > LINUX_AL_SIZE)
1638ea7b81d2SDmitry Chagin 		return (EINVAL);
1639ea7b81d2SDmitry Chagin 	error = copyin(PTRIN(args->args), a, lxs_args[args->what]);
1640ea7b81d2SDmitry Chagin 	if (error)
1641ea7b81d2SDmitry Chagin 		return (error);
1642ea7b81d2SDmitry Chagin 
1643ea7b81d2SDmitry Chagin 	arg = a;
1644c21dee17SSøren Schmidt 	switch (args->what) {
1645c21dee17SSøren Schmidt 	case LINUX_SOCKET:
1646b40ce416SJulian Elischer 		return (linux_socket(td, arg));
1647c21dee17SSøren Schmidt 	case LINUX_BIND:
1648b40ce416SJulian Elischer 		return (linux_bind(td, arg));
1649c21dee17SSøren Schmidt 	case LINUX_CONNECT:
1650b40ce416SJulian Elischer 		return (linux_connect(td, arg));
1651c21dee17SSøren Schmidt 	case LINUX_LISTEN:
1652b40ce416SJulian Elischer 		return (linux_listen(td, arg));
1653c21dee17SSøren Schmidt 	case LINUX_ACCEPT:
1654b40ce416SJulian Elischer 		return (linux_accept(td, arg));
1655c21dee17SSøren Schmidt 	case LINUX_GETSOCKNAME:
1656b40ce416SJulian Elischer 		return (linux_getsockname(td, arg));
1657c21dee17SSøren Schmidt 	case LINUX_GETPEERNAME:
1658b40ce416SJulian Elischer 		return (linux_getpeername(td, arg));
1659c21dee17SSøren Schmidt 	case LINUX_SOCKETPAIR:
1660b40ce416SJulian Elischer 		return (linux_socketpair(td, arg));
1661c21dee17SSøren Schmidt 	case LINUX_SEND:
1662b40ce416SJulian Elischer 		return (linux_send(td, arg));
1663c21dee17SSøren Schmidt 	case LINUX_RECV:
1664b40ce416SJulian Elischer 		return (linux_recv(td, arg));
1665c21dee17SSøren Schmidt 	case LINUX_SENDTO:
1666b40ce416SJulian Elischer 		return (linux_sendto(td, arg));
1667c21dee17SSøren Schmidt 	case LINUX_RECVFROM:
1668b40ce416SJulian Elischer 		return (linux_recvfrom(td, arg));
1669c21dee17SSøren Schmidt 	case LINUX_SHUTDOWN:
1670b40ce416SJulian Elischer 		return (linux_shutdown(td, arg));
1671c21dee17SSøren Schmidt 	case LINUX_SETSOCKOPT:
1672b40ce416SJulian Elischer 		return (linux_setsockopt(td, arg));
1673c21dee17SSøren Schmidt 	case LINUX_GETSOCKOPT:
1674b40ce416SJulian Elischer 		return (linux_getsockopt(td, arg));
1675e76bba09SSøren Schmidt 	case LINUX_SENDMSG:
1676ca26842eSHajimu UMEMOTO 		return (linux_sendmsg(td, arg));
1677e76bba09SSøren Schmidt 	case LINUX_RECVMSG:
1678b40ce416SJulian Elischer 		return (linux_recvmsg(td, arg));
1679f8cd0af2SDmitry Chagin 	case LINUX_ACCEPT4:
1680f8cd0af2SDmitry Chagin 		return (linux_accept4(td, arg));
1681c21dee17SSøren Schmidt 	}
16823f3a4815SMarcel Moolenaar 
16833f3a4815SMarcel Moolenaar 	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
16843f3a4815SMarcel Moolenaar 	return (ENOSYS);
1685c21dee17SSøren Schmidt }
1686