1f14ea10sos/*-
2624a2a7emaste * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
32a03579pfg *
4d61d88auqs * Copyright (c) 1995 S��ren Schmidt
5f14ea10sos * All rights reserved.
6f14ea10sos *
7f14ea10sos * Redistribution and use in source and binary forms, with or without
8f14ea10sos * modification, are permitted provided that the following conditions
9f14ea10sos * are met:
10f14ea10sos * 1. Redistributions of source code must retain the above copyright
11624a2a7emaste *    notice, this list of conditions and the following disclaimer.
12f14ea10sos * 2. Redistributions in binary form must reproduce the above copyright
13f14ea10sos *    notice, this list of conditions and the following disclaimer in the
14f14ea10sos *    documentation and/or other materials provided with the distribution.
15f14ea10sos *
16624a2a7emaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17624a2a7emaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18624a2a7emaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19624a2a7emaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20624a2a7emaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21624a2a7emaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22624a2a7emaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23624a2a7emaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24624a2a7emaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25624a2a7emaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26624a2a7emaste * SUCH DAMAGE.
27f14ea10sos */
28f14ea10sos
29f72cbcfobrien#include <sys/cdefs.h>
30f72cbcfobrien__FBSDID("$FreeBSD$");
31f72cbcfobrien
32825daf3bde/* XXX we use functions that might not exist. */
33106242fru#include "opt_compat.h"
349689f05ume#include "opt_inet6.h"
3501dd609eivind
36f14ea10sos#include <sys/param.h>
377bbd5d4msmith#include <sys/proc.h>
38f14ea10sos#include <sys/systm.h>
39825daf3bde#include <sys/sysproto.h>
4033fdc14rwatson#include <sys/capsicum.h>
41d3baeedmsmith#include <sys/fcntl.h>
420bdef26jlemon#include <sys/file.h>
43967382atrasz#include <sys/filedesc.h>
449468fdakan#include <sys/limits.h>
458816876jhb#include <sys/lock.h>
469689f05ume#include <sys/malloc.h>
478816876jhb#include <sys/mutex.h>
483e991a3dwmalone#include <sys/mbuf.h>
49f14ea10sos#include <sys/socket.h>
500bdef26jlemon#include <sys/socketvar.h>
519689f05ume#include <sys/syscallsubr.h>
52cd450d6bde#include <sys/uio.h>
5377a92d8emaste#include <sys/stat.h>
549689f05ume#include <sys/syslog.h>
558f81241kib#include <sys/un.h>
5677a92d8emaste#include <sys/unistd.h>
5777a92d8emaste
5877a92d8emaste#include <security/audit/audit.h>
59825daf3bde
60604d894bz#include <net/if.h>
612c1ec83glebius#include <net/vnet.h>
62f14ea10sos#include <netinet/in.h>
637bbd5d4msmith#include <netinet/in_systm.h>
647bbd5d4msmith#include <netinet/ip.h>
65af17a55jhb#include <netinet/tcp.h>
669689f05ume#ifdef INET6
679689f05ume#include <netinet/ip6.h>
689689f05ume#include <netinet6/ip6_var.h>
699689f05ume#endif
70f14ea10sos
7198e2482obrien#ifdef COMPAT_LINUX32
726d0528atjr#include <machine/../linux32/linux.h>
736d0528atjr#include <machine/../linux32/linux32_proto.h>
7498e2482obrien#else
7598e2482obrien#include <machine/../linux/linux.h>
7698e2482obrien#include <machine/../linux/linux_proto.h>
776d0528atjr#endif
7857102bcdchagin#include <compat/linux/linux_common.h>
79db8a000dchagin#include <compat/linux/linux_file.h>
8075529fftrasz#include <compat/linux/linux_mib.h>
81f587d3bassar#include <compat/linux/linux_socket.h>
82ea83597dchagin#include <compat/linux/linux_timer.h>
83219e295marcel#include <compat/linux/linux_util.h>
84f14ea10sos
85ea83597dchaginstatic int linux_sendmsg_common(struct thread *, l_int, struct l_msghdr *,
86ea83597dchagin					l_uint);
87ea83597dchaginstatic int linux_recvmsg_common(struct thread *, l_int, struct l_msghdr *,
88ea83597dchagin					l_uint, struct msghdr *);
89796d34ddchaginstatic int linux_set_socket_flags(int, int *);
909689f05ume
91f14ea10sosstatic int
92f14ea10soslinux_to_bsd_sockopt_level(int level)
93f14ea10sos{
94abf2c20marcel
951d5d0f3trasz	if (level == LINUX_SOL_SOCKET)
96abf2c20marcel		return (SOL_SOCKET);
971d5d0f3trasz	/* Remaining values are RFC-defined protocol numbers. */
98abf2c20marcel	return (level);
99f14ea10sos}
100f14ea10sos
101abf2c20marcelstatic int
102324480ciwasakibsd_to_linux_sockopt_level(int level)
103324480ciwasaki{
104324480ciwasaki
1051d5d0f3trasz	if (level == SOL_SOCKET)
106324480ciwasaki		return (LINUX_SOL_SOCKET);
107324480ciwasaki	return (level);
108324480ciwasaki}
109324480ciwasaki
110324480ciwasakistatic int
111abf2c20marcellinux_to_bsd_ip_sockopt(int opt)
112f14ea10sos{
113abf2c20marcel
114abf2c20marcel	switch (opt) {
115abf2c20marcel	case LINUX_IP_TOS:
116abf2c20marcel		return (IP_TOS);
117abf2c20marcel	case LINUX_IP_TTL:
118abf2c20marcel		return (IP_TTL);
119abf2c20marcel	case LINUX_IP_OPTIONS:
120abf2c20marcel		return (IP_OPTIONS);
121abf2c20marcel	case LINUX_IP_MULTICAST_IF:
122abf2c20marcel		return (IP_MULTICAST_IF);
123abf2c20marcel	case LINUX_IP_MULTICAST_TTL:
124abf2c20marcel		return (IP_MULTICAST_TTL);
125abf2c20marcel	case LINUX_IP_MULTICAST_LOOP:
126abf2c20marcel		return (IP_MULTICAST_LOOP);
127abf2c20marcel	case LINUX_IP_ADD_MEMBERSHIP:
128abf2c20marcel		return (IP_ADD_MEMBERSHIP);
129abf2c20marcel	case LINUX_IP_DROP_MEMBERSHIP:
130abf2c20marcel		return (IP_DROP_MEMBERSHIP);
131abf2c20marcel	case LINUX_IP_HDRINCL:
132abf2c20marcel		return (IP_HDRINCL);
133abf2c20marcel	}
134abf2c20marcel	return (-1);
135f14ea10sos}
136f14ea10sos
137f14ea10sosstatic int
13807110a8aelinux_to_bsd_ip6_sockopt(int opt)
13907110a8ae{
14007110a8ae
14107110a8ae	switch (opt) {
14207110a8ae	case LINUX_IPV6_NEXTHOP:
14307110a8ae		return (IPV6_NEXTHOP);
14407110a8ae	case LINUX_IPV6_UNICAST_HOPS:
14507110a8ae		return (IPV6_UNICAST_HOPS);
14607110a8ae	case LINUX_IPV6_MULTICAST_IF:
14707110a8ae		return (IPV6_MULTICAST_IF);
14807110a8ae	case LINUX_IPV6_MULTICAST_HOPS:
14907110a8ae		return (IPV6_MULTICAST_HOPS);
15007110a8ae	case LINUX_IPV6_MULTICAST_LOOP:
15107110a8ae		return (IPV6_MULTICAST_LOOP);
15207110a8ae	case LINUX_IPV6_ADD_MEMBERSHIP:
15307110a8ae		return (IPV6_JOIN_GROUP);
15407110a8ae	case LINUX_IPV6_DROP_MEMBERSHIP:
15507110a8ae		return (IPV6_LEAVE_GROUP);
15607110a8ae	case LINUX_IPV6_V6ONLY:
15707110a8ae		return (IPV6_V6ONLY);
15807110a8ae	case LINUX_IPV6_DONTFRAG:
15907110a8ae		return (IPV6_DONTFRAG);
16007110a8ae#if 0
16107110a8ae	case LINUX_IPV6_CHECKSUM:
16207110a8ae		return (IPV6_CHECKSUM);
16307110a8ae	case LINUX_IPV6_RECVPKTINFO:
16407110a8ae		return (IPV6_RECVPKTINFO);
16507110a8ae	case LINUX_IPV6_PKTINFO:
16607110a8ae		return (IPV6_PKTINFO);
16707110a8ae	case LINUX_IPV6_RECVHOPLIMIT:
16807110a8ae		return (IPV6_RECVHOPLIMIT);
16907110a8ae	case LINUX_IPV6_HOPLIMIT:
17007110a8ae		return (IPV6_HOPLIMIT);
17107110a8ae	case LINUX_IPV6_RECVHOPOPTS:
17207110a8ae		return (IPV6_RECVHOPOPTS);
17307110a8ae	case LINUX_IPV6_HOPOPTS:
17407110a8ae		return (IPV6_HOPOPTS);
17507110a8ae	case LINUX_IPV6_RTHDRDSTOPTS:
17607110a8ae		return (IPV6_RTHDRDSTOPTS);
17707110a8ae	case LINUX_IPV6_RECVRTHDR:
17807110a8ae		return (IPV6_RECVRTHDR);
17907110a8ae	case LINUX_IPV6_RTHDR:
18007110a8ae		return (IPV6_RTHDR);
18107110a8ae	case LINUX_IPV6_RECVDSTOPTS:
18207110a8ae		return (IPV6_RECVDSTOPTS);
18307110a8ae	case LINUX_IPV6_DSTOPTS:
18407110a8ae		return (IPV6_DSTOPTS);
18507110a8ae	case LINUX_IPV6_RECVPATHMTU:
18607110a8ae		return (IPV6_RECVPATHMTU);
18707110a8ae	case LINUX_IPV6_PATHMTU:
18807110a8ae		return (IPV6_PATHMTU);
18907110a8ae#endif
19007110a8ae	}
19107110a8ae	return (-1);
19207110a8ae}
19307110a8ae
19407110a8aestatic int
195f14ea10soslinux_to_bsd_so_sockopt(int opt)
196f14ea10sos{
197abf2c20marcel
198abf2c20marcel	switch (opt) {
199abf2c20marcel	case LINUX_SO_DEBUG:
200abf2c20marcel		return (SO_DEBUG);
201abf2c20marcel	case LINUX_SO_REUSEADDR:
202abf2c20marcel		return (SO_REUSEADDR);
203abf2c20marcel	case LINUX_SO_TYPE:
204abf2c20marcel		return (SO_TYPE);
205abf2c20marcel	case LINUX_SO_ERROR:
206abf2c20marcel		return (SO_ERROR);
207abf2c20marcel	case LINUX_SO_DONTROUTE:
208abf2c20marcel		return (SO_DONTROUTE);
209abf2c20marcel	case LINUX_SO_BROADCAST:
210abf2c20marcel		return (SO_BROADCAST);
211abf2c20marcel	case LINUX_SO_SNDBUF:
212d9496f6trasz	case LINUX_SO_SNDBUFFORCE:
213abf2c20marcel		return (SO_SNDBUF);
214abf2c20marcel	case LINUX_SO_RCVBUF:
215d9496f6trasz	case LINUX_SO_RCVBUFFORCE:
216abf2c20marcel		return (SO_RCVBUF);
217abf2c20marcel	case LINUX_SO_KEEPALIVE:
218abf2c20marcel		return (SO_KEEPALIVE);
219abf2c20marcel	case LINUX_SO_OOBINLINE:
220abf2c20marcel		return (SO_OOBINLINE);
221abf2c20marcel	case LINUX_SO_LINGER:
222abf2c20marcel		return (SO_LINGER);
223d9119d0trasz	case LINUX_SO_REUSEPORT:
224d9119d0trasz		return (SO_REUSEPORT_LB);
2258f81241kib	case LINUX_SO_PEERCRED:
2268f81241kib		return (LOCAL_PEERCRED);
2278f81241kib	case LINUX_SO_RCVLOWAT:
2288f81241kib		return (SO_RCVLOWAT);
2298f81241kib	case LINUX_SO_SNDLOWAT:
2308f81241kib		return (SO_SNDLOWAT);
2318f81241kib	case LINUX_SO_RCVTIMEO:
2328f81241kib		return (SO_RCVTIMEO);
2338f81241kib	case LINUX_SO_SNDTIMEO:
2348f81241kib		return (SO_SNDTIMEO);
2358f81241kib	case LINUX_SO_TIMESTAMP:
2368f81241kib		return (SO_TIMESTAMP);
2378f81241kib	case LINUX_SO_ACCEPTCONN:
2388f81241kib		return (SO_ACCEPTCONN);
2394ee3a6btrasz	case LINUX_SO_PROTOCOL:
2404ee3a6btrasz		return (SO_PROTOCOL);
241abf2c20marcel	}
242abf2c20marcel	return (-1);
243f14ea10sos}
244f14ea10sos
245f587d3bassarstatic int
246af17a55jhblinux_to_bsd_tcp_sockopt(int opt)
247af17a55jhb{
248af17a55jhb
249af17a55jhb	switch (opt) {
250af17a55jhb	case LINUX_TCP_NODELAY:
251af17a55jhb		return (TCP_NODELAY);
252af17a55jhb	case LINUX_TCP_MAXSEG:
253af17a55jhb		return (TCP_MAXSEG);
25476f0f76trasz	case LINUX_TCP_CORK:
25576f0f76trasz		return (TCP_NOPUSH);
256af17a55jhb	case LINUX_TCP_KEEPIDLE:
257af17a55jhb		return (TCP_KEEPIDLE);
258af17a55jhb	case LINUX_TCP_KEEPINTVL:
259af17a55jhb		return (TCP_KEEPINTVL);
260af17a55jhb	case LINUX_TCP_KEEPCNT:
261af17a55jhb		return (TCP_KEEPCNT);
262af17a55jhb	case LINUX_TCP_MD5SIG:
263af17a55jhb		return (TCP_MD5SIG);
264af17a55jhb	}
265af17a55jhb	return (-1);
266af17a55jhb}
267af17a55jhb
268af17a55jhbstatic int
269f587d3bassarlinux_to_bsd_msg_flags(int flags)
270f587d3bassar{
271f587d3bassar	int ret_flags = 0;
272f587d3bassar
273f587d3bassar	if (flags & LINUX_MSG_OOB)
274f587d3bassar		ret_flags |= MSG_OOB;
275f587d3bassar	if (flags & LINUX_MSG_PEEK)
276f587d3bassar		ret_flags |= MSG_PEEK;
277f587d3bassar	if (flags & LINUX_MSG_DONTROUTE)
278f587d3bassar		ret_flags |= MSG_DONTROUTE;
279f587d3bassar	if (flags & LINUX_MSG_CTRUNC)
280f587d3bassar		ret_flags |= MSG_CTRUNC;
281f587d3bassar	if (flags & LINUX_MSG_TRUNC)
282f587d3bassar		ret_flags |= MSG_TRUNC;
283f587d3bassar	if (flags & LINUX_MSG_DONTWAIT)
284f587d3bassar		ret_flags |= MSG_DONTWAIT;
285f587d3bassar	if (flags & LINUX_MSG_EOR)
286f587d3bassar		ret_flags |= MSG_EOR;
287f587d3bassar	if (flags & LINUX_MSG_WAITALL)
288f587d3bassar		ret_flags |= MSG_WAITALL;
289b795e24sobomax	if (flags & LINUX_MSG_NOSIGNAL)
290b795e24sobomax		ret_flags |= MSG_NOSIGNAL;
291f587d3bassar#if 0 /* not handled */
292f587d3bassar	if (flags & LINUX_MSG_PROXY)
293f587d3bassar		;
294f587d3bassar	if (flags & LINUX_MSG_FIN)
295f587d3bassar		;
296f587d3bassar	if (flags & LINUX_MSG_SYN)
297f587d3bassar		;
298f587d3bassar	if (flags & LINUX_MSG_CONFIRM)
299f587d3bassar		;
300f587d3bassar	if (flags & LINUX_MSG_RST)
301f587d3bassar		;
302f587d3bassar	if (flags & LINUX_MSG_ERRQUEUE)
303f587d3bassar		;
304f587d3bassar#endif
3055a426e1dchagin	return (ret_flags);
306f587d3bassar}
307f587d3bassar
3083e991a3dwmalonestatic int
3098ffb383kiblinux_to_bsd_cmsg_type(int cmsg_type)
3108ffb383kib{
3118ffb383kib
3128ffb383kib	switch (cmsg_type) {
3138ffb383kib	case LINUX_SCM_RIGHTS:
3148ffb383kib		return (SCM_RIGHTS);
315a923658avg	case LINUX_SCM_CREDENTIALS:
316a923658avg		return (SCM_CREDS);
3178ffb383kib	}
3188ffb383kib	return (-1);
3198ffb383kib}
3208ffb383kib
3218ffb383kibstatic int
3228ffb383kibbsd_to_linux_cmsg_type(int cmsg_type)
3238ffb383kib{
3248ffb383kib
3258ffb383kib	switch (cmsg_type) {
3268ffb383kib	case SCM_RIGHTS:
3278ffb383kib		return (LINUX_SCM_RIGHTS);
328a923658avg	case SCM_CREDS:
329a923658avg		return (LINUX_SCM_CREDENTIALS);
330a71dbd5dchagin	case SCM_TIMESTAMP:
331a71dbd5dchagin		return (LINUX_SCM_TIMESTAMP);
3328ffb383kib	}
3338ffb383kib	return (-1);
3348ffb383kib}
3358ffb383kib
3368ffb383kibstatic int
3378ffb383kiblinux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
3388ffb383kib{
3398ffb383kib	if (lhdr->msg_controllen > INT_MAX)
3408ffb383kib		return (ENOBUFS);
3418ffb383kib
3428ffb383kib	bhdr->msg_name		= PTRIN(lhdr->msg_name);
3438ffb383kib	bhdr->msg_namelen	= lhdr->msg_namelen;
3448ffb383kib	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
3458ffb383kib	bhdr->msg_iovlen	= lhdr->msg_iovlen;
3468ffb383kib	bhdr->msg_control	= PTRIN(lhdr->msg_control);
347a923658avg
348a923658avg	/*
349a923658avg	 * msg_controllen is skipped since BSD and LINUX control messages
350a923658avg	 * are potentially different sizes (e.g. the cred structure used
351a923658avg	 * by SCM_CREDS is different between the two operating system).
352a923658avg	 *
353a923658avg	 * The caller can set it (if necessary) after converting all the
354a923658avg	 * control messages.
355a923658avg	 */
356a923658avg
3578ffb383kib	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
3588ffb383kib	return (0);
3598ffb383kib}
3608ffb383kib
3618ffb383kibstatic int
3628ffb383kibbsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
3638ffb383kib{
3648ffb383kib	lhdr->msg_name		= PTROUT(bhdr->msg_name);
3658ffb383kib	lhdr->msg_namelen	= bhdr->msg_namelen;
3668ffb383kib	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
3678ffb383kib	lhdr->msg_iovlen	= bhdr->msg_iovlen;
3688ffb383kib	lhdr->msg_control	= PTROUT(bhdr->msg_control);
369a923658avg
370a923658avg	/*
371a923658avg	 * msg_controllen is skipped since BSD and LINUX control messages
372a923658avg	 * are potentially different sizes (e.g. the cred structure used
373a923658avg	 * by SCM_CREDS is different between the two operating system).
374a923658avg	 *
375a923658avg	 * The caller can set it (if necessary) after converting all the
376a923658avg	 * control messages.
377a923658avg	 */
378a923658avg
3798ffb383kib	/* msg_flags skipped */
3808ffb383kib	return (0);
3818ffb383kib}
3828ffb383kib
3838ffb383kibstatic int
384796d34ddchaginlinux_set_socket_flags(int lflags, int *flags)
385ab797d4dchagin{
386ab797d4dchagin
387796d34ddchagin	if (lflags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
388796d34ddchagin		return (EINVAL);
389796d34ddchagin	if (lflags & LINUX_SOCK_NONBLOCK)
390796d34ddchagin		*flags |= SOCK_NONBLOCK;
391796d34ddchagin	if (lflags & LINUX_SOCK_CLOEXEC)
392796d34ddchagin		*flags |= SOCK_CLOEXEC;
393ab797d4dchagin	return (0);
394ab797d4dchagin}
395ab797d4dchagin
396ab797d4dchaginstatic int
3977fbac81traszlinux_copyout_sockaddr(const struct sockaddr *sa, void *uaddr, size_t len)
3987fbac81trasz{
3997fbac81trasz	struct l_sockaddr *lsa;
4007fbac81trasz	int error;
4017fbac81trasz
4027fbac81trasz	error = bsd_to_linux_sockaddr(sa, &lsa, len);
4037fbac81trasz	if (error != 0)
4047fbac81trasz		return (error);
4057fbac81trasz
4067fbac81trasz	error = copyout(lsa, uaddr, len);
4077fbac81trasz	free(lsa, M_SONAME);
4087fbac81trasz
4097fbac81trasz	return (error);
4107fbac81trasz}
4117fbac81trasz
4127fbac81traszstatic int
41368d0bd2sobomaxlinux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
4148ffb383kib    struct mbuf *control, enum uio_seg segflg)
4153e991a3dwmalone{
4163e991a3dwmalone	struct sockaddr *to;
41757102bcdchagin	int error, len;
4183e991a3dwmalone
4193e991a3dwmalone	if (mp->msg_name != NULL) {
42057102bcdchagin		len = mp->msg_namelen;
42157102bcdchagin		error = linux_to_bsd_sockaddr(mp->msg_name, &to, &len);
422554adaedchagin		if (error != 0)
4233e991a3dwmalone			return (error);
4243e991a3dwmalone		mp->msg_name = to;
4253e991a3dwmalone	} else
4263e991a3dwmalone		to = NULL;
4273e991a3dwmalone
42868d0bd2sobomax	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
42968d0bd2sobomax	    segflg);
4303e991a3dwmalone
4313e991a3dwmalone	if (to)
43266f807edes		free(to, M_SONAME);
4333e991a3dwmalone	return (error);
4343e991a3dwmalone}
4353e991a3dwmalone
436abf2c20marcel/* Return 0 if IP_HDRINCL is set for the given socket. */
4377bbd5d4msmithstatic int
438c8c1b8fdwmalonelinux_check_hdrincl(struct thread *td, int s)
4397bbd5d4msmith{
440236e47cdchagin	int error, optval;
441236e47cdchagin	socklen_t size_val;
442abf2c20marcel
443c8c1b8fdwmalone	size_val = sizeof(optval);
444c8c1b8fdwmalone	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
445c8c1b8fdwmalone	    &optval, UIO_SYSSPACE, &size_val);
446554adaedchagin	if (error != 0)
447abf2c20marcel		return (error);
448abf2c20marcel
449abf2c20marcel	return (optval == 0);
4507bbd5d4msmith}
4517bbd5d4msmith
4527bbd5d4msmith/*
4537bbd5d4msmith * Updated sendto() when IP_HDRINCL is set:
4547bbd5d4msmith * tweak endian-dependent fields in the IP packet.
4557bbd5d4msmith */
4567bbd5d4msmithstatic int
457606ea36rwatsonlinux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
4587bbd5d4msmith{
4597bbd5d4msmith/*
4607bbd5d4msmith * linux_ip_copysize defines how many bytes we should copy
4617bbd5d4msmith * from the beginning of the IP packet before we customize it for BSD.
46268d0bd2sobomax * It should include all the fields we modify (ip_len and ip_off).
4637bbd5d4msmith */
4647bbd5d4msmith#define linux_ip_copysize	8
4657bbd5d4msmith
466abf2c20marcel	struct ip *packet;
4673e991a3dwmalone	struct msghdr msg;
46868d0bd2sobomax	struct iovec aiov[1];
469abf2c20marcel	int error;
470abf2c20marcel
471fbf7a9bdas	/* Check that the packet isn't too big or too small. */
472fbf7a9bdas	if (linux_args->len < linux_ip_copysize ||
473fbf7a9bdas	    linux_args->len > IP_MAXPACKET)
474abf2c20marcel		return (EINVAL);
475abf2c20marcel
476f5eca4cdchagin	packet = (struct ip *)malloc(linux_args->len, M_LINUX, M_WAITOK);
477abf2c20marcel
47868d0bd2sobomax	/* Make kernel copy of the packet to be sent */
4796d0528atjr	if ((error = copyin(PTRIN(linux_args->msg), packet,
48068d0bd2sobomax	    linux_args->len)))
48168d0bd2sobomax		goto goout;
482abf2c20marcel
483abf2c20marcel	/* Convert fields from Linux to BSD raw IP socket format */
4843e991a3dwmalone	packet->ip_len = linux_args->len;
485abf2c20marcel	packet->ip_off = ntohs(packet->ip_off);
486abf2c20marcel
487abf2c20marcel	/* Prepare the msghdr and iovec structures describing the new packet */
4886d0528atjr	msg.msg_name = PTRIN(linux_args->to);
4893e991a3dwmalone	msg.msg_namelen = linux_args->tolen;
4903e991a3dwmalone	msg.msg_iov = aiov;
49168d0bd2sobomax	msg.msg_iovlen = 1;
4923e991a3dwmalone	msg.msg_control = NULL;
4933e991a3dwmalone	msg.msg_flags = 0;
4943e991a3dwmalone	aiov[0].iov_base = (char *)packet;
49568d0bd2sobomax	aiov[0].iov_len = linux_args->len;
49668d0bd2sobomax	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
4978ffb383kib	    NULL, UIO_SYSSPACE);
49868d0bd2sobomaxgoout:
499f5eca4cdchagin	free(packet, M_LINUX);
5003e991a3dwmalone	return (error);
5017bbd5d4msmith}
5027bbd5d4msmith
503c0ca16ddchaginint
5045596676julianlinux_socket(struct thread *td, struct linux_socket_args *args)
505f14ea10sos{
506a546306trasz	int domain, retval_socket, type;
507abf2c20marcel
508a546306trasz	type = args->type & LINUX_SOCK_TYPE_MASK;
509a546306trasz	if (type < 0 || type > LINUX_SOCK_MAX)
510a0c026bdchagin		return (EINVAL);
511796d34ddchagin	retval_socket = linux_set_socket_flags(args->type & ~LINUX_SOCK_TYPE_MASK,
512a546306trasz		&type);
513796d34ddchagin	if (retval_socket != 0)
514796d34ddchagin		return (retval_socket);
515a546306trasz	domain = linux_to_bsd_domain(args->domain);
516a546306trasz	if (domain == -1)
5173ce5087dchagin		return (EAFNOSUPPORT);
518abf2c20marcel
519a546306trasz	retval_socket = kern_socket(td, domain, type, args->protocol);
520eae11e9dchagin	if (retval_socket)
521eae11e9dchagin		return (retval_socket);
522eae11e9dchagin
523a546306trasz	if (type == SOCK_RAW
524a546306trasz	    && (args->protocol == IPPROTO_RAW || args->protocol == 0)
525a546306trasz	    && domain == PF_INET) {
526abf2c20marcel		/* It's a raw IP socket: set the IP_HDRINCL option. */
527c8c1b8fdwmalone		int hdrincl;
528c8c1b8fdwmalone
529c8c1b8fdwmalone		hdrincl = 1;
530c8c1b8fdwmalone		/* We ignore any error returned by kern_setsockopt() */
531c8c1b8fdwmalone		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
532c8c1b8fdwmalone		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
533abf2c20marcel	}
5349689f05ume#ifdef INET6
5359689f05ume	/*
536b991a4abz	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by default
537b991a4abz	 * and some apps depend on this. So, set V6ONLY to 0 for Linux apps.
538b991a4abz	 * For simplicity we do this unconditionally of the net.inet6.ip6.v6only
539b991a4abz	 * sysctl value.
5409689f05ume	 */
541a546306trasz	if (domain == PF_INET6) {
542c8c1b8fdwmalone		int v6only;
543c8c1b8fdwmalone
544c8c1b8fdwmalone		v6only = 0;
5459689f05ume		/* We ignore any error returned by setsockopt() */
546c8c1b8fdwmalone		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
547c8c1b8fdwmalone		    &v6only, UIO_SYSSPACE, sizeof(v6only));
5489689f05ume	}
5499689f05ume#endif
5507bbd5d4msmith
551abf2c20marcel	return (retval_socket);
552f14ea10sos}
553f14ea10sos
554c0ca16ddchaginint
5555596676julianlinux_bind(struct thread *td, struct linux_bind_args *args)
556f14ea10sos{
5579689f05ume	struct sockaddr *sa;
558abf2c20marcel	int error;
559abf2c20marcel
56057102bcdchagin	error = linux_to_bsd_sockaddr(PTRIN(args->name), &sa,
56157102bcdchagin	    &args->namelen);
562554adaedchagin	if (error != 0)
5639689f05ume		return (error);
5649689f05ume
565b4ef709kib	error = kern_bindat(td, AT_FDCWD, args->s, sa);
566947b8c9jhb	free(sa, M_SONAME);
56757102bcdchagin
56857102bcdchagin	/* XXX */
569626be49kib	if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
5705c9ea56emaste		return (EINVAL);
571947b8c9jhb	return (error);
572f14ea10sos}
573f14ea10sos
5740ee48b4gallatinint
5755596676julianlinux_connect(struct thread *td, struct linux_connect_args *args)
576f14ea10sos{
5770bdef26jlemon	struct socket *so;
5789689f05ume	struct sockaddr *sa;
5797827ebfglebius	struct file *fp;
580271e616dillon	u_int fflag;
581abf2c20marcel	int error;
582abf2c20marcel
58357102bcdchagin	error = linux_to_bsd_sockaddr(PTRIN(args->name), &sa,
58457102bcdchagin	    &args->namelen);
585554adaedchagin	if (error != 0)
5869689f05ume		return (error);
5879689f05ume
588b4ef709kib	error = kern_connectat(td, AT_FDCWD, args->s, sa);
589947b8c9jhb	free(sa, M_SONAME);
5900bdef26jlemon	if (error != EISCONN)
5910bdef26jlemon		return (error);
592abf2c20marcel
5930bdef26jlemon	/*
5940bdef26jlemon	 * Linux doesn't return EISCONN the first time it occurs,
5950bdef26jlemon	 * when on a non-blocking socket. Instead it returns the
5960bdef26jlemon	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
5970bdef26jlemon	 */
598a0bd5d3mmacy	error = getsock_cap(td, args->s, &cap_connect_rights,
5997827ebfglebius	    &fp, &fflag, NULL);
6007827ebfglebius	if (error != 0)
6017827ebfglebius		return (error);
6027827ebfglebius
6037827ebfglebius	error = EISCONN;
6047827ebfglebius	so = fp->f_data;
6057827ebfglebius	if (fflag & FNONBLOCK) {
6067827ebfglebius		SOCK_LOCK(so);
6077827ebfglebius		if (so->so_emuldata == 0)
6087827ebfglebius			error = so->so_error;
6097827ebfglebius		so->so_emuldata = (void *)1;
6107827ebfglebius		SOCK_UNLOCK(so);
611d3baeedmsmith	}
6127827ebfglebius	fdrop(fp, td);
6137827ebfglebius
614abf2c20marcel	return (error);
615f14ea10sos}
616f14ea10sos
617c0ca16ddchaginint
6185596676julianlinux_listen(struct thread *td, struct linux_listen_args *args)
619f14ea10sos{
620abf2c20marcel
621a546306trasz	return (kern_listen(td, args->s, args->backlog));
622f14ea10sos}
623f14ea10sos
624326540dnetchildstatic int
6250cc88e7dchaginlinux_accept_common(struct thread *td, int s, l_uintptr_t addr,
6268e9d8c2dchagin    l_uintptr_t namelen, int flags)
627f14ea10sos{
62857102bcdchagin	struct sockaddr *sa;
629967382atrasz	struct file *fp, *fp1;
63057102bcdchagin	int bflags, len;
63157102bcdchagin	struct socket *so;
63251e9cd7dchagin	int error, error1;
63376d24c5dchagin
63457102bcdchagin	bflags = 0;
635967382atrasz	fp = NULL;
636967382atrasz	sa = NULL;
637967382atrasz
63857102bcdchagin	error = linux_set_socket_flags(flags, &bflags);
639796d34ddchagin	if (error != 0)
640796d34ddchagin		return (error);
64157102bcdchagin
64257102bcdchagin	if (PTRIN(addr) == NULL) {
64357102bcdchagin		len = 0;
64457102bcdchagin		error = kern_accept4(td, s, NULL, NULL, bflags, NULL);
64557102bcdchagin	} else {
64657102bcdchagin		error = copyin(PTRIN(namelen), &len, sizeof(len));
64757102bcdchagin		if (error != 0)
64857102bcdchagin			return (error);
64957102bcdchagin		if (len < 0)
65057102bcdchagin			return (EINVAL);
65157102bcdchagin		error = kern_accept4(td, s, &sa, &len, bflags, &fp);
65257102bcdchagin	}
65357102bcdchagin
654967382atrasz	/*
655967382atrasz	 * Translate errno values into ones used by Linux.
656967382atrasz	 */
657554adaedchagin	if (error != 0) {
65857102bcdchagin		/*
65957102bcdchagin		 * XXX. This is wrong, different sockaddr structures
66057102bcdchagin		 * have different sizes.
66157102bcdchagin		 */
662967382atrasz		switch (error) {
663967382atrasz		case EFAULT:
664967382atrasz			if (namelen != sizeof(struct sockaddr_in))
665967382atrasz				error = EINVAL;
666967382atrasz			break;
667967382atrasz		case EINVAL:
668967382atrasz			error1 = getsock_cap(td, s, &cap_accept_rights, &fp1, NULL, NULL);
66957102bcdchagin			if (error1 != 0) {
67057102bcdchagin				error = error1;
671967382atrasz				break;
67282003e4dchagin			}
673967382atrasz			so = fp1->f_data;
67457102bcdchagin			if (so->so_type == SOCK_DGRAM)
67557102bcdchagin				error = EOPNOTSUPP;
676967382atrasz			fdrop(fp1, td);
677967382atrasz			break;
67851e9cd7dchagin		}
679967382atrasz		return (error);
68057102bcdchagin	}
68157102bcdchagin
682967382atrasz	if (len != 0) {
6837fbac81trasz		error = linux_copyout_sockaddr(sa, PTRIN(addr), len);
68457102bcdchagin
685967382atrasz		/*
686967382atrasz		 * XXX: We should also copyout the len, shouldn't we?
687967382atrasz		 */
68857102bcdchagin
689967382atrasz		if (error != 0) {
690967382atrasz			fdclose(td, fp, td->td_retval[0]);
691967382atrasz			td->td_retval[0] = 0;
692967382atrasz		}
69376d24c5dchagin	}
694967382atrasz	if (fp != NULL)
695967382atrasz		fdrop(fp, td);
696967382atrasz	free(sa, M_SONAME);
69776d24c5dchagin	return (error);
698f14ea10sos}
699f14ea10sos
700c0ca16ddchaginint
7010cc88e7dchaginlinux_accept(struct thread *td, struct linux_accept_args *args)
7020cc88e7dchagin{
7030cc88e7dchagin
7040cc88e7dchagin	return (linux_accept_common(td, args->s, args->addr,
7058e9d8c2dchagin	    args->namelen, 0));
7060cc88e7dchagin}
7070cc88e7dchagin
708c0ca16ddchaginint
709bb8f1f3dchaginlinux_accept4(struct thread *td, struct linux_accept4_args *args)
710bb8f1f3dchagin{
711bb8f1f3dchagin
712bb8f1f3dchagin	return (linux_accept_common(td, args->s, args->addr,
713bb8f1f3dchagin	    args->namelen, args->flags));
714bb8f1f3dchagin}
715bb8f1f3dchagin
716c0ca16ddchaginint
7175596676julianlinux_getsockname(struct thread *td, struct linux_getsockname_args *args)
718f14ea10sos{
71957102bcdchagin	struct sockaddr *sa;
72057102bcdchagin	int len, error;
72157102bcdchagin
72257102bcdchagin	error = copyin(PTRIN(args->namelen), &len, sizeof(len));
72357102bcdchagin	if (error != 0)
72457102bcdchagin		return (error);
725abf2c20marcel
72657102bcdchagin	error = kern_getsockname(td, args->s, &sa, &len);
727554adaedchagin	if (error != 0)
7289689f05ume		return (error);
72957102bcdchagin
7307fbac81trasz	if (len != 0)
7317fbac81trasz		error = linux_copyout_sockaddr(sa, PTRIN(args->addr), len);
73257102bcdchagin
73357102bcdchagin	free(sa, M_SONAME);
73457102bcdchagin	if (error == 0)
73557102bcdchagin		error = copyout(&len, PTRIN(args->namelen), sizeof(len));
73657102bcdchagin	return (error);
737f14ea10sos}
738f14ea10sos
739c0ca16ddchaginint
7405596676julianlinux_getpeername(struct thread *td, struct linux_getpeername_args *args)
741f14ea10sos{
74257102bcdchagin	struct sockaddr *sa;
74357102bcdchagin	int len, error;
74457102bcdchagin
74557102bcdchagin	error = copyin(PTRIN(args->namelen), &len, sizeof(len));
74657102bcdchagin	if (error != 0)
74757102bcdchagin		return (error);
748c800199dchagin	if (len < 0)
749c800199dchagin		return (EINVAL);
750abf2c20marcel
75157102bcdchagin	error = kern_getpeername(td, args->s, &sa, &len);
752554adaedchagin	if (error != 0)
7539689f05ume		return (error);
75457102bcdchagin
7557fbac81trasz	if (len != 0)
7567fbac81trasz		error = linux_copyout_sockaddr(sa, PTRIN(args->addr), len);
75757102bcdchagin
75857102bcdchagin	free(sa, M_SONAME);
75957102bcdchagin	if (error == 0)
76057102bcdchagin		error = copyout(&len, PTRIN(args->namelen), sizeof(len));
76157102bcdchagin	return (error);
762f14ea10sos}
763f14ea10sos
764c0ca16ddchaginint
7655596676julianlinux_socketpair(struct thread *td, struct linux_socketpair_args *args)
766f14ea10sos{
7671c619betrasz	int domain, error, sv[2], type;
768abf2c20marcel
7691c619betrasz	domain = linux_to_bsd_domain(args->domain);
7701c619betrasz	if (domain != PF_LOCAL)
7719f1df51dchagin		return (EAFNOSUPPORT);
7721c619betrasz	type = args->type & LINUX_SOCK_TYPE_MASK;
7731c619betrasz	if (type < 0 || type > LINUX_SOCK_MAX)
7746fb0275dchagin		return (EINVAL);
775796d34ddchagin	error = linux_set_socket_flags(args->type & ~LINUX_SOCK_TYPE_MASK,
7761c619betrasz	    &type);
777796d34ddchagin	if (error != 0)
778796d34ddchagin		return (error);
7791c619betrasz	if (args->protocol != 0 && args->protocol != PF_UNIX) {
7809f1df51dchagin		/*
7819f1df51dchagin		 * Use of PF_UNIX as protocol argument is not right,
7829f1df51dchagin		 * but Linux does it.
7839f1df51dchagin		 * Do not map PF_UNIX as its Linux value is identical
7849f1df51dchagin		 * to FreeBSD one.
7859f1df51dchagin		 */
7869f1df51dchagin		return (EPROTONOSUPPORT);
7871c619betrasz	}
7881c619betrasz	error = kern_socketpair(td, domain, type, 0, sv);
7891c619betrasz	if (error != 0)
7901c619betrasz                return (error);
7911c619betrasz        error = copyout(sv, PTRIN(args->rsv), 2 * sizeof(int));
7921c619betrasz        if (error != 0) {
793