xref: /freebsd/sys/compat/linux/linux_socket.c (revision 0007f669caf68d2abf6b924b82263d590ef63cce)
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
84*0007f669SJung-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 
95*0007f669SJung-uk Kim 	if (osalen < 2 || osalen > UCHAR_MAX || !osa)
96ca26842eSHajimu UMEMOTO 		return (EINVAL);
97ca26842eSHajimu UMEMOTO 
98*0007f669SJung-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 
112*0007f669SJung-uk Kim 	kosa = malloc(alloclen, M_SONAME, M_WAITOK);
113ca26842eSHajimu UMEMOTO 
114*0007f669SJung-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 	 */
131ca26842eSHajimu UMEMOTO 	if (oldv6size && bdom == AF_INET6) {
132ca26842eSHajimu UMEMOTO 		sin6 = (struct sockaddr_in6 *)kosa;
133ca26842eSHajimu UMEMOTO 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
134ca26842eSHajimu UMEMOTO 		    (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
135ca26842eSHajimu UMEMOTO 		     !IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) &&
136ca26842eSHajimu UMEMOTO 		     !IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) &&
137ca26842eSHajimu UMEMOTO 		     !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
138ca26842eSHajimu UMEMOTO 		     !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) {
139ca26842eSHajimu UMEMOTO 			sin6->sin6_scope_id = 0;
140ca26842eSHajimu UMEMOTO 		} else {
141ca26842eSHajimu UMEMOTO 			log(LOG_DEBUG,
1423c616032SGleb Smirnoff 			    "obsolete pre-RFC2553 sockaddr_in6 rejected\n");
143ca26842eSHajimu UMEMOTO 			error = EINVAL;
144ca26842eSHajimu UMEMOTO 			goto out;
145ca26842eSHajimu UMEMOTO 		}
146ca26842eSHajimu UMEMOTO 	} else
147ca26842eSHajimu UMEMOTO #endif
1485cb9c68cSXin LI 	if (bdom == AF_INET) {
149ca26842eSHajimu UMEMOTO 		alloclen = sizeof(struct sockaddr_in);
150*0007f669SJung-uk Kim 		if (osalen < alloclen) {
1515cb9c68cSXin LI 			error = EINVAL;
1525cb9c68cSXin LI 			goto out;
1535cb9c68cSXin LI 		}
1545cb9c68cSXin LI 	}
155ca26842eSHajimu UMEMOTO 
156*0007f669SJung-uk Kim 	if (bdom == AF_LOCAL && osalen > sizeof(struct sockaddr_un)) {
157c15cdbf2SJung-uk Kim 		hdrlen = offsetof(struct sockaddr_un, sun_path);
158c15cdbf2SJung-uk Kim 		namelen = strnlen(((struct sockaddr_un *)kosa)->sun_path,
159*0007f669SJung-uk Kim 		    osalen - hdrlen);
160c15cdbf2SJung-uk Kim 		if (hdrlen + namelen > sizeof(struct sockaddr_un)) {
1615da3eb94SColin Percival 			error = EINVAL;
1625da3eb94SColin Percival 			goto out;
1635da3eb94SColin Percival 		}
1645da3eb94SColin Percival 		alloclen = sizeof(struct sockaddr_un);
1655da3eb94SColin Percival 	}
1665da3eb94SColin Percival 
167ca26842eSHajimu UMEMOTO 	sa = (struct sockaddr *) kosa;
168ca26842eSHajimu UMEMOTO 	sa->sa_family = bdom;
169ca26842eSHajimu UMEMOTO 	sa->sa_len = alloclen;
170ca26842eSHajimu UMEMOTO 
171ca26842eSHajimu UMEMOTO 	*sap = sa;
172ca26842eSHajimu UMEMOTO 	return (0);
173ca26842eSHajimu UMEMOTO 
174ca26842eSHajimu UMEMOTO out:
175*0007f669SJung-uk Kim 	free(kosa, M_SONAME);
176ca26842eSHajimu UMEMOTO 	return (error);
177ca26842eSHajimu UMEMOTO }
178ca26842eSHajimu UMEMOTO 
179c21dee17SSøren Schmidt static int
180c21dee17SSøren Schmidt linux_to_bsd_domain(int domain)
181c21dee17SSøren Schmidt {
1823f3a4815SMarcel Moolenaar 
183c21dee17SSøren Schmidt 	switch (domain) {
184c21dee17SSøren Schmidt 	case LINUX_AF_UNSPEC:
1853f3a4815SMarcel Moolenaar 		return (AF_UNSPEC);
186c21dee17SSøren Schmidt 	case LINUX_AF_UNIX:
1873f3a4815SMarcel Moolenaar 		return (AF_LOCAL);
188c21dee17SSøren Schmidt 	case LINUX_AF_INET:
1893f3a4815SMarcel Moolenaar 		return (AF_INET);
190ca26842eSHajimu UMEMOTO 	case LINUX_AF_INET6:
191ca26842eSHajimu UMEMOTO 		return (AF_INET6);
192c21dee17SSøren Schmidt 	case LINUX_AF_AX25:
1933f3a4815SMarcel Moolenaar 		return (AF_CCITT);
194c21dee17SSøren Schmidt 	case LINUX_AF_IPX:
1953f3a4815SMarcel Moolenaar 		return (AF_IPX);
196c21dee17SSøren Schmidt 	case LINUX_AF_APPLETALK:
1973f3a4815SMarcel Moolenaar 		return (AF_APPLETALK);
198c21dee17SSøren Schmidt 	}
1993f3a4815SMarcel Moolenaar 	return (-1);
200c21dee17SSøren Schmidt }
201c21dee17SSøren Schmidt 
202ca26842eSHajimu UMEMOTO static int
203ca26842eSHajimu UMEMOTO bsd_to_linux_domain(int domain)
204ca26842eSHajimu UMEMOTO {
205ca26842eSHajimu UMEMOTO 
206ca26842eSHajimu UMEMOTO 	switch (domain) {
207ca26842eSHajimu UMEMOTO 	case AF_UNSPEC:
208ca26842eSHajimu UMEMOTO 		return (LINUX_AF_UNSPEC);
209ca26842eSHajimu UMEMOTO 	case AF_LOCAL:
210ca26842eSHajimu UMEMOTO 		return (LINUX_AF_UNIX);
211ca26842eSHajimu UMEMOTO 	case AF_INET:
212ca26842eSHajimu UMEMOTO 		return (LINUX_AF_INET);
213ca26842eSHajimu UMEMOTO 	case AF_INET6:
214ca26842eSHajimu UMEMOTO 		return (LINUX_AF_INET6);
215ca26842eSHajimu UMEMOTO 	case AF_CCITT:
216ca26842eSHajimu UMEMOTO 		return (LINUX_AF_AX25);
217ca26842eSHajimu UMEMOTO 	case AF_IPX:
218ca26842eSHajimu UMEMOTO 		return (LINUX_AF_IPX);
219ca26842eSHajimu UMEMOTO 	case AF_APPLETALK:
220ca26842eSHajimu UMEMOTO 		return (LINUX_AF_APPLETALK);
221ca26842eSHajimu UMEMOTO 	}
222ca26842eSHajimu UMEMOTO 	return (-1);
223ca26842eSHajimu UMEMOTO }
224ca26842eSHajimu UMEMOTO 
225c21dee17SSøren Schmidt static int
226c21dee17SSøren Schmidt linux_to_bsd_sockopt_level(int level)
227c21dee17SSøren Schmidt {
2283f3a4815SMarcel Moolenaar 
229c21dee17SSøren Schmidt 	switch (level) {
230c21dee17SSøren Schmidt 	case LINUX_SOL_SOCKET:
2313f3a4815SMarcel Moolenaar 		return (SOL_SOCKET);
232c21dee17SSøren Schmidt 	}
2333f3a4815SMarcel Moolenaar 	return (level);
234c21dee17SSøren Schmidt }
235c21dee17SSøren Schmidt 
2363f3a4815SMarcel Moolenaar static int
23784b11cd8SMitsuru IWASAKI bsd_to_linux_sockopt_level(int level)
23884b11cd8SMitsuru IWASAKI {
23984b11cd8SMitsuru IWASAKI 
24084b11cd8SMitsuru IWASAKI 	switch (level) {
24184b11cd8SMitsuru IWASAKI 	case SOL_SOCKET:
24284b11cd8SMitsuru IWASAKI 		return (LINUX_SOL_SOCKET);
24384b11cd8SMitsuru IWASAKI 	}
24484b11cd8SMitsuru IWASAKI 	return (level);
24584b11cd8SMitsuru IWASAKI }
24684b11cd8SMitsuru IWASAKI 
24784b11cd8SMitsuru IWASAKI static int
2483f3a4815SMarcel Moolenaar linux_to_bsd_ip_sockopt(int opt)
249c21dee17SSøren Schmidt {
2503f3a4815SMarcel Moolenaar 
251c21dee17SSøren Schmidt 	switch (opt) {
252c21dee17SSøren Schmidt 	case LINUX_IP_TOS:
2533f3a4815SMarcel Moolenaar 		return (IP_TOS);
254c21dee17SSøren Schmidt 	case LINUX_IP_TTL:
2553f3a4815SMarcel Moolenaar 		return (IP_TTL);
25666ff6a3cSBill Fenner 	case LINUX_IP_OPTIONS:
2573f3a4815SMarcel Moolenaar 		return (IP_OPTIONS);
25866ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_IF:
2593f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_IF);
26066ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_TTL:
2613f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_TTL);
26266ff6a3cSBill Fenner 	case LINUX_IP_MULTICAST_LOOP:
2633f3a4815SMarcel Moolenaar 		return (IP_MULTICAST_LOOP);
26466ff6a3cSBill Fenner 	case LINUX_IP_ADD_MEMBERSHIP:
2653f3a4815SMarcel Moolenaar 		return (IP_ADD_MEMBERSHIP);
26666ff6a3cSBill Fenner 	case LINUX_IP_DROP_MEMBERSHIP:
2673f3a4815SMarcel Moolenaar 		return (IP_DROP_MEMBERSHIP);
26866ff6a3cSBill Fenner 	case LINUX_IP_HDRINCL:
2693f3a4815SMarcel Moolenaar 		return (IP_HDRINCL);
270c21dee17SSøren Schmidt 	}
2713f3a4815SMarcel Moolenaar 	return (-1);
272c21dee17SSøren Schmidt }
273c21dee17SSøren Schmidt 
274c21dee17SSøren Schmidt static int
275c21dee17SSøren Schmidt linux_to_bsd_so_sockopt(int opt)
276c21dee17SSøren Schmidt {
2773f3a4815SMarcel Moolenaar 
278c21dee17SSøren Schmidt 	switch (opt) {
279c21dee17SSøren Schmidt 	case LINUX_SO_DEBUG:
2803f3a4815SMarcel Moolenaar 		return (SO_DEBUG);
281c21dee17SSøren Schmidt 	case LINUX_SO_REUSEADDR:
2823f3a4815SMarcel Moolenaar 		return (SO_REUSEADDR);
283c21dee17SSøren Schmidt 	case LINUX_SO_TYPE:
2843f3a4815SMarcel Moolenaar 		return (SO_TYPE);
285c21dee17SSøren Schmidt 	case LINUX_SO_ERROR:
2863f3a4815SMarcel Moolenaar 		return (SO_ERROR);
287c21dee17SSøren Schmidt 	case LINUX_SO_DONTROUTE:
2883f3a4815SMarcel Moolenaar 		return (SO_DONTROUTE);
289c21dee17SSøren Schmidt 	case LINUX_SO_BROADCAST:
2903f3a4815SMarcel Moolenaar 		return (SO_BROADCAST);
291c21dee17SSøren Schmidt 	case LINUX_SO_SNDBUF:
2923f3a4815SMarcel Moolenaar 		return (SO_SNDBUF);
293c21dee17SSøren Schmidt 	case LINUX_SO_RCVBUF:
2943f3a4815SMarcel Moolenaar 		return (SO_RCVBUF);
295c21dee17SSøren Schmidt 	case LINUX_SO_KEEPALIVE:
2963f3a4815SMarcel Moolenaar 		return (SO_KEEPALIVE);
297c21dee17SSøren Schmidt 	case LINUX_SO_OOBINLINE:
2983f3a4815SMarcel Moolenaar 		return (SO_OOBINLINE);
299c21dee17SSøren Schmidt 	case LINUX_SO_LINGER:
3003f3a4815SMarcel Moolenaar 		return (SO_LINGER);
301d0b2365eSKonstantin Belousov 	case LINUX_SO_PEERCRED:
302d0b2365eSKonstantin Belousov 		return (LOCAL_PEERCRED);
303d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVLOWAT:
304d0b2365eSKonstantin Belousov 		return (SO_RCVLOWAT);
305d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDLOWAT:
306d0b2365eSKonstantin Belousov 		return (SO_SNDLOWAT);
307d0b2365eSKonstantin Belousov 	case LINUX_SO_RCVTIMEO:
308d0b2365eSKonstantin Belousov 		return (SO_RCVTIMEO);
309d0b2365eSKonstantin Belousov 	case LINUX_SO_SNDTIMEO:
310d0b2365eSKonstantin Belousov 		return (SO_SNDTIMEO);
311d0b2365eSKonstantin Belousov 	case LINUX_SO_TIMESTAMP:
312d0b2365eSKonstantin Belousov 		return (SO_TIMESTAMP);
313d0b2365eSKonstantin Belousov 	case LINUX_SO_ACCEPTCONN:
314d0b2365eSKonstantin Belousov 		return (SO_ACCEPTCONN);
315c21dee17SSøren Schmidt 	}
3163f3a4815SMarcel Moolenaar 	return (-1);
317c21dee17SSøren Schmidt }
318c21dee17SSøren Schmidt 
31940dbba57SAssar Westerlund static int
32040dbba57SAssar Westerlund linux_to_bsd_msg_flags(int flags)
32140dbba57SAssar Westerlund {
32240dbba57SAssar Westerlund 	int ret_flags = 0;
32340dbba57SAssar Westerlund 
32440dbba57SAssar Westerlund 	if (flags & LINUX_MSG_OOB)
32540dbba57SAssar Westerlund 		ret_flags |= MSG_OOB;
32640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PEEK)
32740dbba57SAssar Westerlund 		ret_flags |= MSG_PEEK;
32840dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTROUTE)
32940dbba57SAssar Westerlund 		ret_flags |= MSG_DONTROUTE;
33040dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CTRUNC)
33140dbba57SAssar Westerlund 		ret_flags |= MSG_CTRUNC;
33240dbba57SAssar Westerlund 	if (flags & LINUX_MSG_TRUNC)
33340dbba57SAssar Westerlund 		ret_flags |= MSG_TRUNC;
33440dbba57SAssar Westerlund 	if (flags & LINUX_MSG_DONTWAIT)
33540dbba57SAssar Westerlund 		ret_flags |= MSG_DONTWAIT;
33640dbba57SAssar Westerlund 	if (flags & LINUX_MSG_EOR)
33740dbba57SAssar Westerlund 		ret_flags |= MSG_EOR;
33840dbba57SAssar Westerlund 	if (flags & LINUX_MSG_WAITALL)
33940dbba57SAssar Westerlund 		ret_flags |= MSG_WAITALL;
3408d6e40c3SMaxim Sobolev 	if (flags & LINUX_MSG_NOSIGNAL)
3418d6e40c3SMaxim Sobolev 		ret_flags |= MSG_NOSIGNAL;
34240dbba57SAssar Westerlund #if 0 /* not handled */
34340dbba57SAssar Westerlund 	if (flags & LINUX_MSG_PROXY)
34440dbba57SAssar Westerlund 		;
34540dbba57SAssar Westerlund 	if (flags & LINUX_MSG_FIN)
34640dbba57SAssar Westerlund 		;
34740dbba57SAssar Westerlund 	if (flags & LINUX_MSG_SYN)
34840dbba57SAssar Westerlund 		;
34940dbba57SAssar Westerlund 	if (flags & LINUX_MSG_CONFIRM)
35040dbba57SAssar Westerlund 		;
35140dbba57SAssar Westerlund 	if (flags & LINUX_MSG_RST)
35240dbba57SAssar Westerlund 		;
35340dbba57SAssar Westerlund 	if (flags & LINUX_MSG_ERRQUEUE)
35440dbba57SAssar Westerlund 		;
35540dbba57SAssar Westerlund #endif
35640dbba57SAssar Westerlund 	return ret_flags;
35740dbba57SAssar Westerlund }
35840dbba57SAssar Westerlund 
3595c8919adSAlexander Leidinger /*
3605c8919adSAlexander Leidinger * If bsd_to_linux_sockaddr() or linux_to_bsd_sockaddr() faults, then the
3615c8919adSAlexander Leidinger * native syscall will fault.  Thus, we don't really need to check the
3625c8919adSAlexander Leidinger * return values for these functions.
3635c8919adSAlexander Leidinger */
3645c8919adSAlexander Leidinger 
3655c8919adSAlexander Leidinger static int
3665c8919adSAlexander Leidinger bsd_to_linux_sockaddr(struct sockaddr *arg)
3675c8919adSAlexander Leidinger {
3685c8919adSAlexander Leidinger 	struct sockaddr sa;
3695c8919adSAlexander Leidinger 	size_t sa_len = sizeof(struct sockaddr);
3705c8919adSAlexander Leidinger 	int error;
3715c8919adSAlexander Leidinger 
3725c8919adSAlexander Leidinger 	if ((error = copyin(arg, &sa, sa_len)))
3735c8919adSAlexander Leidinger 		return (error);
3745c8919adSAlexander Leidinger 
3755c8919adSAlexander Leidinger 	*(u_short *)&sa = sa.sa_family;
3765c8919adSAlexander Leidinger 
3775c8919adSAlexander Leidinger 	error = copyout(&sa, arg, sa_len);
3785c8919adSAlexander Leidinger 
3795c8919adSAlexander Leidinger 	return (error);
3805c8919adSAlexander Leidinger }
3815c8919adSAlexander Leidinger 
3825c8919adSAlexander Leidinger static int
3835c8919adSAlexander Leidinger linux_to_bsd_sockaddr(struct sockaddr *arg, int len)
3845c8919adSAlexander Leidinger {
3855c8919adSAlexander Leidinger 	struct sockaddr sa;
3865c8919adSAlexander Leidinger 	size_t sa_len = sizeof(struct sockaddr);
3875c8919adSAlexander Leidinger 	int error;
3885c8919adSAlexander Leidinger 
3895c8919adSAlexander Leidinger 	if ((error = copyin(arg, &sa, sa_len)))
3905c8919adSAlexander Leidinger 		return (error);
3915c8919adSAlexander Leidinger 
3925c8919adSAlexander Leidinger 	sa.sa_family = *(sa_family_t *)&sa;
3935c8919adSAlexander Leidinger 	sa.sa_len = len;
3945c8919adSAlexander Leidinger 
3955c8919adSAlexander Leidinger 	error = copyout(&sa, arg, sa_len);
3965c8919adSAlexander Leidinger 
3975c8919adSAlexander Leidinger 	return (error);
3985c8919adSAlexander Leidinger }
3995c8919adSAlexander Leidinger 
4005c8919adSAlexander Leidinger 
401ca26842eSHajimu UMEMOTO static int
402ca26842eSHajimu UMEMOTO linux_sa_put(struct osockaddr *osa)
403ca26842eSHajimu UMEMOTO {
404ca26842eSHajimu UMEMOTO 	struct osockaddr sa;
405ca26842eSHajimu UMEMOTO 	int error, bdom;
406ca26842eSHajimu UMEMOTO 
407ca26842eSHajimu UMEMOTO 	/*
408ca26842eSHajimu UMEMOTO 	 * Only read/write the osockaddr family part, the rest is
409ca26842eSHajimu UMEMOTO 	 * not changed.
410ca26842eSHajimu UMEMOTO 	 */
4114b7ef73dSDag-Erling Smørgrav 	error = copyin(osa, &sa, sizeof(sa.sa_family));
412ca26842eSHajimu UMEMOTO 	if (error)
413ca26842eSHajimu UMEMOTO 		return (error);
414ca26842eSHajimu UMEMOTO 
415ca26842eSHajimu UMEMOTO 	bdom = bsd_to_linux_domain(sa.sa_family);
416ca26842eSHajimu UMEMOTO 	if (bdom == -1)
417ca26842eSHajimu UMEMOTO 		return (EINVAL);
418ca26842eSHajimu UMEMOTO 
419ca26842eSHajimu UMEMOTO 	sa.sa_family = bdom;
420ca26842eSHajimu UMEMOTO 	error = copyout(&sa, osa, sizeof(sa.sa_family));
421ca26842eSHajimu UMEMOTO 	if (error)
422ca26842eSHajimu UMEMOTO 		return (error);
423ca26842eSHajimu UMEMOTO 
424ca26842eSHajimu UMEMOTO 	return (0);
425ca26842eSHajimu UMEMOTO }
426ca26842eSHajimu UMEMOTO 
4275a8a13e0SDavid Malone static int
42874f5d680SKonstantin Belousov linux_to_bsd_cmsg_type(int cmsg_type)
4295a8a13e0SDavid Malone {
43074f5d680SKonstantin Belousov 
43174f5d680SKonstantin Belousov 	switch (cmsg_type) {
43274f5d680SKonstantin Belousov 	case LINUX_SCM_RIGHTS:
43374f5d680SKonstantin Belousov 		return (SCM_RIGHTS);
434605da56bSAndriy Gapon 	case LINUX_SCM_CREDENTIALS:
435605da56bSAndriy Gapon 		return (SCM_CREDS);
43674f5d680SKonstantin Belousov 	}
43774f5d680SKonstantin Belousov 	return (-1);
43874f5d680SKonstantin Belousov }
43974f5d680SKonstantin Belousov 
44074f5d680SKonstantin Belousov static int
44174f5d680SKonstantin Belousov bsd_to_linux_cmsg_type(int cmsg_type)
44274f5d680SKonstantin Belousov {
44374f5d680SKonstantin Belousov 
44474f5d680SKonstantin Belousov 	switch (cmsg_type) {
44574f5d680SKonstantin Belousov 	case SCM_RIGHTS:
44674f5d680SKonstantin Belousov 		return (LINUX_SCM_RIGHTS);
447605da56bSAndriy Gapon 	case SCM_CREDS:
448605da56bSAndriy Gapon 		return (LINUX_SCM_CREDENTIALS);
44974f5d680SKonstantin Belousov 	}
45074f5d680SKonstantin Belousov 	return (-1);
45174f5d680SKonstantin Belousov }
45274f5d680SKonstantin Belousov 
45374f5d680SKonstantin Belousov static int
45474f5d680SKonstantin Belousov linux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
45574f5d680SKonstantin Belousov {
45674f5d680SKonstantin Belousov 	if (lhdr->msg_controllen > INT_MAX)
45774f5d680SKonstantin Belousov 		return (ENOBUFS);
45874f5d680SKonstantin Belousov 
45974f5d680SKonstantin Belousov 	bhdr->msg_name		= PTRIN(lhdr->msg_name);
46074f5d680SKonstantin Belousov 	bhdr->msg_namelen	= lhdr->msg_namelen;
46174f5d680SKonstantin Belousov 	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
46274f5d680SKonstantin Belousov 	bhdr->msg_iovlen	= lhdr->msg_iovlen;
46374f5d680SKonstantin Belousov 	bhdr->msg_control	= PTRIN(lhdr->msg_control);
464605da56bSAndriy Gapon 
465605da56bSAndriy Gapon 	/*
466605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
467605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
468605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
469605da56bSAndriy Gapon 	 *
470605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
471605da56bSAndriy Gapon 	 * control messages.
472605da56bSAndriy Gapon 	 */
473605da56bSAndriy Gapon 
47474f5d680SKonstantin Belousov 	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
47574f5d680SKonstantin Belousov 	return (0);
47674f5d680SKonstantin Belousov }
47774f5d680SKonstantin Belousov 
47874f5d680SKonstantin Belousov static int
47974f5d680SKonstantin Belousov bsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
48074f5d680SKonstantin Belousov {
48174f5d680SKonstantin Belousov 	lhdr->msg_name		= PTROUT(bhdr->msg_name);
48274f5d680SKonstantin Belousov 	lhdr->msg_namelen	= bhdr->msg_namelen;
48374f5d680SKonstantin Belousov 	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
48474f5d680SKonstantin Belousov 	lhdr->msg_iovlen	= bhdr->msg_iovlen;
48574f5d680SKonstantin Belousov 	lhdr->msg_control	= PTROUT(bhdr->msg_control);
486605da56bSAndriy Gapon 
487605da56bSAndriy Gapon 	/*
488605da56bSAndriy Gapon 	 * msg_controllen is skipped since BSD and LINUX control messages
489605da56bSAndriy Gapon 	 * are potentially different sizes (e.g. the cred structure used
490605da56bSAndriy Gapon 	 * by SCM_CREDS is different between the two operating system).
491605da56bSAndriy Gapon 	 *
492605da56bSAndriy Gapon 	 * The caller can set it (if necessary) after converting all the
493605da56bSAndriy Gapon 	 * control messages.
494605da56bSAndriy Gapon 	 */
495605da56bSAndriy Gapon 
49674f5d680SKonstantin Belousov 	/* msg_flags skipped */
49774f5d680SKonstantin Belousov 	return (0);
49874f5d680SKonstantin Belousov }
49974f5d680SKonstantin Belousov 
50074f5d680SKonstantin Belousov static int
50138a18e97SDmitry Chagin linux_set_socket_flags(struct thread *td, int s, int flags)
50238a18e97SDmitry Chagin {
50338a18e97SDmitry Chagin 	int error;
50438a18e97SDmitry Chagin 
50538a18e97SDmitry Chagin 	if (flags & LINUX_SOCK_NONBLOCK) {
50638a18e97SDmitry Chagin 		error = kern_fcntl(td, s, F_SETFL, O_NONBLOCK);
50738a18e97SDmitry Chagin 		if (error)
50838a18e97SDmitry Chagin 			return (error);
50938a18e97SDmitry Chagin 	}
51038a18e97SDmitry Chagin 	if (flags & LINUX_SOCK_CLOEXEC) {
51138a18e97SDmitry Chagin 		error = kern_fcntl(td, s, F_SETFD, FD_CLOEXEC);
51238a18e97SDmitry Chagin 		if (error)
51338a18e97SDmitry Chagin 			return (error);
51438a18e97SDmitry Chagin 	}
51538a18e97SDmitry Chagin 	return (0);
51638a18e97SDmitry Chagin }
51738a18e97SDmitry Chagin 
51838a18e97SDmitry Chagin static int
51974f5d680SKonstantin Belousov linux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
52074f5d680SKonstantin Belousov     struct mbuf *control, enum uio_seg segflg)
52174f5d680SKonstantin Belousov {
5225a8a13e0SDavid Malone 	struct sockaddr *to;
5235a8a13e0SDavid Malone 	int error;
5245a8a13e0SDavid Malone 
5255a8a13e0SDavid Malone 	if (mp->msg_name != NULL) {
5265a8a13e0SDavid Malone 		error = linux_getsockaddr(&to, mp->msg_name, mp->msg_namelen);
5275a8a13e0SDavid Malone 		if (error)
5285a8a13e0SDavid Malone 			return (error);
5295a8a13e0SDavid Malone 		mp->msg_name = to;
5305a8a13e0SDavid Malone 	} else
5315a8a13e0SDavid Malone 		to = NULL;
5325a8a13e0SDavid Malone 
533a6886ef1SMaxim Sobolev 	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
534a6886ef1SMaxim Sobolev 	    segflg);
5355a8a13e0SDavid Malone 
5365a8a13e0SDavid Malone 	if (to)
5371ede983cSDag-Erling Smørgrav 		free(to, M_SONAME);
5385a8a13e0SDavid Malone 	return (error);
5395a8a13e0SDavid Malone }
5405a8a13e0SDavid Malone 
5413f3a4815SMarcel Moolenaar /* Return 0 if IP_HDRINCL is set for the given socket. */
542f2477ae1SMike Smith static int
543e140eb43SDavid Malone linux_check_hdrincl(struct thread *td, int s)
544f2477ae1SMike Smith {
5453db2a843SBruce Evans 	int error, optval, size_val;
546f2477ae1SMike Smith 
547e140eb43SDavid Malone 	size_val = sizeof(optval);
548e140eb43SDavid Malone 	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
549e140eb43SDavid Malone 	    &optval, UIO_SYSSPACE, &size_val);
550e140eb43SDavid Malone 	if (error)
5513f3a4815SMarcel Moolenaar 		return (error);
5523f3a4815SMarcel Moolenaar 
5533f3a4815SMarcel Moolenaar 	return (optval == 0);
554f2477ae1SMike Smith }
555f2477ae1SMike Smith 
5565a8a13e0SDavid Malone struct linux_sendto_args {
5575a8a13e0SDavid Malone 	int s;
5584af27623STim J. Robbins 	l_uintptr_t msg;
5595a8a13e0SDavid Malone 	int len;
5605a8a13e0SDavid Malone 	int flags;
5614af27623STim J. Robbins 	l_uintptr_t to;
5625a8a13e0SDavid Malone 	int tolen;
5635a8a13e0SDavid Malone };
5645a8a13e0SDavid Malone 
565f2477ae1SMike Smith /*
566f2477ae1SMike Smith  * Updated sendto() when IP_HDRINCL is set:
567f2477ae1SMike Smith  * tweak endian-dependent fields in the IP packet.
568f2477ae1SMike Smith  */
569f2477ae1SMike Smith static int
57038da2381SRobert Watson linux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
571f2477ae1SMike Smith {
572f2477ae1SMike Smith /*
573f2477ae1SMike Smith  * linux_ip_copysize defines how many bytes we should copy
574f2477ae1SMike Smith  * from the beginning of the IP packet before we customize it for BSD.
575a6886ef1SMaxim Sobolev  * It should include all the fields we modify (ip_len and ip_off).
576f2477ae1SMike Smith  */
577f2477ae1SMike Smith #define linux_ip_copysize	8
578f2477ae1SMike Smith 
579f2477ae1SMike Smith 	struct ip *packet;
5805a8a13e0SDavid Malone 	struct msghdr msg;
581a6886ef1SMaxim Sobolev 	struct iovec aiov[1];
582f2477ae1SMike Smith 	int error;
583f2477ae1SMike Smith 
584aa675b57SDavid Schultz 	/* Check that the packet isn't too big or too small. */
585aa675b57SDavid Schultz 	if (linux_args->len < linux_ip_copysize ||
586aa675b57SDavid Schultz 	    linux_args->len > IP_MAXPACKET)
5873f3a4815SMarcel Moolenaar 		return (EINVAL);
588f2477ae1SMike Smith 
589a6886ef1SMaxim Sobolev 	packet = (struct ip *)malloc(linux_args->len, M_TEMP, M_WAITOK);
590f2477ae1SMike Smith 
591a6886ef1SMaxim Sobolev 	/* Make kernel copy of the packet to be sent */
5924af27623STim J. Robbins 	if ((error = copyin(PTRIN(linux_args->msg), packet,
593a6886ef1SMaxim Sobolev 	    linux_args->len)))
594a6886ef1SMaxim Sobolev 		goto goout;
595f2477ae1SMike Smith 
596f2477ae1SMike Smith 	/* Convert fields from Linux to BSD raw IP socket format */
5975a8a13e0SDavid Malone 	packet->ip_len = linux_args->len;
598f2477ae1SMike Smith 	packet->ip_off = ntohs(packet->ip_off);
599f2477ae1SMike Smith 
600f2477ae1SMike Smith 	/* Prepare the msghdr and iovec structures describing the new packet */
6014af27623STim J. Robbins 	msg.msg_name = PTRIN(linux_args->to);
6025a8a13e0SDavid Malone 	msg.msg_namelen = linux_args->tolen;
6035a8a13e0SDavid Malone 	msg.msg_iov = aiov;
604a6886ef1SMaxim Sobolev 	msg.msg_iovlen = 1;
6055a8a13e0SDavid Malone 	msg.msg_control = NULL;
6065a8a13e0SDavid Malone 	msg.msg_flags = 0;
6075a8a13e0SDavid Malone 	aiov[0].iov_base = (char *)packet;
608a6886ef1SMaxim Sobolev 	aiov[0].iov_len = linux_args->len;
609a6886ef1SMaxim Sobolev 	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
61074f5d680SKonstantin Belousov 	    NULL, UIO_SYSSPACE);
611a6886ef1SMaxim Sobolev goout:
612a6886ef1SMaxim Sobolev 	free(packet, M_TEMP);
6135a8a13e0SDavid Malone 	return (error);
614f2477ae1SMike Smith }
615f2477ae1SMike Smith 
616c21dee17SSøren Schmidt struct linux_socket_args {
617c21dee17SSøren Schmidt 	int domain;
618c21dee17SSøren Schmidt 	int type;
619c21dee17SSøren Schmidt 	int protocol;
620c21dee17SSøren Schmidt };
621c21dee17SSøren Schmidt 
622c21dee17SSøren Schmidt static int
623b40ce416SJulian Elischer linux_socket(struct thread *td, struct linux_socket_args *args)
624c21dee17SSøren Schmidt {
625ef04503dSPeter Wemm 	struct socket_args /* {
626c21dee17SSøren Schmidt 		int domain;
627c21dee17SSøren Schmidt 		int type;
628c21dee17SSøren Schmidt 		int protocol;
629ef04503dSPeter Wemm 	} */ bsd_args;
6303933bde2SDmitry Chagin 	int retval_socket, socket_flags;
631c21dee17SSøren Schmidt 
632745aaef5SKonstantin Belousov 	bsd_args.protocol = args->protocol;
6333933bde2SDmitry Chagin 	socket_flags = args->type & ~LINUX_SOCK_TYPE_MASK;
6343933bde2SDmitry Chagin 	if (socket_flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
6353933bde2SDmitry Chagin 		return (EINVAL);
6363933bde2SDmitry Chagin 	bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
637eeb63e51SDmitry Chagin 	if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
638eeb63e51SDmitry Chagin 		return (EINVAL);
639745aaef5SKonstantin Belousov 	bsd_args.domain = linux_to_bsd_domain(args->domain);
640c21dee17SSøren Schmidt 	if (bsd_args.domain == -1)
641d9b063ccSDmitry Chagin 		return (EAFNOSUPPORT);
642f2477ae1SMike Smith 
6438451d0ddSKip Macy 	retval_socket = sys_socket(td, &bsd_args);
6446994ea54SDmitry Chagin 	if (retval_socket)
6456994ea54SDmitry Chagin 		return (retval_socket);
6466994ea54SDmitry Chagin 
64738a18e97SDmitry Chagin 	retval_socket = linux_set_socket_flags(td, td->td_retval[0],
64838a18e97SDmitry Chagin 	    socket_flags);
6493933bde2SDmitry Chagin 	if (retval_socket) {
6503933bde2SDmitry Chagin 		(void)kern_close(td, td->td_retval[0]);
6513933bde2SDmitry Chagin 		goto out;
6523933bde2SDmitry Chagin 	}
6533933bde2SDmitry Chagin 
654f2477ae1SMike Smith 	if (bsd_args.type == SOCK_RAW
655f2477ae1SMike Smith 	    && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0)
6566994ea54SDmitry Chagin 	    && bsd_args.domain == PF_INET) {
657f2477ae1SMike Smith 		/* It's a raw IP socket: set the IP_HDRINCL option. */
658e140eb43SDavid Malone 		int hdrincl;
659f2477ae1SMike Smith 
660e140eb43SDavid Malone 		hdrincl = 1;
661e140eb43SDavid Malone 		/* We ignore any error returned by kern_setsockopt() */
662e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
663e140eb43SDavid Malone 		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
664f2477ae1SMike Smith 	}
665ca26842eSHajimu UMEMOTO #ifdef INET6
666ca26842eSHajimu UMEMOTO 	/*
667d97bee3eSBjoern A. Zeeb 	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by default
668d97bee3eSBjoern A. Zeeb 	 * and some apps depend on this. So, set V6ONLY to 0 for Linux apps.
669d97bee3eSBjoern A. Zeeb 	 * For simplicity we do this unconditionally of the net.inet6.ip6.v6only
670d97bee3eSBjoern A. Zeeb 	 * sysctl value.
671ca26842eSHajimu UMEMOTO 	 */
672d97bee3eSBjoern A. Zeeb 	if (bsd_args.domain == PF_INET6) {
673e140eb43SDavid Malone 		int v6only;
674ca26842eSHajimu UMEMOTO 
675e140eb43SDavid Malone 		v6only = 0;
676ca26842eSHajimu UMEMOTO 		/* We ignore any error returned by setsockopt() */
677e140eb43SDavid Malone 		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
678e140eb43SDavid Malone 		    &v6only, UIO_SYSSPACE, sizeof(v6only));
679ca26842eSHajimu UMEMOTO 	}
680ca26842eSHajimu UMEMOTO #endif
6813f3a4815SMarcel Moolenaar 
6823933bde2SDmitry Chagin out:
6833f3a4815SMarcel Moolenaar 	return (retval_socket);
684c21dee17SSøren Schmidt }
685c21dee17SSøren Schmidt 
686c21dee17SSøren Schmidt struct linux_bind_args {
687c21dee17SSøren Schmidt 	int s;
6884af27623STim J. Robbins 	l_uintptr_t name;
689c21dee17SSøren Schmidt 	int namelen;
690c21dee17SSøren Schmidt };
691c21dee17SSøren Schmidt 
692c21dee17SSøren Schmidt static int
693b40ce416SJulian Elischer linux_bind(struct thread *td, struct linux_bind_args *args)
694c21dee17SSøren Schmidt {
695ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
696c21dee17SSøren Schmidt 	int error;
697c21dee17SSøren Schmidt 
698745aaef5SKonstantin Belousov 	error = linux_getsockaddr(&sa, PTRIN(args->name),
699745aaef5SKonstantin Belousov 	    args->namelen);
700ca26842eSHajimu UMEMOTO 	if (error)
701ca26842eSHajimu UMEMOTO 		return (error);
702ca26842eSHajimu UMEMOTO 
703745aaef5SKonstantin Belousov 	error = kern_bind(td, args->s, sa);
704b33887eaSJohn Baldwin 	free(sa, M_SONAME);
705745aaef5SKonstantin Belousov 	if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
706d4b7423fSAlexander Leidinger 	   	return (EINVAL);
707b33887eaSJohn Baldwin 	return (error);
708c21dee17SSøren Schmidt }
709c21dee17SSøren Schmidt 
71001e0ffbaSAlexander Leidinger struct linux_connect_args {
711c21dee17SSøren Schmidt 	int s;
7124af27623STim J. Robbins 	l_uintptr_t name;
713c21dee17SSøren Schmidt 	int namelen;
714c21dee17SSøren Schmidt };
715b40ce416SJulian Elischer int linux_connect(struct thread *, struct linux_connect_args *);
716c21dee17SSøren Schmidt 
717930a65feSAndrew Gallatin int
718b40ce416SJulian Elischer linux_connect(struct thread *td, struct linux_connect_args *args)
719c21dee17SSøren Schmidt {
7200bf301c0SJonathan Lemon 	struct socket *so;
721ca26842eSHajimu UMEMOTO 	struct sockaddr *sa;
72239c95b83SMatthew Dillon 	u_int fflag;
723c21dee17SSøren Schmidt 	int error;
724c21dee17SSøren Schmidt 
725745aaef5SKonstantin Belousov 	error = linux_getsockaddr(&sa, (struct osockaddr *)PTRIN(args->name),
726745aaef5SKonstantin Belousov 	    args->namelen);
727ca26842eSHajimu UMEMOTO 	if (error)
728ca26842eSHajimu UMEMOTO 		return (error);
729ca26842eSHajimu UMEMOTO 
730745aaef5SKonstantin Belousov 	error = kern_connect(td, args->s, sa);
731b33887eaSJohn Baldwin 	free(sa, M_SONAME);
7320bf301c0SJonathan Lemon 	if (error != EISCONN)
7330bf301c0SJonathan Lemon 		return (error);
7340bf301c0SJonathan Lemon 
735dad3b88aSMike Smith 	/*
736dad3b88aSMike Smith 	 * Linux doesn't return EISCONN the first time it occurs,
737dad3b88aSMike Smith 	 * when on a non-blocking socket. Instead it returns the
738dad3b88aSMike Smith 	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
739f7f45ac8SRobert Watson 	 *
740f7f45ac8SRobert Watson 	 * XXXRW: Instead of using fgetsock(), check that it is a
741f7f45ac8SRobert Watson 	 * socket and use the file descriptor reference instead of
742f7f45ac8SRobert Watson 	 * creating a new one.
743dad3b88aSMike Smith 	 */
744a9d2f8d8SRobert Watson 	error = fgetsock(td, args->s, CAP_CONNECT, &so, &fflag);
7454641373fSJohn Baldwin 	if (error == 0) {
7460bf301c0SJonathan Lemon 		error = EISCONN;
74739c95b83SMatthew Dillon 		if (fflag & FNONBLOCK) {
7484641373fSJohn Baldwin 			SOCK_LOCK(so);
7495002a60fSMarcel Moolenaar 			if (so->so_emuldata == 0)
7500bf301c0SJonathan Lemon 				error = so->so_error;
7510bf301c0SJonathan Lemon 			so->so_emuldata = (void *)1;
7524641373fSJohn Baldwin 			SOCK_UNLOCK(so);
753dad3b88aSMike Smith 		}
75439c95b83SMatthew Dillon 		fputsock(so);
7554641373fSJohn Baldwin 	}
7563f3a4815SMarcel Moolenaar 	return (error);
757c21dee17SSøren Schmidt }
758c21dee17SSøren Schmidt 
759c21dee17SSøren Schmidt struct linux_listen_args {
760c21dee17SSøren Schmidt 	int s;
761c21dee17SSøren Schmidt 	int backlog;
762c21dee17SSøren Schmidt };
763c21dee17SSøren Schmidt 
764c21dee17SSøren Schmidt static int
765b40ce416SJulian Elischer linux_listen(struct thread *td, struct linux_listen_args *args)
766c21dee17SSøren Schmidt {
767ef04503dSPeter Wemm 	struct listen_args /* {
768c21dee17SSøren Schmidt 		int s;
769c21dee17SSøren Schmidt 		int backlog;
770ef04503dSPeter Wemm 	} */ bsd_args;
771c21dee17SSøren Schmidt 
772745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
773745aaef5SKonstantin Belousov 	bsd_args.backlog = args->backlog;
7748451d0ddSKip Macy 	return (sys_listen(td, &bsd_args));
775c21dee17SSøren Schmidt }
776c21dee17SSøren Schmidt 
77701e0ffbaSAlexander Leidinger static int
778c8f37d61SDmitry Chagin linux_accept_common(struct thread *td, int s, l_uintptr_t addr,
779f83427b8SDmitry Chagin     l_uintptr_t namelen, int flags)
780c21dee17SSøren Schmidt {
781ef04503dSPeter Wemm 	struct accept_args /* {
782c21dee17SSøren Schmidt 		int	s;
7833db2a843SBruce Evans 		struct sockaddr * __restrict name;
7843db2a843SBruce Evans 		socklen_t * __restrict anamelen;
785ef04503dSPeter Wemm 	} */ bsd_args;
78693e694c9SDmitry Chagin 	int error;
78793e694c9SDmitry Chagin 
78893e694c9SDmitry Chagin 	if (flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
78993e694c9SDmitry Chagin 		return (EINVAL);
790c21dee17SSøren Schmidt 
791c8f37d61SDmitry Chagin 	bsd_args.s = s;
7923db2a843SBruce Evans 	/* XXX: */
793c8f37d61SDmitry Chagin 	bsd_args.name = (struct sockaddr * __restrict)PTRIN(addr);
794c8f37d61SDmitry Chagin 	bsd_args.anamelen = PTRIN(namelen);/* XXX */
7958451d0ddSKip Macy 	error = sys_accept(td, &bsd_args);
7965c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.name);
797d4b7423fSAlexander Leidinger 	if (error) {
798c8f37d61SDmitry Chagin 		if (error == EFAULT && namelen != sizeof(struct sockaddr_in))
799d4b7423fSAlexander Leidinger 			return (EINVAL);
800dba5ab66SMarcel Moolenaar 		return (error);
801d4b7423fSAlexander Leidinger 	}
802dba5ab66SMarcel Moolenaar 
803dba5ab66SMarcel Moolenaar 	/*
804dba5ab66SMarcel Moolenaar 	 * linux appears not to copy flags from the parent socket to the
80593e694c9SDmitry Chagin 	 * accepted one, so we must clear the flags in the new descriptor
80693e694c9SDmitry Chagin 	 * and apply the requested flags.
807dba5ab66SMarcel Moolenaar 	 */
80893e694c9SDmitry Chagin 	error = kern_fcntl(td, td->td_retval[0], F_SETFL, 0);
80993e694c9SDmitry Chagin 	if (error)
81093e694c9SDmitry Chagin 		goto out;
81193e694c9SDmitry Chagin 	error = linux_set_socket_flags(td, td->td_retval[0], flags);
81293e694c9SDmitry Chagin 	if (error)
81393e694c9SDmitry Chagin 		goto out;
81493e694c9SDmitry Chagin 	if (addr)
81593e694c9SDmitry Chagin 		error = linux_sa_put(PTRIN(addr));
81693e694c9SDmitry Chagin 
81793e694c9SDmitry Chagin out:
81893e694c9SDmitry Chagin 	if (error) {
81993e694c9SDmitry Chagin 		(void)kern_close(td, td->td_retval[0]);
82093e694c9SDmitry Chagin 		td->td_retval[0] = 0;
82193e694c9SDmitry Chagin 	}
82293e694c9SDmitry Chagin 	return (error);
823c21dee17SSøren Schmidt }
824c21dee17SSøren Schmidt 
825c8f37d61SDmitry Chagin struct linux_accept_args {
826c8f37d61SDmitry Chagin 	int s;
827c8f37d61SDmitry Chagin 	l_uintptr_t addr;
828c8f37d61SDmitry Chagin 	l_uintptr_t namelen;
829c8f37d61SDmitry Chagin };
830c8f37d61SDmitry Chagin 
831c8f37d61SDmitry Chagin static int
832c8f37d61SDmitry Chagin linux_accept(struct thread *td, struct linux_accept_args *args)
833c8f37d61SDmitry Chagin {
834c8f37d61SDmitry Chagin 
835c8f37d61SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
836f83427b8SDmitry Chagin 	    args->namelen, 0));
837c8f37d61SDmitry Chagin }
838c8f37d61SDmitry Chagin 
839f8cd0af2SDmitry Chagin struct linux_accept4_args {
840f8cd0af2SDmitry Chagin 	int s;
841f8cd0af2SDmitry Chagin 	l_uintptr_t addr;
842f8cd0af2SDmitry Chagin 	l_uintptr_t namelen;
843f8cd0af2SDmitry Chagin 	int flags;
844f8cd0af2SDmitry Chagin };
845f8cd0af2SDmitry Chagin 
846f8cd0af2SDmitry Chagin static int
847f8cd0af2SDmitry Chagin linux_accept4(struct thread *td, struct linux_accept4_args *args)
848f8cd0af2SDmitry Chagin {
849f8cd0af2SDmitry Chagin 
850f8cd0af2SDmitry Chagin 	return (linux_accept_common(td, args->s, args->addr,
851f8cd0af2SDmitry Chagin 	    args->namelen, args->flags));
852f8cd0af2SDmitry Chagin }
853f8cd0af2SDmitry Chagin 
85401e0ffbaSAlexander Leidinger struct linux_getsockname_args {
855c21dee17SSøren Schmidt 	int s;
8564af27623STim J. Robbins 	l_uintptr_t addr;
8574af27623STim J. Robbins 	l_uintptr_t namelen;
858c21dee17SSøren Schmidt };
859c21dee17SSøren Schmidt 
86001e0ffbaSAlexander Leidinger static int
861b40ce416SJulian Elischer linux_getsockname(struct thread *td, struct linux_getsockname_args *args)
862c21dee17SSøren Schmidt {
863ef04503dSPeter Wemm 	struct getsockname_args /* {
864c21dee17SSøren Schmidt 		int	fdes;
8653db2a843SBruce Evans 		struct sockaddr * __restrict asa;
8663db2a843SBruce Evans 		socklen_t * __restrict alen;
867ef04503dSPeter Wemm 	} */ bsd_args;
868c21dee17SSøren Schmidt 	int error;
869c21dee17SSøren Schmidt 
870745aaef5SKonstantin Belousov 	bsd_args.fdes = args->s;
8713db2a843SBruce Evans 	/* XXX: */
872745aaef5SKonstantin Belousov 	bsd_args.asa = (struct sockaddr * __restrict)PTRIN(args->addr);
873745aaef5SKonstantin Belousov 	bsd_args.alen = PTRIN(args->namelen);	/* XXX */
8748451d0ddSKip Macy 	error = sys_getsockname(td, &bsd_args);
8755c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
876ca26842eSHajimu UMEMOTO 	if (error)
877ca26842eSHajimu UMEMOTO 		return (error);
878745aaef5SKonstantin Belousov 	error = linux_sa_put(PTRIN(args->addr));
879ca26842eSHajimu UMEMOTO 	if (error)
880ca26842eSHajimu UMEMOTO 		return (error);
881ca26842eSHajimu UMEMOTO 	return (0);
882c21dee17SSøren Schmidt }
883c21dee17SSøren Schmidt 
88401e0ffbaSAlexander Leidinger struct linux_getpeername_args {
885c21dee17SSøren Schmidt 	int s;
8864af27623STim J. Robbins 	l_uintptr_t addr;
8874af27623STim J. Robbins 	l_uintptr_t namelen;
888c21dee17SSøren Schmidt };
889c21dee17SSøren Schmidt 
89001e0ffbaSAlexander Leidinger static int
891b40ce416SJulian Elischer linux_getpeername(struct thread *td, struct linux_getpeername_args *args)
892c21dee17SSøren Schmidt {
8935c8919adSAlexander Leidinger 	struct getpeername_args /* {
894c21dee17SSøren Schmidt 		int fdes;
895c21dee17SSøren Schmidt 		caddr_t asa;
896c21dee17SSøren Schmidt 		int *alen;
897ef04503dSPeter Wemm 	} */ bsd_args;
898c21dee17SSøren Schmidt 	int error;
899c21dee17SSøren Schmidt 
900745aaef5SKonstantin Belousov 	bsd_args.fdes = args->s;
901745aaef5SKonstantin Belousov 	bsd_args.asa = (struct sockaddr *)PTRIN(args->addr);
902745aaef5SKonstantin Belousov 	bsd_args.alen = (int *)PTRIN(args->namelen);
9038451d0ddSKip Macy 	error = sys_getpeername(td, &bsd_args);
9045c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
905ca26842eSHajimu UMEMOTO 	if (error)
906ca26842eSHajimu UMEMOTO 		return (error);
907745aaef5SKonstantin Belousov 	error = linux_sa_put(PTRIN(args->addr));
908ca26842eSHajimu UMEMOTO 	if (error)
909ca26842eSHajimu UMEMOTO 		return (error);
910ca26842eSHajimu UMEMOTO 	return (0);
911c21dee17SSøren Schmidt }
912c21dee17SSøren Schmidt 
91301e0ffbaSAlexander Leidinger struct linux_socketpair_args {
914c21dee17SSøren Schmidt 	int domain;
915c21dee17SSøren Schmidt 	int type;
916c21dee17SSøren Schmidt 	int protocol;
9174af27623STim J. Robbins 	l_uintptr_t rsv;
918c21dee17SSøren Schmidt };
919c21dee17SSøren Schmidt 
92001e0ffbaSAlexander Leidinger static int
921b40ce416SJulian Elischer linux_socketpair(struct thread *td, struct linux_socketpair_args *args)
922c21dee17SSøren Schmidt {
923ef04503dSPeter Wemm 	struct socketpair_args /* {
924c21dee17SSøren Schmidt 		int domain;
925c21dee17SSøren Schmidt 		int type;
926c21dee17SSøren Schmidt 		int protocol;
927c21dee17SSøren Schmidt 		int *rsv;
928ef04503dSPeter Wemm 	} */ bsd_args;
92939253cf9SDmitry Chagin 	int error, socket_flags;
93039253cf9SDmitry Chagin 	int sv[2];
931c21dee17SSøren Schmidt 
932745aaef5SKonstantin Belousov 	bsd_args.domain = linux_to_bsd_domain(args->domain);
9331a52a4abSDmitry Chagin 	if (bsd_args.domain != PF_LOCAL)
9341a52a4abSDmitry Chagin 		return (EAFNOSUPPORT);
9353f3a4815SMarcel Moolenaar 
93639253cf9SDmitry Chagin 	socket_flags = args->type & ~LINUX_SOCK_TYPE_MASK;
93739253cf9SDmitry Chagin 	if (socket_flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
93839253cf9SDmitry Chagin 		return (EINVAL);
93939253cf9SDmitry Chagin 	bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
94039253cf9SDmitry Chagin 	if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
94139253cf9SDmitry Chagin 		return (EINVAL);
94239253cf9SDmitry Chagin 
9431a52a4abSDmitry Chagin 	if (args->protocol != 0 && args->protocol != PF_UNIX)
9441a52a4abSDmitry Chagin 
9451a52a4abSDmitry Chagin 		/*
9461a52a4abSDmitry Chagin 		 * Use of PF_UNIX as protocol argument is not right,
9471a52a4abSDmitry Chagin 		 * but Linux does it.
9481a52a4abSDmitry Chagin 		 * Do not map PF_UNIX as its Linux value is identical
9491a52a4abSDmitry Chagin 		 * to FreeBSD one.
9501a52a4abSDmitry Chagin 		 */
9511a52a4abSDmitry Chagin 		return (EPROTONOSUPPORT);
95240092d93SDmitry Chagin 	else
9531a52a4abSDmitry Chagin 		bsd_args.protocol = 0;
954745aaef5SKonstantin Belousov 	bsd_args.rsv = (int *)PTRIN(args->rsv);
95539253cf9SDmitry Chagin 	error = kern_socketpair(td, bsd_args.domain, bsd_args.type,
95639253cf9SDmitry Chagin 	    bsd_args.protocol, sv);
95739253cf9SDmitry Chagin 	if (error)
95839253cf9SDmitry Chagin 		return (error);
95939253cf9SDmitry Chagin 	error = linux_set_socket_flags(td, sv[0], socket_flags);
96039253cf9SDmitry Chagin 	if (error)
96139253cf9SDmitry Chagin 		goto out;
96239253cf9SDmitry Chagin 	error = linux_set_socket_flags(td, sv[1], socket_flags);
96339253cf9SDmitry Chagin 	if (error)
96439253cf9SDmitry Chagin 		goto out;
96539253cf9SDmitry Chagin 
96639253cf9SDmitry Chagin 	error = copyout(sv, bsd_args.rsv, 2 * sizeof(int));
96739253cf9SDmitry Chagin 
96839253cf9SDmitry Chagin out:
96939253cf9SDmitry Chagin 	if (error) {
97039253cf9SDmitry Chagin 		(void)kern_close(td, sv[0]);
97139253cf9SDmitry Chagin 		(void)kern_close(td, sv[1]);
97239253cf9SDmitry Chagin 	}
97339253cf9SDmitry Chagin 	return (error);
974c21dee17SSøren Schmidt }
975c21dee17SSøren Schmidt 
97601e0ffbaSAlexander Leidinger struct linux_send_args {
977c21dee17SSøren Schmidt 	int s;
9784af27623STim J. Robbins 	l_uintptr_t msg;
979c21dee17SSøren Schmidt 	int len;
980c21dee17SSøren Schmidt 	int flags;
981c21dee17SSøren Schmidt };
982c21dee17SSøren Schmidt 
98301e0ffbaSAlexander Leidinger static int
984b40ce416SJulian Elischer linux_send(struct thread *td, struct linux_send_args *args)
985c21dee17SSøren Schmidt {
98687d72a8fSPoul-Henning Kamp 	struct sendto_args /* {
987c21dee17SSøren Schmidt 		int s;
988c21dee17SSøren Schmidt 		caddr_t buf;
989044af7c3SJonathan Mini 		int len;
990c21dee17SSøren Schmidt 		int flags;
99187d72a8fSPoul-Henning Kamp 		caddr_t to;
99287d72a8fSPoul-Henning Kamp 		int tolen;
993ef04503dSPeter Wemm 	} */ bsd_args;
994c21dee17SSøren Schmidt 
995745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
996745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
997745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
998745aaef5SKonstantin Belousov 	bsd_args.flags = args->flags;
99987d72a8fSPoul-Henning Kamp 	bsd_args.to = NULL;
100087d72a8fSPoul-Henning Kamp 	bsd_args.tolen = 0;
10018451d0ddSKip Macy 	return sys_sendto(td, &bsd_args);
1002c21dee17SSøren Schmidt }
1003c21dee17SSøren Schmidt 
100401e0ffbaSAlexander Leidinger struct linux_recv_args {
1005c21dee17SSøren Schmidt 	int s;
10064af27623STim J. Robbins 	l_uintptr_t msg;
1007c21dee17SSøren Schmidt 	int len;
1008c21dee17SSøren Schmidt 	int flags;
1009c21dee17SSøren Schmidt };
1010c21dee17SSøren Schmidt 
101101e0ffbaSAlexander Leidinger static int
1012b40ce416SJulian Elischer linux_recv(struct thread *td, struct linux_recv_args *args)
1013c21dee17SSøren Schmidt {
101487d72a8fSPoul-Henning Kamp 	struct recvfrom_args /* {
1015c21dee17SSøren Schmidt 		int s;
1016c21dee17SSøren Schmidt 		caddr_t buf;
1017c21dee17SSøren Schmidt 		int len;
1018c21dee17SSøren Schmidt 		int flags;
101987d72a8fSPoul-Henning Kamp 		struct sockaddr *from;
102087d72a8fSPoul-Henning Kamp 		socklen_t fromlenaddr;
1021ef04503dSPeter Wemm 	} */ bsd_args;
1022c21dee17SSøren Schmidt 
1023745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1024745aaef5SKonstantin Belousov 	bsd_args.buf = (caddr_t)PTRIN(args->msg);
1025745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
10263980a435SDmitry Chagin 	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
102787d72a8fSPoul-Henning Kamp 	bsd_args.from = NULL;
102887d72a8fSPoul-Henning Kamp 	bsd_args.fromlenaddr = 0;
10298451d0ddSKip Macy 	return (sys_recvfrom(td, &bsd_args));
1030c21dee17SSøren Schmidt }
1031c21dee17SSøren Schmidt 
1032c21dee17SSøren Schmidt static int
1033b40ce416SJulian Elischer linux_sendto(struct thread *td, struct linux_sendto_args *args)
1034c21dee17SSøren Schmidt {
10355a8a13e0SDavid Malone 	struct msghdr msg;
10365a8a13e0SDavid Malone 	struct iovec aiov;
10375a8a13e0SDavid Malone 	int error;
1038c21dee17SSøren Schmidt 
1039745aaef5SKonstantin Belousov 	if (linux_check_hdrincl(td, args->s) == 0)
1040f2477ae1SMike Smith 		/* IP_HDRINCL set, tweak the packet before sending */
1041745aaef5SKonstantin Belousov 		return (linux_sendto_hdrincl(td, args));
1042f2477ae1SMike Smith 
1043745aaef5SKonstantin Belousov 	msg.msg_name = PTRIN(args->to);
1044745aaef5SKonstantin Belousov 	msg.msg_namelen = args->tolen;
10455a8a13e0SDavid Malone 	msg.msg_iov = &aiov;
10465a8a13e0SDavid Malone 	msg.msg_iovlen = 1;
10475a8a13e0SDavid Malone 	msg.msg_control = NULL;
10485a8a13e0SDavid Malone 	msg.msg_flags = 0;
1049745aaef5SKonstantin Belousov 	aiov.iov_base = PTRIN(args->msg);
1050745aaef5SKonstantin Belousov 	aiov.iov_len = args->len;
105174f5d680SKonstantin Belousov 	error = linux_sendit(td, args->s, &msg, args->flags, NULL,
105274f5d680SKonstantin Belousov 	    UIO_USERSPACE);
10535a8a13e0SDavid Malone 	return (error);
1054c21dee17SSøren Schmidt }
1055c21dee17SSøren Schmidt 
105601e0ffbaSAlexander Leidinger struct linux_recvfrom_args {
1057c21dee17SSøren Schmidt 	int s;
10584af27623STim J. Robbins 	l_uintptr_t buf;
1059c21dee17SSøren Schmidt 	int len;
1060c21dee17SSøren Schmidt 	int flags;
10614af27623STim J. Robbins 	l_uintptr_t from;
10624af27623STim J. Robbins 	l_uintptr_t fromlen;
1063c21dee17SSøren Schmidt };
1064c21dee17SSøren Schmidt 
106501e0ffbaSAlexander Leidinger static int
1066b40ce416SJulian Elischer linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
1067c21dee17SSøren Schmidt {
1068ef04503dSPeter Wemm 	struct recvfrom_args /* {
1069c21dee17SSøren Schmidt 		int	s;
1070c21dee17SSøren Schmidt 		caddr_t	buf;
1071c21dee17SSøren Schmidt 		size_t	len;
1072c21dee17SSøren Schmidt 		int	flags;
10733db2a843SBruce Evans 		struct sockaddr * __restrict from;
10743db2a843SBruce Evans 		socklen_t * __restrict fromlenaddr;
1075ef04503dSPeter Wemm 	} */ bsd_args;
10765c8919adSAlexander Leidinger 	size_t len;
1077c21dee17SSøren Schmidt 	int error;
1078c21dee17SSøren Schmidt 
1079745aaef5SKonstantin Belousov 	if ((error = copyin(PTRIN(args->fromlen), &len, sizeof(size_t))))
10803f3a4815SMarcel Moolenaar 		return (error);
10813f3a4815SMarcel Moolenaar 
1082745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1083745aaef5SKonstantin Belousov 	bsd_args.buf = PTRIN(args->buf);
1084745aaef5SKonstantin Belousov 	bsd_args.len = args->len;
1085745aaef5SKonstantin Belousov 	bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
10863db2a843SBruce Evans 	/* XXX: */
1087745aaef5SKonstantin Belousov 	bsd_args.from = (struct sockaddr * __restrict)PTRIN(args->from);
1088745aaef5SKonstantin Belousov 	bsd_args.fromlenaddr = PTRIN(args->fromlen);/* XXX */
10895c8919adSAlexander Leidinger 
10905c8919adSAlexander Leidinger 	linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.from, len);
10918451d0ddSKip Macy 	error = sys_recvfrom(td, &bsd_args);
10925c8919adSAlexander Leidinger 	bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.from);
10935c8919adSAlexander Leidinger 
1094ca26842eSHajimu UMEMOTO 	if (error)
1095ca26842eSHajimu UMEMOTO 		return (error);
1096745aaef5SKonstantin Belousov 	if (args->from) {
10974af27623STim J. Robbins 		error = linux_sa_put((struct osockaddr *)
1098745aaef5SKonstantin Belousov 		    PTRIN(args->from));
1099ca26842eSHajimu UMEMOTO 		if (error)
1100ca26842eSHajimu UMEMOTO 			return (error);
1101ca26842eSHajimu UMEMOTO 	}
1102ca26842eSHajimu UMEMOTO 	return (0);
1103ca26842eSHajimu UMEMOTO }
1104ca26842eSHajimu UMEMOTO 
110501e0ffbaSAlexander Leidinger struct linux_sendmsg_args {
1106ca26842eSHajimu UMEMOTO 	int s;
11074af27623STim J. Robbins 	l_uintptr_t msg;
1108ca26842eSHajimu UMEMOTO 	int flags;
1109ca26842eSHajimu UMEMOTO };
1110ca26842eSHajimu UMEMOTO 
111101e0ffbaSAlexander Leidinger static int
1112ca26842eSHajimu UMEMOTO linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
1113ca26842eSHajimu UMEMOTO {
111474f5d680SKonstantin Belousov 	struct cmsghdr *cmsg;
1115605da56bSAndriy Gapon 	struct cmsgcred cmcred;
111674f5d680SKonstantin Belousov 	struct mbuf *control;
1117ca26842eSHajimu UMEMOTO 	struct msghdr msg;
111874f5d680SKonstantin Belousov 	struct l_cmsghdr linux_cmsg;
111974f5d680SKonstantin Belousov 	struct l_cmsghdr *ptr_cmsg;
112074f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
1121552afd9cSPoul-Henning Kamp 	struct iovec *iov;
112274f5d680SKonstantin Belousov 	socklen_t datalen;
1123605da56bSAndriy Gapon 	struct sockaddr *sa;
1124605da56bSAndriy Gapon 	sa_family_t sa_family;
112574f5d680SKonstantin Belousov 	void *data;
1126ca26842eSHajimu UMEMOTO 	int error;
1127ca26842eSHajimu UMEMOTO 
112874f5d680SKonstantin Belousov 	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
112974f5d680SKonstantin Belousov 	if (error)
113074f5d680SKonstantin Belousov 		return (error);
1131d72a6158SRobert Watson 
1132d72a6158SRobert Watson 	/*
1133d72a6158SRobert Watson 	 * Some Linux applications (ping) define a non-NULL control data
1134d72a6158SRobert Watson 	 * pointer, but a msg_controllen of 0, which is not allowed in the
1135d72a6158SRobert Watson 	 * FreeBSD system call interface.  NULL the msg_control pointer in
1136d72a6158SRobert Watson 	 * order to handle this case.  This should be checked, but allows the
1137d72a6158SRobert Watson 	 * Linux ping to work.
1138d72a6158SRobert Watson 	 */
1139605da56bSAndriy Gapon 	if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0)
1140605da56bSAndriy Gapon 		linux_msg.msg_control = PTROUT(NULL);
1141605da56bSAndriy Gapon 
1142605da56bSAndriy Gapon 	error = linux_to_bsd_msghdr(&msg, &linux_msg);
1143605da56bSAndriy Gapon 	if (error)
1144605da56bSAndriy Gapon 		return (error);
114574f5d680SKonstantin Belousov 
114674f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
114774f5d680SKonstantin Belousov 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
114874f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
114974f5d680SKonstantin Belousov #else
1150552afd9cSPoul-Henning Kamp 	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
115174f5d680SKonstantin Belousov #endif
1152552afd9cSPoul-Henning Kamp 	if (error)
1153552afd9cSPoul-Henning Kamp 		return (error);
115474f5d680SKonstantin Belousov 
1155605da56bSAndriy Gapon 	control = NULL;
1156605da56bSAndriy Gapon 	cmsg = NULL;
1157605da56bSAndriy Gapon 
1158605da56bSAndriy Gapon 	if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) {
1159605da56bSAndriy Gapon 		error = kern_getsockname(td, args->s, &sa, &datalen);
1160605da56bSAndriy Gapon 		if (error)
1161605da56bSAndriy Gapon 			goto bad;
1162605da56bSAndriy Gapon 		sa_family = sa->sa_family;
1163605da56bSAndriy Gapon 		free(sa, M_SONAME);
1164605da56bSAndriy Gapon 
116574f5d680SKonstantin Belousov 		error = ENOBUFS;
116674f5d680SKonstantin Belousov 		cmsg = malloc(CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
116774f5d680SKonstantin Belousov 		control = m_get(M_WAIT, MT_CONTROL);
116874f5d680SKonstantin Belousov 		if (control == NULL)
116974f5d680SKonstantin Belousov 			goto bad;
117074f5d680SKonstantin Belousov 
117174f5d680SKonstantin Belousov 		do {
117274f5d680SKonstantin Belousov 			error = copyin(ptr_cmsg, &linux_cmsg,
117374f5d680SKonstantin Belousov 			    sizeof(struct l_cmsghdr));
117474f5d680SKonstantin Belousov 			if (error)
117574f5d680SKonstantin Belousov 				goto bad;
117674f5d680SKonstantin Belousov 
117774f5d680SKonstantin Belousov 			error = EINVAL;
117874f5d680SKonstantin Belousov 			if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr))
117974f5d680SKonstantin Belousov 				goto bad;
118074f5d680SKonstantin Belousov 
118174f5d680SKonstantin Belousov 			/*
1182605da56bSAndriy Gapon 			 * Now we support only SCM_RIGHTS and SCM_CRED,
1183605da56bSAndriy Gapon 			 * so return EINVAL in any other cmsg_type
118474f5d680SKonstantin Belousov 			 */
1185605da56bSAndriy Gapon 			cmsg->cmsg_type =
1186605da56bSAndriy Gapon 			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
118774f5d680SKonstantin Belousov 			cmsg->cmsg_level =
118874f5d680SKonstantin Belousov 			    linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
1189605da56bSAndriy Gapon 			if (cmsg->cmsg_type == -1
1190605da56bSAndriy Gapon 			    || cmsg->cmsg_level != SOL_SOCKET)
1191605da56bSAndriy Gapon 				goto bad;
119274f5d680SKonstantin Belousov 
1193605da56bSAndriy Gapon 			/*
1194605da56bSAndriy Gapon 			 * Some applications (e.g. pulseaudio) attempt to
1195605da56bSAndriy Gapon 			 * send ancillary data even if the underlying protocol
1196605da56bSAndriy Gapon 			 * doesn't support it which is not allowed in the
1197605da56bSAndriy Gapon 			 * FreeBSD system call interface.
1198605da56bSAndriy Gapon 			 */
1199605da56bSAndriy Gapon 			if (sa_family != AF_UNIX)
1200605da56bSAndriy Gapon 				continue;
1201605da56bSAndriy Gapon 
120274f5d680SKonstantin Belousov 			data = LINUX_CMSG_DATA(ptr_cmsg);
1203605da56bSAndriy Gapon 			datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
1204605da56bSAndriy Gapon 
1205605da56bSAndriy Gapon 			switch (cmsg->cmsg_type)
1206605da56bSAndriy Gapon 			{
1207605da56bSAndriy Gapon 			case SCM_RIGHTS:
1208605da56bSAndriy Gapon 				break;
1209605da56bSAndriy Gapon 
1210605da56bSAndriy Gapon 			case SCM_CREDS:
1211605da56bSAndriy Gapon 				data = &cmcred;
1212605da56bSAndriy Gapon 				datalen = sizeof(cmcred);
1213605da56bSAndriy Gapon 
1214605da56bSAndriy Gapon 				/*
1215605da56bSAndriy Gapon 				 * The lower levels will fill in the structure
1216605da56bSAndriy Gapon 				 */
1217605da56bSAndriy Gapon 				bzero(data, datalen);
1218605da56bSAndriy Gapon 				break;
1219605da56bSAndriy Gapon 			}
1220605da56bSAndriy Gapon 
1221605da56bSAndriy Gapon 			cmsg->cmsg_len = CMSG_LEN(datalen);
122274f5d680SKonstantin Belousov 
122374f5d680SKonstantin Belousov 			error = ENOBUFS;
122474f5d680SKonstantin Belousov 			if (!m_append(control, CMSG_HDRSZ, (c_caddr_t) cmsg))
122574f5d680SKonstantin Belousov 				goto bad;
122674f5d680SKonstantin Belousov 			if (!m_append(control, datalen, (c_caddr_t) data))
122774f5d680SKonstantin Belousov 				goto bad;
1228605da56bSAndriy Gapon 		} while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg)));
1229605da56bSAndriy Gapon 
1230605da56bSAndriy Gapon 		if (m_length(control, NULL) == 0) {
1231605da56bSAndriy Gapon 			m_freem(control);
123274f5d680SKonstantin Belousov 			control = NULL;
1233605da56bSAndriy Gapon 		}
123474f5d680SKonstantin Belousov 	}
123574f5d680SKonstantin Belousov 
12365a8a13e0SDavid Malone 	msg.msg_iov = iov;
12375a8a13e0SDavid Malone 	msg.msg_flags = 0;
123874f5d680SKonstantin Belousov 	error = linux_sendit(td, args->s, &msg, args->flags, control,
123974f5d680SKonstantin Belousov 	    UIO_USERSPACE);
124074f5d680SKonstantin Belousov 
124174f5d680SKonstantin Belousov bad:
1242552afd9cSPoul-Henning Kamp 	free(iov, M_IOV);
124374f5d680SKonstantin Belousov 	if (cmsg)
124474f5d680SKonstantin Belousov 		free(cmsg, M_TEMP);
1245ca26842eSHajimu UMEMOTO 	return (error);
1246c21dee17SSøren Schmidt }
1247c21dee17SSøren Schmidt 
124801e0ffbaSAlexander Leidinger struct linux_recvmsg_args {
124940dbba57SAssar Westerlund 	int s;
12504af27623STim J. Robbins 	l_uintptr_t msg;
125140dbba57SAssar Westerlund 	int flags;
125240dbba57SAssar Westerlund };
125340dbba57SAssar Westerlund 
125401e0ffbaSAlexander Leidinger static int
1255b40ce416SJulian Elischer linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
125640dbba57SAssar Westerlund {
125774f5d680SKonstantin Belousov 	struct cmsghdr *cm;
1258605da56bSAndriy Gapon 	struct cmsgcred *cmcred;
1259ca26842eSHajimu UMEMOTO 	struct msghdr msg;
126074f5d680SKonstantin Belousov 	struct l_cmsghdr *linux_cmsg = NULL;
1261605da56bSAndriy Gapon 	struct l_ucred linux_ucred;
1262605da56bSAndriy Gapon 	socklen_t datalen, outlen;
126374f5d680SKonstantin Belousov 	struct l_msghdr linux_msg;
126474f5d680SKonstantin Belousov 	struct iovec *iov, *uiov;
126574f5d680SKonstantin Belousov 	struct mbuf *control = NULL;
126674f5d680SKonstantin Belousov 	struct mbuf **controlp;
126774f5d680SKonstantin Belousov 	caddr_t outbuf;
126874f5d680SKonstantin Belousov 	void *data;
12693a72bf04SDmitry Chagin 	int error, i, fd, fds, *fdp;
127040dbba57SAssar Westerlund 
127174f5d680SKonstantin Belousov 	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
1272ca26842eSHajimu UMEMOTO 	if (error)
1273ca26842eSHajimu UMEMOTO 		return (error);
1274ca26842eSHajimu UMEMOTO 
127574f5d680SKonstantin Belousov 	error = linux_to_bsd_msghdr(&msg, &linux_msg);
127674f5d680SKonstantin Belousov 	if (error)
127774f5d680SKonstantin Belousov 		return (error);
127874f5d680SKonstantin Belousov 
127974f5d680SKonstantin Belousov #ifdef COMPAT_LINUX32
128074f5d680SKonstantin Belousov 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
128174f5d680SKonstantin Belousov 	    &iov, EMSGSIZE);
128274f5d680SKonstantin Belousov #else
128374f5d680SKonstantin Belousov 	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
128474f5d680SKonstantin Belousov #endif
128574f5d680SKonstantin Belousov 	if (error)
128674f5d680SKonstantin Belousov 		return (error);
128774f5d680SKonstantin Belousov 
128874f5d680SKonstantin Belousov 	if (msg.msg_name) {
128974f5d680SKonstantin Belousov 		error = linux_to_bsd_sockaddr((struct sockaddr *)msg.msg_name,
129074f5d680SKonstantin Belousov 		    msg.msg_namelen);
129174f5d680SKonstantin Belousov 		if (error)
129274f5d680SKonstantin Belousov 			goto bad;
129384b11cd8SMitsuru IWASAKI 	}
129484b11cd8SMitsuru IWASAKI 
129574f5d680SKonstantin Belousov 	uiov = msg.msg_iov;
129674f5d680SKonstantin Belousov 	msg.msg_iov = iov;
129774f5d680SKonstantin Belousov 	controlp = (msg.msg_control != NULL) ? &control : NULL;
129874f5d680SKonstantin Belousov 	error = kern_recvit(td, args->s, &msg, UIO_USERSPACE, controlp);
129974f5d680SKonstantin Belousov 	msg.msg_iov = uiov;
1300ca26842eSHajimu UMEMOTO 	if (error)
130174f5d680SKonstantin Belousov 		goto bad;
130274f5d680SKonstantin Belousov 
130374f5d680SKonstantin Belousov 	error = bsd_to_linux_msghdr(&msg, &linux_msg);
130474f5d680SKonstantin Belousov 	if (error)
130574f5d680SKonstantin Belousov 		goto bad;
130674f5d680SKonstantin Belousov 
130774f5d680SKonstantin Belousov 	if (linux_msg.msg_name) {
130874f5d680SKonstantin Belousov 		error = bsd_to_linux_sockaddr((struct sockaddr *)
130974f5d680SKonstantin Belousov 		    PTRIN(linux_msg.msg_name));
131074f5d680SKonstantin Belousov 		if (error)
131174f5d680SKonstantin Belousov 			goto bad;
131274f5d680SKonstantin Belousov 	}
131374f5d680SKonstantin Belousov 	if (linux_msg.msg_name && linux_msg.msg_namelen > 2) {
131474f5d680SKonstantin Belousov 		error = linux_sa_put(PTRIN(linux_msg.msg_name));
131574f5d680SKonstantin Belousov 		if (error)
131674f5d680SKonstantin Belousov 			goto bad;
131774f5d680SKonstantin Belousov 	}
131874f5d680SKonstantin Belousov 
131974f5d680SKonstantin Belousov 	outbuf = PTRIN(linux_msg.msg_control);
132074f5d680SKonstantin Belousov 	outlen = 0;
1321605da56bSAndriy Gapon 
1322605da56bSAndriy Gapon 	if (control) {
1323605da56bSAndriy Gapon 		linux_cmsg = malloc(L_CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
1324605da56bSAndriy Gapon 
1325605da56bSAndriy Gapon 		msg.msg_control = mtod(control, struct cmsghdr *);
1326605da56bSAndriy Gapon 		msg.msg_controllen = control->m_len;
1327605da56bSAndriy Gapon 
1328605da56bSAndriy Gapon 		cm = CMSG_FIRSTHDR(&msg);
132974f5d680SKonstantin Belousov 
133074f5d680SKonstantin Belousov 		while (cm != NULL) {
1331605da56bSAndriy Gapon 			linux_cmsg->cmsg_type =
1332605da56bSAndriy Gapon 			    bsd_to_linux_cmsg_type(cm->cmsg_type);
1333605da56bSAndriy Gapon 			linux_cmsg->cmsg_level =
1334605da56bSAndriy Gapon 			    bsd_to_linux_sockopt_level(cm->cmsg_level);
1335605da56bSAndriy Gapon 			if (linux_cmsg->cmsg_type == -1
1336605da56bSAndriy Gapon 			    || cm->cmsg_level != SOL_SOCKET)
133774f5d680SKonstantin Belousov 			{
133874f5d680SKonstantin Belousov 				error = EINVAL;
133974f5d680SKonstantin Belousov 				goto bad;
134074f5d680SKonstantin Belousov 			}
1341605da56bSAndriy Gapon 
134274f5d680SKonstantin Belousov 			data = CMSG_DATA(cm);
134374f5d680SKonstantin Belousov 			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
134474f5d680SKonstantin Belousov 
1345605da56bSAndriy Gapon 			switch (cm->cmsg_type)
13463a72bf04SDmitry Chagin 			{
1347605da56bSAndriy Gapon 			case SCM_RIGHTS:
1348605da56bSAndriy Gapon 				if (args->flags & LINUX_MSG_CMSG_CLOEXEC) {
1349605da56bSAndriy Gapon 					fds = datalen / sizeof(int);
1350605da56bSAndriy Gapon 					fdp = data;
1351605da56bSAndriy Gapon 					for (i = 0; i < fds; i++) {
1352605da56bSAndriy Gapon 						fd = *fdp++;
1353605da56bSAndriy Gapon 						(void)kern_fcntl(td, fd,
1354605da56bSAndriy Gapon 						    F_SETFD, FD_CLOEXEC);
1355605da56bSAndriy Gapon 					}
1356605da56bSAndriy Gapon 				}
1357605da56bSAndriy Gapon 				break;
1358605da56bSAndriy Gapon 
1359605da56bSAndriy Gapon 			case SCM_CREDS:
1360605da56bSAndriy Gapon 				/*
1361605da56bSAndriy Gapon 				 * Currently LOCAL_CREDS is never in
1362605da56bSAndriy Gapon 				 * effect for Linux so no need to worry
1363605da56bSAndriy Gapon 				 * about sockcred
1364605da56bSAndriy Gapon 				 */
1365605da56bSAndriy Gapon 				if (datalen != sizeof (*cmcred)) {
1366605da56bSAndriy Gapon 					error = EMSGSIZE;
1367605da56bSAndriy Gapon 					goto bad;
1368605da56bSAndriy Gapon 				}
1369605da56bSAndriy Gapon 				cmcred = (struct cmsgcred *)data;
1370605da56bSAndriy Gapon 				bzero(&linux_ucred, sizeof(linux_ucred));
1371605da56bSAndriy Gapon 				linux_ucred.pid = cmcred->cmcred_pid;
1372605da56bSAndriy Gapon 				linux_ucred.uid = cmcred->cmcred_uid;
1373605da56bSAndriy Gapon 				linux_ucred.gid = cmcred->cmcred_gid;
1374605da56bSAndriy Gapon 				data = &linux_ucred;
1375605da56bSAndriy Gapon 				datalen = sizeof(linux_ucred);
1376605da56bSAndriy Gapon 				break;
1377605da56bSAndriy Gapon 			}
1378605da56bSAndriy Gapon 
137974f5d680SKonstantin Belousov 			if (outlen + LINUX_CMSG_LEN(datalen) >
138074f5d680SKonstantin Belousov 			    linux_msg.msg_controllen) {
138174f5d680SKonstantin Belousov 				if (outlen == 0) {
138274f5d680SKonstantin Belousov 					error = EMSGSIZE;
138374f5d680SKonstantin Belousov 					goto bad;
138474f5d680SKonstantin Belousov 				} else {
13853a72bf04SDmitry Chagin 					linux_msg.msg_flags |=
13863a72bf04SDmitry Chagin 					    LINUX_MSG_CTRUNC;
138774f5d680SKonstantin Belousov 					goto out;
138874f5d680SKonstantin Belousov 				}
138974f5d680SKonstantin Belousov 			}
139074f5d680SKonstantin Belousov 
139174f5d680SKonstantin Belousov 			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
139274f5d680SKonstantin Belousov 
139374f5d680SKonstantin Belousov 			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
139474f5d680SKonstantin Belousov 			if (error)
139574f5d680SKonstantin Belousov 				goto bad;
139674f5d680SKonstantin Belousov 			outbuf += L_CMSG_HDRSZ;
139774f5d680SKonstantin Belousov 
139874f5d680SKonstantin Belousov 			error = copyout(data, outbuf, datalen);
139974f5d680SKonstantin Belousov 			if (error)
140074f5d680SKonstantin Belousov 				goto bad;
140174f5d680SKonstantin Belousov 
140274f5d680SKonstantin Belousov 			outbuf += LINUX_CMSG_ALIGN(datalen);
140374f5d680SKonstantin Belousov 			outlen += LINUX_CMSG_LEN(datalen);
140474f5d680SKonstantin Belousov 
1405605da56bSAndriy Gapon 			cm = CMSG_NXTHDR(&msg, cm);
140674f5d680SKonstantin Belousov 		}
140774f5d680SKonstantin Belousov 	}
140874f5d680SKonstantin Belousov 
140974f5d680SKonstantin Belousov out:
1410605da56bSAndriy Gapon 	linux_msg.msg_controllen = outlen;
141174f5d680SKonstantin Belousov 	error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg));
141274f5d680SKonstantin Belousov 
141374f5d680SKonstantin Belousov bad:
141474f5d680SKonstantin Belousov 	free(iov, M_IOV);
141574f5d680SKonstantin Belousov 	if (control != NULL)
141674f5d680SKonstantin Belousov 		m_freem(control);
141774f5d680SKonstantin Belousov 	if (linux_cmsg != NULL)
141874f5d680SKonstantin Belousov 		free(linux_cmsg, M_TEMP);
141974f5d680SKonstantin Belousov 
1420ca26842eSHajimu UMEMOTO 	return (error);
142140dbba57SAssar Westerlund }
142240dbba57SAssar Westerlund 
1423c21dee17SSøren Schmidt struct linux_shutdown_args {
1424c21dee17SSøren Schmidt 	int s;
1425c21dee17SSøren Schmidt 	int how;
1426c21dee17SSøren Schmidt };
1427c21dee17SSøren Schmidt 
1428c21dee17SSøren Schmidt static int
1429b40ce416SJulian Elischer linux_shutdown(struct thread *td, struct linux_shutdown_args *args)
1430c21dee17SSøren Schmidt {
1431ef04503dSPeter Wemm 	struct shutdown_args /* {
1432c21dee17SSøren Schmidt 		int s;
1433c21dee17SSøren Schmidt 		int how;
1434ef04503dSPeter Wemm 	} */ bsd_args;
1435c21dee17SSøren Schmidt 
1436745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1437745aaef5SKonstantin Belousov 	bsd_args.how = args->how;
14388451d0ddSKip Macy 	return (sys_shutdown(td, &bsd_args));
1439c21dee17SSøren Schmidt }
1440c21dee17SSøren Schmidt 
1441c21dee17SSøren Schmidt struct linux_setsockopt_args {
1442c21dee17SSøren Schmidt 	int s;
1443c21dee17SSøren Schmidt 	int level;
1444c21dee17SSøren Schmidt 	int optname;
14454af27623STim J. Robbins 	l_uintptr_t optval;
1446c21dee17SSøren Schmidt 	int optlen;
1447c21dee17SSøren Schmidt };
1448c21dee17SSøren Schmidt 
1449c21dee17SSøren Schmidt static int
1450b40ce416SJulian Elischer linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
1451c21dee17SSøren Schmidt {
1452ef04503dSPeter Wemm 	struct setsockopt_args /* {
1453c21dee17SSøren Schmidt 		int s;
1454c21dee17SSøren Schmidt 		int level;
1455c21dee17SSøren Schmidt 		int name;
1456c21dee17SSøren Schmidt 		caddr_t val;
1457c21dee17SSøren Schmidt 		int valsize;
1458ef04503dSPeter Wemm 	} */ bsd_args;
145903cc95d2SDmitry Chagin 	l_timeval linux_tv;
146003cc95d2SDmitry Chagin 	struct timeval tv;
1461c21dee17SSøren Schmidt 	int error, name;
1462c21dee17SSøren Schmidt 
1463745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1464745aaef5SKonstantin Belousov 	bsd_args.level = linux_to_bsd_sockopt_level(args->level);
1465c21dee17SSøren Schmidt 	switch (bsd_args.level) {
1466c21dee17SSøren Schmidt 	case SOL_SOCKET:
1467745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
146803cc95d2SDmitry Chagin 		switch (name) {
146903cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
147003cc95d2SDmitry Chagin 			/* FALLTHROUGH */
147103cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
147203cc95d2SDmitry Chagin 			error = copyin(PTRIN(args->optval), &linux_tv,
147303cc95d2SDmitry Chagin 			    sizeof(linux_tv));
147403cc95d2SDmitry Chagin 			if (error)
147503cc95d2SDmitry Chagin 				return (error);
147603cc95d2SDmitry Chagin 			tv.tv_sec = linux_tv.tv_sec;
147703cc95d2SDmitry Chagin 			tv.tv_usec = linux_tv.tv_usec;
147803cc95d2SDmitry Chagin 			return (kern_setsockopt(td, args->s, bsd_args.level,
147903cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, sizeof(tv)));
148003cc95d2SDmitry Chagin 			/* NOTREACHED */
148103cc95d2SDmitry Chagin 			break;
148203cc95d2SDmitry Chagin 		default:
148303cc95d2SDmitry Chagin 			break;
148403cc95d2SDmitry Chagin 		}
1485c21dee17SSøren Schmidt 		break;
1486c21dee17SSøren Schmidt 	case IPPROTO_IP:
1487745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1488c21dee17SSøren Schmidt 		break;
1489dad3b88aSMike Smith 	case IPPROTO_TCP:
1490dad3b88aSMike Smith 		/* Linux TCP option values match BSD's */
1491745aaef5SKonstantin Belousov 		name = args->optname;
1492dad3b88aSMike Smith 		break;
1493c21dee17SSøren Schmidt 	default:
14943f3a4815SMarcel Moolenaar 		name = -1;
14953f3a4815SMarcel Moolenaar 		break;
1496c21dee17SSøren Schmidt 	}
1497c21dee17SSøren Schmidt 	if (name == -1)
1498d4b7423fSAlexander Leidinger 		return (ENOPROTOOPT);
14993f3a4815SMarcel Moolenaar 
1500c21dee17SSøren Schmidt 	bsd_args.name = name;
1501745aaef5SKonstantin Belousov 	bsd_args.val = PTRIN(args->optval);
1502745aaef5SKonstantin Belousov 	bsd_args.valsize = args->optlen;
15035c8919adSAlexander Leidinger 
15045c8919adSAlexander Leidinger 	if (name == IPV6_NEXTHOP) {
15055c8919adSAlexander Leidinger 		linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.val,
15065c8919adSAlexander Leidinger 			bsd_args.valsize);
15078451d0ddSKip Macy 		error = sys_setsockopt(td, &bsd_args);
15085c8919adSAlexander Leidinger 		bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
15095c8919adSAlexander Leidinger 	} else
15108451d0ddSKip Macy 		error = sys_setsockopt(td, &bsd_args);
15115c8919adSAlexander Leidinger 
15125c8919adSAlexander Leidinger 	return (error);
1513c21dee17SSøren Schmidt }
1514c21dee17SSøren Schmidt 
1515c21dee17SSøren Schmidt struct linux_getsockopt_args {
1516c21dee17SSøren Schmidt 	int s;
1517c21dee17SSøren Schmidt 	int level;
1518c21dee17SSøren Schmidt 	int optname;
15194af27623STim J. Robbins 	l_uintptr_t optval;
15204af27623STim J. Robbins 	l_uintptr_t optlen;
1521c21dee17SSøren Schmidt };
1522c21dee17SSøren Schmidt 
1523c21dee17SSøren Schmidt static int
1524b40ce416SJulian Elischer linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
1525c21dee17SSøren Schmidt {
1526ef04503dSPeter Wemm 	struct getsockopt_args /* {
1527c21dee17SSøren Schmidt 		int s;
1528c21dee17SSøren Schmidt 		int level;
1529c21dee17SSøren Schmidt 		int name;
1530c21dee17SSøren Schmidt 		caddr_t val;
1531c21dee17SSøren Schmidt 		int *avalsize;
1532ef04503dSPeter Wemm 	} */ bsd_args;
153303cc95d2SDmitry Chagin 	l_timeval linux_tv;
153403cc95d2SDmitry Chagin 	struct timeval tv;
1535d4dd69c4SDmitry Chagin 	socklen_t tv_len, xulen;
1536d4dd69c4SDmitry Chagin 	struct xucred xu;
1537d4dd69c4SDmitry Chagin 	struct l_ucred lxu;
1538c21dee17SSøren Schmidt 	int error, name;
1539c21dee17SSøren Schmidt 
1540745aaef5SKonstantin Belousov 	bsd_args.s = args->s;
1541745aaef5SKonstantin Belousov 	bsd_args.level = linux_to_bsd_sockopt_level(args->level);
1542c21dee17SSøren Schmidt 	switch (bsd_args.level) {
1543c21dee17SSøren Schmidt 	case SOL_SOCKET:
1544745aaef5SKonstantin Belousov 		name = linux_to_bsd_so_sockopt(args->optname);
154503cc95d2SDmitry Chagin 		switch (name) {
154603cc95d2SDmitry Chagin 		case SO_RCVTIMEO:
154703cc95d2SDmitry Chagin 			/* FALLTHROUGH */
154803cc95d2SDmitry Chagin 		case SO_SNDTIMEO:
154903cc95d2SDmitry Chagin 			tv_len = sizeof(tv);
155003cc95d2SDmitry Chagin 			error = kern_getsockopt(td, args->s, bsd_args.level,
155103cc95d2SDmitry Chagin 			    name, &tv, UIO_SYSSPACE, &tv_len);
155203cc95d2SDmitry Chagin 			if (error)
155303cc95d2SDmitry Chagin 				return (error);
155403cc95d2SDmitry Chagin 			linux_tv.tv_sec = tv.tv_sec;
155503cc95d2SDmitry Chagin 			linux_tv.tv_usec = tv.tv_usec;
155603cc95d2SDmitry Chagin 			return (copyout(&linux_tv, PTRIN(args->optval),
155703cc95d2SDmitry Chagin 			    sizeof(linux_tv)));
155803cc95d2SDmitry Chagin 			/* NOTREACHED */
155903cc95d2SDmitry Chagin 			break;
1560d4dd69c4SDmitry Chagin 		case LOCAL_PEERCRED:
1561d4dd69c4SDmitry Chagin 			if (args->optlen != sizeof(lxu))
1562d4dd69c4SDmitry Chagin 				return (EINVAL);
1563d4dd69c4SDmitry Chagin 			xulen = sizeof(xu);
1564d4dd69c4SDmitry Chagin 			error = kern_getsockopt(td, args->s, bsd_args.level,
1565d4dd69c4SDmitry Chagin 			    name, &xu, UIO_SYSSPACE, &xulen);
1566d4dd69c4SDmitry Chagin 			if (error)
1567d4dd69c4SDmitry Chagin 				return (error);
1568d4dd69c4SDmitry Chagin 			/*
1569d4dd69c4SDmitry Chagin 			 * XXX Use 0 for pid as the FreeBSD does not cache peer pid.
1570d4dd69c4SDmitry Chagin 			 */
1571d4dd69c4SDmitry Chagin 			lxu.pid = 0;
1572d4dd69c4SDmitry Chagin 			lxu.uid = xu.cr_uid;
1573d4dd69c4SDmitry Chagin 			lxu.gid = xu.cr_gid;
1574d4dd69c4SDmitry Chagin 			return (copyout(&lxu, PTRIN(args->optval), sizeof(lxu)));
1575d4dd69c4SDmitry Chagin 			/* NOTREACHED */
1576d4dd69c4SDmitry Chagin 			break;
157703cc95d2SDmitry Chagin 		default:
157803cc95d2SDmitry Chagin 			break;
157903cc95d2SDmitry Chagin 		}
1580c21dee17SSøren Schmidt 		break;
1581c21dee17SSøren Schmidt 	case IPPROTO_IP:
1582745aaef5SKonstantin Belousov 		name = linux_to_bsd_ip_sockopt(args->optname);
1583c21dee17SSøren Schmidt 		break;
1584dad3b88aSMike Smith 	case IPPROTO_TCP:
1585dad3b88aSMike Smith 		/* Linux TCP option values match BSD's */
1586745aaef5SKonstantin Belousov 		name = args->optname;
1587dad3b88aSMike Smith 		break;
1588c21dee17SSøren Schmidt 	default:
15893f3a4815SMarcel Moolenaar 		name = -1;
15903f3a4815SMarcel Moolenaar 		break;
1591c21dee17SSøren Schmidt 	}
1592c21dee17SSøren Schmidt 	if (name == -1)
15933f3a4815SMarcel Moolenaar 		return (EINVAL);
15943f3a4815SMarcel Moolenaar 
1595f2477ae1SMike Smith 	bsd_args.name = name;
1596745aaef5SKonstantin Belousov 	bsd_args.val = PTRIN(args->optval);
1597745aaef5SKonstantin Belousov 	bsd_args.avalsize = PTRIN(args->optlen);
15985c8919adSAlexander Leidinger 
15995c8919adSAlexander Leidinger 	if (name == IPV6_NEXTHOP) {
16008451d0ddSKip Macy 		error = sys_getsockopt(td, &bsd_args);
16015c8919adSAlexander Leidinger 		bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
16025c8919adSAlexander Leidinger 	} else
16038451d0ddSKip Macy 		error = sys_getsockopt(td, &bsd_args);
16045c8919adSAlexander Leidinger 
16055c8919adSAlexander Leidinger 	return (error);
1606c21dee17SSøren Schmidt }
1607c21dee17SSøren Schmidt 
1608ea7b81d2SDmitry Chagin /* Argument list sizes for linux_socketcall */
1609ea7b81d2SDmitry Chagin 
1610ea7b81d2SDmitry Chagin #define LINUX_AL(x) ((x) * sizeof(l_ulong))
1611ea7b81d2SDmitry Chagin 
1612ea7b81d2SDmitry Chagin static const unsigned char lxs_args[] = {
1613ea7b81d2SDmitry Chagin 	LINUX_AL(0) /* unused*/,	LINUX_AL(3) /* socket */,
1614ea7b81d2SDmitry Chagin 	LINUX_AL(3) /* bind */,		LINUX_AL(3) /* connect */,
1615ea7b81d2SDmitry Chagin 	LINUX_AL(2) /* listen */,	LINUX_AL(3) /* accept */,
1616ea7b81d2SDmitry Chagin 	LINUX_AL(3) /* getsockname */,	LINUX_AL(3) /* getpeername */,
1617ea7b81d2SDmitry Chagin 	LINUX_AL(4) /* socketpair */,	LINUX_AL(4) /* send */,
1618ea7b81d2SDmitry Chagin 	LINUX_AL(4) /* recv */,		LINUX_AL(6) /* sendto */,
1619ea7b81d2SDmitry Chagin 	LINUX_AL(6) /* recvfrom */,	LINUX_AL(2) /* shutdown */,
1620ea7b81d2SDmitry Chagin 	LINUX_AL(5) /* setsockopt */,	LINUX_AL(5) /* getsockopt */,
1621f8cd0af2SDmitry Chagin 	LINUX_AL(3) /* sendmsg */,	LINUX_AL(3) /* recvmsg */,
1622f8cd0af2SDmitry Chagin 	LINUX_AL(4) /* accept4 */
1623ea7b81d2SDmitry Chagin };
1624ea7b81d2SDmitry Chagin 
1625ea7b81d2SDmitry Chagin #define	LINUX_AL_SIZE	sizeof(lxs_args) / sizeof(lxs_args[0]) - 1
1626ea7b81d2SDmitry Chagin 
1627c21dee17SSøren Schmidt int
1628b40ce416SJulian Elischer linux_socketcall(struct thread *td, struct linux_socketcall_args *args)
1629c21dee17SSøren Schmidt {
1630ea7b81d2SDmitry Chagin 	l_ulong a[6];
1631ea7b81d2SDmitry Chagin 	void *arg;
1632ea7b81d2SDmitry Chagin 	int error;
16333f3a4815SMarcel Moolenaar 
1634ea7b81d2SDmitry Chagin 	if (args->what < LINUX_SOCKET || args->what > LINUX_AL_SIZE)
1635ea7b81d2SDmitry Chagin 		return (EINVAL);
1636ea7b81d2SDmitry Chagin 	error = copyin(PTRIN(args->args), a, lxs_args[args->what]);
1637ea7b81d2SDmitry Chagin 	if (error)
1638ea7b81d2SDmitry Chagin 		return (error);
1639ea7b81d2SDmitry Chagin 
1640ea7b81d2SDmitry Chagin 	arg = a;
1641c21dee17SSøren Schmidt 	switch (args->what) {
1642c21dee17SSøren Schmidt 	case LINUX_SOCKET:
1643b40ce416SJulian Elischer 		return (linux_socket(td, arg));
1644c21dee17SSøren Schmidt 	case LINUX_BIND:
1645b40ce416SJulian Elischer 		return (linux_bind(td, arg));
1646c21dee17SSøren Schmidt 	case LINUX_CONNECT:
1647b40ce416SJulian Elischer 		return (linux_connect(td, arg));
1648c21dee17SSøren Schmidt 	case LINUX_LISTEN:
1649b40ce416SJulian Elischer 		return (linux_listen(td, arg));
1650c21dee17SSøren Schmidt 	case LINUX_ACCEPT:
1651b40ce416SJulian Elischer 		return (linux_accept(td, arg));
1652c21dee17SSøren Schmidt 	case LINUX_GETSOCKNAME:
1653b40ce416SJulian Elischer 		return (linux_getsockname(td, arg));
1654c21dee17SSøren Schmidt 	case LINUX_GETPEERNAME:
1655b40ce416SJulian Elischer 		return (linux_getpeername(td, arg));
1656c21dee17SSøren Schmidt 	case LINUX_SOCKETPAIR:
1657b40ce416SJulian Elischer 		return (linux_socketpair(td, arg));
1658c21dee17SSøren Schmidt 	case LINUX_SEND:
1659b40ce416SJulian Elischer 		return (linux_send(td, arg));
1660c21dee17SSøren Schmidt 	case LINUX_RECV:
1661b40ce416SJulian Elischer 		return (linux_recv(td, arg));
1662c21dee17SSøren Schmidt 	case LINUX_SENDTO:
1663b40ce416SJulian Elischer 		return (linux_sendto(td, arg));
1664c21dee17SSøren Schmidt 	case LINUX_RECVFROM:
1665b40ce416SJulian Elischer 		return (linux_recvfrom(td, arg));
1666c21dee17SSøren Schmidt 	case LINUX_SHUTDOWN:
1667b40ce416SJulian Elischer 		return (linux_shutdown(td, arg));
1668c21dee17SSøren Schmidt 	case LINUX_SETSOCKOPT:
1669b40ce416SJulian Elischer 		return (linux_setsockopt(td, arg));
1670c21dee17SSøren Schmidt 	case LINUX_GETSOCKOPT:
1671b40ce416SJulian Elischer 		return (linux_getsockopt(td, arg));
1672e76bba09SSøren Schmidt 	case LINUX_SENDMSG:
1673ca26842eSHajimu UMEMOTO 		return (linux_sendmsg(td, arg));
1674e76bba09SSøren Schmidt 	case LINUX_RECVMSG:
1675b40ce416SJulian Elischer 		return (linux_recvmsg(td, arg));
1676f8cd0af2SDmitry Chagin 	case LINUX_ACCEPT4:
1677f8cd0af2SDmitry Chagin 		return (linux_accept4(td, arg));
1678c21dee17SSøren Schmidt 	}
16793f3a4815SMarcel Moolenaar 
16803f3a4815SMarcel Moolenaar 	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
16813f3a4815SMarcel Moolenaar 	return (ENOSYS);
1682c21dee17SSøren Schmidt }
1683