linux_socket.c revision 132313
1238104Sdes/*-
2238104Sdes * Copyright (c) 1995 S�ren Schmidt
3238104Sdes * All rights reserved.
4238104Sdes *
5238104Sdes * Redistribution and use in source and binary forms, with or without
6238104Sdes * modification, are permitted provided that the following conditions
7238104Sdes * are met:
8238104Sdes * 1. Redistributions of source code must retain the above copyright
9238104Sdes *    notice, this list of conditions and the following disclaimer
10238104Sdes *    in this position and unchanged.
11238104Sdes * 2. Redistributions in binary form must reproduce the above copyright
12238104Sdes *    notice, this list of conditions and the following disclaimer in the
13238104Sdes *    documentation and/or other materials provided with the distribution.
14238104Sdes * 3. The name of the author may not be used to endorse or promote products
15238104Sdes *    derived from this software without specific prior written permission
16238104Sdes *
17238104Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18238104Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19238104Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20238104Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21238104Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22238104Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23238104Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24238104Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25238104Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26238104Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27238104Sdes */
28238104Sdes
29238104Sdes#include <sys/cdefs.h>
30238104Sdes__FBSDID("$FreeBSD: head/sys/compat/linux/linux_socket.c 132313 2004-07-17 21:06:36Z dwmalone $");
31238104Sdes
32238104Sdes/* XXX we use functions that might not exist. */
33238104Sdes#include "opt_compat.h"
34238104Sdes#include "opt_inet6.h"
35238104Sdes
36238104Sdes#ifndef COMPAT_43
37238104Sdes#error "Unable to compile Linux-emulator due to missing COMPAT_43 option!"
38238104Sdes#endif
39238104Sdes
40238104Sdes#include <sys/param.h>
41238104Sdes#include <sys/proc.h>
42238104Sdes#include <sys/systm.h>
43238104Sdes#include <sys/sysproto.h>
44238104Sdes#include <sys/fcntl.h>
45238104Sdes#include <sys/file.h>
46238104Sdes#include <sys/limits.h>
47238104Sdes#include <sys/malloc.h>
48238104Sdes#include <sys/mbuf.h>
49238104Sdes#include <sys/socket.h>
50238104Sdes#include <sys/socketvar.h>
51238104Sdes#include <sys/syscallsubr.h>
52238104Sdes#include <sys/uio.h>
53238104Sdes#include <sys/syslog.h>
54238104Sdes
55238104Sdes#include <netinet/in.h>
56238104Sdes#include <netinet/in_systm.h>
57238104Sdes#include <netinet/ip.h>
58238104Sdes#ifdef INET6
59238104Sdes#include <netinet/ip6.h>
60238104Sdes#include <netinet6/ip6_var.h>
61238104Sdes#endif
62238104Sdes
63238104Sdes#include <machine/../linux/linux.h>
64238104Sdes#include <machine/../linux/linux_proto.h>
65238104Sdes#include <compat/linux/linux_socket.h>
66238104Sdes#include <compat/linux/linux_util.h>
67238104Sdes
68238104Sdesstatic int do_sa_get(struct sockaddr **, const struct osockaddr *, int *,
69238104Sdes    struct malloc_type *);
70238104Sdesstatic int linux_to_bsd_domain(int);
71238104Sdes
72238104Sdes/*
73238104Sdes * Reads a linux sockaddr and does any necessary translation.
74238104Sdes * Linux sockaddrs don't have a length field, only a family.
75238104Sdes */
76238104Sdesstatic int
77238104Sdeslinux_getsockaddr(struct sockaddr **sap, const struct osockaddr *osa, int len)
78238104Sdes{
79238104Sdes	int osalen = len;
80238104Sdes
81238104Sdes	return (do_sa_get(sap, osa, &osalen, M_SONAME));
82238104Sdes}
83238104Sdes
84238104Sdes/*
85238104Sdes * Copy the osockaddr structure pointed to by osa to kernel, adjust
86238104Sdes * family and convert to sockaddr.
87238104Sdes */
88238104Sdesstatic int
89238104Sdesdo_sa_get(struct sockaddr **sap, const struct osockaddr *osa, int *osalen,
90238104Sdes    struct malloc_type *mtype)
91238104Sdes{
92238104Sdes	int error=0, bdom;
93238104Sdes	struct sockaddr *sa;
94238104Sdes	struct osockaddr *kosa;
95238104Sdes	int alloclen;
96238104Sdes#ifdef INET6
97238104Sdes	int oldv6size;
98238104Sdes	struct sockaddr_in6 *sin6;
99238104Sdes#endif
100238104Sdes
101238104Sdes	if (*osalen < 2 || *osalen > UCHAR_MAX || !osa)
102238104Sdes		return (EINVAL);
103238104Sdes
104238104Sdes	alloclen = *osalen;
105238104Sdes#ifdef INET6
106238104Sdes	oldv6size = 0;
107238104Sdes	/*
108238104Sdes	 * Check for old (pre-RFC2553) sockaddr_in6. We may accept it
109238104Sdes	 * if it's a v4-mapped address, so reserve the proper space
110238104Sdes	 * for it.
111238104Sdes	 */
112238104Sdes	if (alloclen == sizeof (struct sockaddr_in6) - sizeof (u_int32_t)) {
113238104Sdes		alloclen = sizeof (struct sockaddr_in6);
114238104Sdes		oldv6size = 1;
115238104Sdes	}
116238104Sdes#endif
117238104Sdes
118238104Sdes	MALLOC(kosa, struct osockaddr *, alloclen, mtype, M_WAITOK);
119238104Sdes
120238104Sdes	if ((error = copyin(osa, kosa, *osalen)))
121238104Sdes		goto out;
122238104Sdes
123238104Sdes	bdom = linux_to_bsd_domain(kosa->sa_family);
124238104Sdes	if (bdom == -1) {
125238104Sdes		error = EINVAL;
126238104Sdes		goto out;
127238104Sdes	}
128238104Sdes
129238104Sdes#ifdef INET6
130238104Sdes	/*
131238104Sdes	 * Older Linux IPv6 code uses obsolete RFC2133 struct sockaddr_in6,
132238104Sdes	 * which lacks the scope id compared with RFC2553 one. If we detect
133238104Sdes	 * the situation, reject the address and write a message to system log.
134238104Sdes	 *
135238104Sdes	 * Still accept addresses for which the scope id is not used.
136238104Sdes	 */
137238104Sdes	if (oldv6size && bdom == AF_INET6) {
138238104Sdes		sin6 = (struct sockaddr_in6 *)kosa;
139238104Sdes		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
140238104Sdes		    (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
141238104Sdes		     !IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) &&
142238104Sdes		     !IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) &&
143238104Sdes		     !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
144238104Sdes		     !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) {
145238104Sdes			sin6->sin6_scope_id = 0;
146238104Sdes		} else {
147238104Sdes			log(LOG_DEBUG,
148238104Sdes			    "obsolete pre-RFC2553 sockaddr_in6 rejected");
149238104Sdes			error = EINVAL;
150238104Sdes			goto out;
151238104Sdes		}
152238104Sdes	} else
153238104Sdes#endif
154238104Sdes	if (bdom == AF_INET)
155238104Sdes		alloclen = sizeof(struct sockaddr_in);
156238104Sdes
157238104Sdes	sa = (struct sockaddr *) kosa;
158238104Sdes	sa->sa_family = bdom;
159238104Sdes	sa->sa_len = alloclen;
160238104Sdes
161238104Sdes	*sap = sa;
162238104Sdes	*osalen = alloclen;
163238104Sdes	return (0);
164238104Sdes
165238104Sdesout:
166238104Sdes	FREE(kosa, mtype);
167238104Sdes	return (error);
168238104Sdes}
169238104Sdes
170238104Sdesstatic int
171238104Sdeslinux_to_bsd_domain(int domain)
172238104Sdes{
173238104Sdes
174238104Sdes	switch (domain) {
175238104Sdes	case LINUX_AF_UNSPEC:
176238104Sdes		return (AF_UNSPEC);
177238104Sdes	case LINUX_AF_UNIX:
178238104Sdes		return (AF_LOCAL);
179238104Sdes	case LINUX_AF_INET:
180238104Sdes		return (AF_INET);
181238104Sdes	case LINUX_AF_INET6:
182238104Sdes		return (AF_INET6);
183238104Sdes	case LINUX_AF_AX25:
184238104Sdes		return (AF_CCITT);
185238104Sdes	case LINUX_AF_IPX:
186238104Sdes		return (AF_IPX);
187238104Sdes	case LINUX_AF_APPLETALK:
188238104Sdes		return (AF_APPLETALK);
189238104Sdes	}
190238104Sdes	return (-1);
191238104Sdes}
192238104Sdes
193238104Sdes#ifndef __alpha__
194238104Sdesstatic int
195238104Sdesbsd_to_linux_domain(int domain)
196238104Sdes{
197238104Sdes
198238104Sdes	switch (domain) {
199238104Sdes	case AF_UNSPEC:
200238104Sdes		return (LINUX_AF_UNSPEC);
201238104Sdes	case AF_LOCAL:
202238104Sdes		return (LINUX_AF_UNIX);
203238104Sdes	case AF_INET:
204238104Sdes		return (LINUX_AF_INET);
205238104Sdes	case AF_INET6:
206238104Sdes		return (LINUX_AF_INET6);
207238104Sdes	case AF_CCITT:
208238104Sdes		return (LINUX_AF_AX25);
209238104Sdes	case AF_IPX:
210238104Sdes		return (LINUX_AF_IPX);
211238104Sdes	case AF_APPLETALK:
212238104Sdes		return (LINUX_AF_APPLETALK);
213238104Sdes	}
214238104Sdes	return (-1);
215238104Sdes}
216238104Sdes
217238104Sdesstatic int
218238104Sdeslinux_to_bsd_sockopt_level(int level)
219238104Sdes{
220238104Sdes
221238104Sdes	switch (level) {
222238104Sdes	case LINUX_SOL_SOCKET:
223238104Sdes		return (SOL_SOCKET);
224238104Sdes	}
225238104Sdes	return (level);
226238104Sdes}
227238104Sdes
228238104Sdesstatic int
229238104Sdesbsd_to_linux_sockopt_level(int level)
230238104Sdes{
231238104Sdes
232238104Sdes	switch (level) {
233238104Sdes	case SOL_SOCKET:
234238104Sdes		return (LINUX_SOL_SOCKET);
235238104Sdes	}
236238104Sdes	return (level);
237238104Sdes}
238238104Sdes
239238104Sdesstatic int
240238104Sdeslinux_to_bsd_ip_sockopt(int opt)
241238104Sdes{
242238104Sdes
243238104Sdes	switch (opt) {
244238104Sdes	case LINUX_IP_TOS:
245238104Sdes		return (IP_TOS);
246238104Sdes	case LINUX_IP_TTL:
247238104Sdes		return (IP_TTL);
248238104Sdes	case LINUX_IP_OPTIONS:
249238104Sdes		return (IP_OPTIONS);
250238104Sdes	case LINUX_IP_MULTICAST_IF:
251238104Sdes		return (IP_MULTICAST_IF);
252238104Sdes	case LINUX_IP_MULTICAST_TTL:
253238104Sdes		return (IP_MULTICAST_TTL);
254238104Sdes	case LINUX_IP_MULTICAST_LOOP:
255238104Sdes		return (IP_MULTICAST_LOOP);
256238104Sdes	case LINUX_IP_ADD_MEMBERSHIP:
257238104Sdes		return (IP_ADD_MEMBERSHIP);
258238104Sdes	case LINUX_IP_DROP_MEMBERSHIP:
259238104Sdes		return (IP_DROP_MEMBERSHIP);
260238104Sdes	case LINUX_IP_HDRINCL:
261238104Sdes		return (IP_HDRINCL);
262238104Sdes	}
263238104Sdes	return (-1);
264238104Sdes}
265238104Sdes
266238104Sdesstatic int
267238104Sdeslinux_to_bsd_so_sockopt(int opt)
268238104Sdes{
269238104Sdes
270238104Sdes	switch (opt) {
271238104Sdes	case LINUX_SO_DEBUG:
272238104Sdes		return (SO_DEBUG);
273238104Sdes	case LINUX_SO_REUSEADDR:
274238104Sdes		return (SO_REUSEADDR);
275238104Sdes	case LINUX_SO_TYPE:
276238104Sdes		return (SO_TYPE);
277238104Sdes	case LINUX_SO_ERROR:
278238104Sdes		return (SO_ERROR);
279238104Sdes	case LINUX_SO_DONTROUTE:
280238104Sdes		return (SO_DONTROUTE);
281238104Sdes	case LINUX_SO_BROADCAST:
282238104Sdes		return (SO_BROADCAST);
283238104Sdes	case LINUX_SO_SNDBUF:
284238104Sdes		return (SO_SNDBUF);
285238104Sdes	case LINUX_SO_RCVBUF:
286238104Sdes		return (SO_RCVBUF);
287238104Sdes	case LINUX_SO_KEEPALIVE:
288238104Sdes		return (SO_KEEPALIVE);
289238104Sdes	case LINUX_SO_OOBINLINE:
290238104Sdes		return (SO_OOBINLINE);
291238104Sdes	case LINUX_SO_LINGER:
292238104Sdes		return (SO_LINGER);
293238104Sdes	}
294238104Sdes	return (-1);
295238104Sdes}
296238104Sdes
297238104Sdesstatic int
298238104Sdeslinux_to_bsd_msg_flags(int flags)
299238104Sdes{
300238104Sdes	int ret_flags = 0;
301238104Sdes
302238104Sdes	if (flags & LINUX_MSG_OOB)
303238104Sdes		ret_flags |= MSG_OOB;
304238104Sdes	if (flags & LINUX_MSG_PEEK)
305238104Sdes		ret_flags |= MSG_PEEK;
306238104Sdes	if (flags & LINUX_MSG_DONTROUTE)
307238104Sdes		ret_flags |= MSG_DONTROUTE;
308238104Sdes	if (flags & LINUX_MSG_CTRUNC)
309238104Sdes		ret_flags |= MSG_CTRUNC;
310238104Sdes	if (flags & LINUX_MSG_TRUNC)
311238104Sdes		ret_flags |= MSG_TRUNC;
312238104Sdes	if (flags & LINUX_MSG_DONTWAIT)
313238104Sdes		ret_flags |= MSG_DONTWAIT;
314238104Sdes	if (flags & LINUX_MSG_EOR)
315238104Sdes		ret_flags |= MSG_EOR;
316238104Sdes	if (flags & LINUX_MSG_WAITALL)
317238104Sdes		ret_flags |= MSG_WAITALL;
318238104Sdes#if 0 /* not handled */
319238104Sdes	if (flags & LINUX_MSG_PROXY)
320238104Sdes		;
321238104Sdes	if (flags & LINUX_MSG_FIN)
322238104Sdes		;
323238104Sdes	if (flags & LINUX_MSG_SYN)
324238104Sdes		;
325238104Sdes	if (flags & LINUX_MSG_CONFIRM)
326238104Sdes		;
327238104Sdes	if (flags & LINUX_MSG_RST)
328238104Sdes		;
329238104Sdes	if (flags & LINUX_MSG_ERRQUEUE)
330238104Sdes		;
331238104Sdes	if (flags & LINUX_MSG_NOSIGNAL)
332238104Sdes		;
333238104Sdes#endif
334238104Sdes	return ret_flags;
335238104Sdes}
336238104Sdes
337238104Sdesstatic int
338238104Sdeslinux_sa_put(struct osockaddr *osa)
339238104Sdes{
340238104Sdes	struct osockaddr sa;
341238104Sdes	int error, bdom;
342238104Sdes
343238104Sdes	/*
344238104Sdes	 * Only read/write the osockaddr family part, the rest is
345238104Sdes	 * not changed.
346238104Sdes	 */
347238104Sdes	error = copyin(osa, &sa, sizeof(sa.sa_family));
348238104Sdes	if (error)
349238104Sdes		return (error);
350238104Sdes
351238104Sdes	bdom = bsd_to_linux_domain(sa.sa_family);
352238104Sdes	if (bdom == -1)
353238104Sdes		return (EINVAL);
354238104Sdes
355238104Sdes	sa.sa_family = bdom;
356238104Sdes	error = copyout(&sa, osa, sizeof(sa.sa_family));
357238104Sdes	if (error)
358238104Sdes		return (error);
359238104Sdes
360238104Sdes	return (0);
361238104Sdes}
362238104Sdes
363238104Sdesstatic int
364238104Sdeslinux_sendit(struct thread *td, int s, struct msghdr *mp, int flags)
365238104Sdes{
366238104Sdes	struct mbuf *control;
367238104Sdes	struct sockaddr *to;
368238104Sdes	int error;
369238104Sdes
370238104Sdes	if (mp->msg_name != NULL) {
371238104Sdes		error = linux_getsockaddr(&to, mp->msg_name, mp->msg_namelen);
372238104Sdes		if (error)
373238104Sdes			return (error);
374238104Sdes		mp->msg_name = to;
375238104Sdes	} else
376238104Sdes		to = NULL;
377238104Sdes
378238104Sdes	if (mp->msg_control != NULL) {
379238104Sdes		struct cmsghdr *cmsg;
380238104Sdes
381238104Sdes		if (mp->msg_controllen < sizeof(struct cmsghdr)) {
382238104Sdes			error = EINVAL;
383238104Sdes			goto bad;
384238104Sdes		}
385238104Sdes		error = sockargs(&control, mp->msg_control,
386238104Sdes		    mp->msg_controllen, MT_CONTROL);
387238104Sdes		if (error)
388238104Sdes			goto bad;
389238104Sdes
390238104Sdes		cmsg = mtod(control, struct cmsghdr *);
391238104Sdes		cmsg->cmsg_level = linux_to_bsd_sockopt_level(cmsg->cmsg_level);
392238104Sdes	} else
393238104Sdes		control = NULL;
394238104Sdes
395238104Sdes	error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control);
396238104Sdes
397238104Sdesbad:
398238104Sdes	if (to)
399238104Sdes		FREE(to, M_SONAME);
400238104Sdes	return (error);
401238104Sdes}
402238104Sdes
403238104Sdes/* Return 0 if IP_HDRINCL is set for the given socket. */
404238104Sdesstatic int
405238104Sdeslinux_check_hdrincl(struct thread *td, int s)
406238104Sdes{
407238104Sdes	int error, optval, size_val;
408238104Sdes
409238104Sdes	size_val = sizeof(optval);
410238104Sdes	error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
411238104Sdes	    &optval, UIO_SYSSPACE, &size_val);
412238104Sdes	if (error)
413238104Sdes		return (error);
414238104Sdes
415238104Sdes	return (optval == 0);
416238104Sdes}
417238104Sdes
418238104Sdesstruct linux_sendto_args {
419238104Sdes	int s;
420238104Sdes	void *msg;
421238104Sdes	int len;
422238104Sdes	int flags;
423238104Sdes	caddr_t to;
424238104Sdes	int tolen;
425238104Sdes};
426238104Sdes
427238104Sdes/*
428238104Sdes * Updated sendto() when IP_HDRINCL is set:
429238104Sdes * tweak endian-dependent fields in the IP packet.
430238104Sdes */
431238104Sdesstatic int
432238104Sdeslinux_sendto_hdrincl(struct thread *td, caddr_t *sg, struct linux_sendto_args *linux_args)
433238104Sdes{
434238104Sdes/*
435238104Sdes * linux_ip_copysize defines how many bytes we should copy
436238104Sdes * from the beginning of the IP packet before we customize it for BSD.
437238104Sdes * It should include all the fields we modify (ip_len and ip_off)
438238104Sdes * and be as small as possible to minimize copying overhead.
439238104Sdes */
440238104Sdes#define linux_ip_copysize	8
441238104Sdes
442238104Sdes	struct ip *packet;
443238104Sdes	caddr_t sg = stackgap_init();
444238104Sdes	struct msghdr msg;
445238104Sdes	struct iovec aiov[2];
446238104Sdes	int error;
447238104Sdes
448238104Sdes	/* Check the packet isn't too small before we mess with it */
449238104Sdes	if (linux_args->len < linux_ip_copysize)
450238104Sdes		return (EINVAL);
451238104Sdes
452238104Sdes	/*
453238104Sdes	 * Tweaking the user buffer in place would be bad manners.
454238104Sdes	 * We create a corrected IP header with just the needed length,
455238104Sdes	 * then use an iovec to glue it to the rest of the user packet
456238104Sdes	 * when calling sendit().
457238104Sdes	 */
458238104Sdes	packet = (struct ip *)stackgap_alloc(sg, linux_ip_copysize);
459238104Sdes
460238104Sdes	/* Make a copy of the beginning of the packet to be sent */
461238104Sdes	if ((error = copyin(linux_args->msg, packet, linux_ip_copysize)))
462238104Sdes		return (error);
463238104Sdes
464238104Sdes	/* Convert fields from Linux to BSD raw IP socket format */
465238104Sdes	packet->ip_len = linux_args->len;
466238104Sdes	packet->ip_off = ntohs(packet->ip_off);
467238104Sdes
468238104Sdes	/* Prepare the msghdr and iovec structures describing the new packet */
469238104Sdes	msg.msg_name = linux_args->to;
470238104Sdes	msg.msg_namelen = linux_args->tolen;
471238104Sdes	msg.msg_iov = aiov;
472238104Sdes	msg.msg_iovlen = 2;
473238104Sdes	msg.msg_control = NULL;
474238104Sdes	msg.msg_flags = 0;
475238104Sdes	aiov[0].iov_base = (char *)packet;
476238104Sdes	aiov[0].iov_len = linux_ip_copysize;
477238104Sdes	aiov[1].iov_base = (char *)(linux_args->msg) + linux_ip_copysize;
478238104Sdes	aiov[1].iov_len = linux_args->len - linux_ip_copysize;
479238104Sdes	error = linux_sendit(td, linux_args->s, &msg, linux_args->flags);
480238104Sdes	return (error);
481238104Sdes}
482238104Sdes
483238104Sdesstruct linux_socket_args {
484238104Sdes	int domain;
485238104Sdes	int type;
486238104Sdes	int protocol;
487238104Sdes};
488238104Sdes
489238104Sdesstatic int
490238104Sdeslinux_socket(struct thread *td, struct linux_socket_args *args)
491238104Sdes{
492238104Sdes	struct linux_socket_args linux_args;
493238104Sdes	struct socket_args /* {
494238104Sdes		int domain;
495238104Sdes		int type;
496238104Sdes		int protocol;
497238104Sdes	} */ bsd_args;
498238104Sdes	int error;
499238104Sdes	int retval_socket;
500238104Sdes
501238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
502238104Sdes		return (error);
503238104Sdes
504238104Sdes	bsd_args.protocol = linux_args.protocol;
505238104Sdes	bsd_args.type = linux_args.type;
506238104Sdes	bsd_args.domain = linux_to_bsd_domain(linux_args.domain);
507238104Sdes	if (bsd_args.domain == -1)
508238104Sdes		return (EINVAL);
509238104Sdes
510238104Sdes	retval_socket = socket(td, &bsd_args);
511238104Sdes	if (bsd_args.type == SOCK_RAW
512238104Sdes	    && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0)
513238104Sdes	    && bsd_args.domain == AF_INET
514238104Sdes	    && retval_socket >= 0) {
515238104Sdes		/* It's a raw IP socket: set the IP_HDRINCL option. */
516238104Sdes		int hdrincl;
517238104Sdes
518238104Sdes		hdrincl = 1;
519238104Sdes		/* We ignore any error returned by kern_setsockopt() */
520238104Sdes		kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
521238104Sdes		    &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
522238104Sdes	}
523238104Sdes#ifdef INET6
524238104Sdes	/*
525238104Sdes	 * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by
526238104Sdes	 * default and some apps depend on this. So, set V6ONLY to 0
527238104Sdes	 * for Linux apps if the sysctl value is set to 1.
528238104Sdes	 */
529238104Sdes	if (bsd_args.domain == PF_INET6 && retval_socket >= 0
530238104Sdes#ifndef KLD_MODULE
531238104Sdes	    /*
532238104Sdes	     * XXX: Avoid undefined symbol error with an IPv4 only
533238104Sdes	     * kernel.
534238104Sdes	     */
535238104Sdes	    && ip6_v6only
536238104Sdes#endif
537238104Sdes	    ) {
538238104Sdes		int v6only;
539238104Sdes
540238104Sdes		v6only = 0;
541238104Sdes		/* We ignore any error returned by setsockopt() */
542238104Sdes		kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
543238104Sdes		    &v6only, UIO_SYSSPACE, sizeof(v6only));
544238104Sdes	}
545238104Sdes#endif
546238104Sdes
547238104Sdes	return (retval_socket);
548238104Sdes}
549238104Sdes
550238104Sdesstruct linux_bind_args {
551238104Sdes	int s;
552238104Sdes	struct osockaddr *name;
553238104Sdes	int namelen;
554238104Sdes};
555238104Sdes
556238104Sdesstatic int
557238104Sdeslinux_bind(struct thread *td, struct linux_bind_args *args)
558238104Sdes{
559238104Sdes	struct linux_bind_args linux_args;
560238104Sdes	struct sockaddr *sa;
561238104Sdes	int error;
562238104Sdes
563238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
564238104Sdes		return (error);
565238104Sdes
566238104Sdes	error = linux_getsockaddr(&sa, linux_args.name, linux_args.namelen);
567238104Sdes	if (error)
568238104Sdes		return (error);
569238104Sdes
570238104Sdes	return (kern_bind(td, linux_args.s, sa));
571238104Sdes}
572238104Sdes
573238104Sdesstruct linux_connect_args {
574238104Sdes	int s;
575238104Sdes	struct osockaddr * name;
576238104Sdes	int namelen;
577238104Sdes};
578238104Sdesint linux_connect(struct thread *, struct linux_connect_args *);
579238104Sdes#endif /* !__alpha__*/
580238104Sdes
581238104Sdesint
582238104Sdeslinux_connect(struct thread *td, struct linux_connect_args *args)
583238104Sdes{
584238104Sdes	struct linux_connect_args linux_args;
585238104Sdes	struct socket *so;
586238104Sdes	struct sockaddr *sa;
587238104Sdes	u_int fflag;
588238104Sdes	int error;
589238104Sdes
590238104Sdes#ifdef __alpha__
591238104Sdes	bcopy(args, &linux_args, sizeof(linux_args));
592238104Sdes#else
593238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
594238104Sdes		return (error);
595238104Sdes#endif /* __alpha__ */
596238104Sdes
597238104Sdes	error = linux_getsockaddr(&sa, (struct osockaddr *)linux_args.name,
598238104Sdes	    linux_args.namelen);
599238104Sdes	if (error)
600238104Sdes		return (error);
601238104Sdes
602238104Sdes	error = kern_connect(td, linux_args.s, sa);
603238104Sdes	if (error != EISCONN)
604238104Sdes		return (error);
605238104Sdes
606238104Sdes	/*
607238104Sdes	 * Linux doesn't return EISCONN the first time it occurs,
608238104Sdes	 * when on a non-blocking socket. Instead it returns the
609238104Sdes	 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
610238104Sdes	 */
611238104Sdes	if ((error = fgetsock(td, linux_args.s, &so, &fflag)) != 0)
612238104Sdes		return(error);
613238104Sdes	error = EISCONN;
614238104Sdes	if (fflag & FNONBLOCK) {
615238104Sdes		if (so->so_emuldata == 0)
616238104Sdes			error = so->so_error;
617238104Sdes		so->so_emuldata = (void *)1;
618238104Sdes	}
619238104Sdes	fputsock(so);
620238104Sdes	return (error);
621238104Sdes}
622238104Sdes
623238104Sdes#ifndef __alpha__
624238104Sdes
625238104Sdesstruct linux_listen_args {
626238104Sdes	int s;
627238104Sdes	int backlog;
628238104Sdes};
629238104Sdes
630238104Sdesstatic int
631238104Sdeslinux_listen(struct thread *td, struct linux_listen_args *args)
632238104Sdes{
633238104Sdes	struct linux_listen_args linux_args;
634238104Sdes	struct listen_args /* {
635238104Sdes		int s;
636238104Sdes		int backlog;
637238104Sdes	} */ bsd_args;
638238104Sdes	int error;
639238104Sdes
640238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
641238104Sdes		return (error);
642238104Sdes
643238104Sdes	bsd_args.s = linux_args.s;
644238104Sdes	bsd_args.backlog = linux_args.backlog;
645238104Sdes	return (listen(td, &bsd_args));
646238104Sdes}
647238104Sdes
648238104Sdesstruct linux_accept_args {
649238104Sdes	int s;
650238104Sdes	struct osockaddr *addr;
651238104Sdes	int *namelen;
652238104Sdes};
653238104Sdes
654238104Sdesstatic int
655238104Sdeslinux_accept(struct thread *td, struct linux_accept_args *args)
656238104Sdes{
657269257Sdes	struct linux_accept_args linux_args;
658269257Sdes	struct accept_args /* {
659269257Sdes		int	s;
660269257Sdes		struct sockaddr * __restrict name;
661269257Sdes		socklen_t * __restrict anamelen;
662269257Sdes	} */ bsd_args;
663269257Sdes	struct close_args /* {
664269257Sdes		int     fd;
665269257Sdes	} */ c_args;
666269257Sdes	struct fcntl_args /* {
667269257Sdes		int	fd;
668269257Sdes		int	cmd;
669269257Sdes		long	arg;
670269257Sdes	} */ f_args;
671269257Sdes	int error;
672269257Sdes
673269257Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
674269257Sdes		return (error);
675269257Sdes
676269257Sdes	bsd_args.s = linux_args.s;
677269257Sdes	/* XXX: */
678269257Sdes	bsd_args.name = (struct sockaddr * __restrict)linux_args.addr;
679269257Sdes	bsd_args.anamelen = linux_args.namelen;		/* XXX */
680269257Sdes	error = oaccept(td, &bsd_args);
681269257Sdes	if (error)
682269257Sdes		return (error);
683269257Sdes	if (linux_args.addr) {
684238104Sdes		error = linux_sa_put(linux_args.addr);
685238104Sdes		if (error) {
686238104Sdes			c_args.fd = td->td_retval[0];
687238104Sdes			(void)close(td, &c_args);
688238104Sdes			return (error);
689269257Sdes		}
690269257Sdes	}
691269257Sdes
692269257Sdes	/*
693269257Sdes	 * linux appears not to copy flags from the parent socket to the
694269257Sdes	 * accepted one, so we must clear the flags in the new descriptor.
695269257Sdes	 * Ignore any errors, because we already have an open fd.
696269257Sdes	 */
697269257Sdes	f_args.fd = td->td_retval[0];
698238104Sdes	f_args.cmd = F_SETFL;
699269257Sdes	f_args.arg = 0;
700269257Sdes	(void)fcntl(td, &f_args);
701269257Sdes	td->td_retval[0] = f_args.fd;
702269257Sdes	return (0);
703238104Sdes}
704238104Sdes
705238104Sdesstruct linux_getsockname_args {
706238104Sdes	int s;
707238104Sdes	struct osockaddr *addr;
708238104Sdes	int *namelen;
709269257Sdes};
710269257Sdes
711269257Sdesstatic int
712269257Sdeslinux_getsockname(struct thread *td, struct linux_getsockname_args *args)
713269257Sdes{
714269257Sdes	struct linux_getsockname_args linux_args;
715269257Sdes	struct getsockname_args /* {
716269257Sdes		int	fdes;
717238104Sdes		struct sockaddr * __restrict asa;
718238104Sdes		socklen_t * __restrict alen;
719269257Sdes	} */ bsd_args;
720269257Sdes	int error;
721269257Sdes
722269257Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
723269257Sdes		return (error);
724269257Sdes
725269257Sdes	bsd_args.fdes = linux_args.s;
726238104Sdes	/* XXX: */
727269257Sdes	bsd_args.asa = (struct sockaddr * __restrict)linux_args.addr;
728269257Sdes	bsd_args.alen = linux_args.namelen;	/* XXX */
729269257Sdes	error = ogetsockname(td, &bsd_args);
730269257Sdes	if (error)
731269257Sdes		return (error);
732269257Sdes	error = linux_sa_put(linux_args.addr);
733269257Sdes	if (error)
734269257Sdes		return (error);
735269257Sdes	return (0);
736269257Sdes}
737269257Sdes
738238104Sdesstruct linux_getpeername_args {
739269257Sdes	int s;
740269257Sdes	struct osockaddr *addr;
741269257Sdes	int *namelen;
742269257Sdes};
743238104Sdes
744269257Sdesstatic int
745238104Sdeslinux_getpeername(struct thread *td, struct linux_getpeername_args *args)
746238104Sdes{
747238104Sdes	struct linux_getpeername_args linux_args;
748269257Sdes	struct ogetpeername_args /* {
749269257Sdes		int fdes;
750269257Sdes		caddr_t asa;
751269257Sdes		int *alen;
752269257Sdes	} */ bsd_args;
753269257Sdes	int error;
754238104Sdes
755238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
756269257Sdes		return (error);
757269257Sdes
758269257Sdes	bsd_args.fdes = linux_args.s;
759269257Sdes	bsd_args.asa = (caddr_t) linux_args.addr;
760269257Sdes	bsd_args.alen = linux_args.namelen;
761269257Sdes	error = ogetpeername(td, &bsd_args);
762269257Sdes	if (error)
763269257Sdes		return (error);
764238104Sdes	error = linux_sa_put(linux_args.addr);
765238104Sdes	if (error)
766238104Sdes		return (error);
767238104Sdes	return (0);
768238104Sdes}
769238104Sdes
770238104Sdesstruct linux_socketpair_args {
771238104Sdes	int domain;
772238104Sdes	int type;
773238104Sdes	int protocol;
774238104Sdes	int *rsv;
775238104Sdes};
776238104Sdes
777238104Sdesstatic int
778238104Sdeslinux_socketpair(struct thread *td, struct linux_socketpair_args *args)
779238104Sdes{
780238104Sdes	struct linux_socketpair_args linux_args;
781238104Sdes	struct socketpair_args /* {
782238104Sdes		int domain;
783238104Sdes		int type;
784238104Sdes		int protocol;
785238104Sdes		int *rsv;
786238104Sdes	} */ bsd_args;
787238104Sdes	int error;
788238104Sdes
789238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
790238104Sdes		return (error);
791238104Sdes
792238104Sdes	bsd_args.domain = linux_to_bsd_domain(linux_args.domain);
793238104Sdes	if (bsd_args.domain == -1)
794238104Sdes		return (EINVAL);
795238104Sdes
796238104Sdes	bsd_args.type = linux_args.type;
797238104Sdes	bsd_args.protocol = linux_args.protocol;
798238104Sdes	bsd_args.rsv = linux_args.rsv;
799238104Sdes	return (socketpair(td, &bsd_args));
800238104Sdes}
801238104Sdes
802238104Sdesstruct linux_send_args {
803238104Sdes	int s;
804238104Sdes	void *msg;
805238104Sdes	int len;
806238104Sdes	int flags;
807238104Sdes};
808238104Sdes
809238104Sdesstatic int
810238104Sdeslinux_send(struct thread *td, struct linux_send_args *args)
811238104Sdes{
812238104Sdes	struct linux_send_args linux_args;
813238104Sdes	struct sendto_args /* {
814238104Sdes		int s;
815238104Sdes		caddr_t buf;
816238104Sdes		int len;
817238104Sdes		int flags;
818238104Sdes		caddr_t to;
819238104Sdes		int tolen;
820238104Sdes	} */ bsd_args;
821238104Sdes	int error;
822238104Sdes
823238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
824238104Sdes		return (error);
825238104Sdes
826238104Sdes	bsd_args.s = linux_args.s;
827238104Sdes	bsd_args.buf = linux_args.msg;
828238104Sdes	bsd_args.len = linux_args.len;
829238104Sdes	bsd_args.flags = linux_args.flags;
830238104Sdes	bsd_args.to = NULL;
831238104Sdes	bsd_args.tolen = 0;
832238104Sdes	return (sendto(td, &bsd_args));
833238104Sdes}
834238104Sdes
835238104Sdesstruct linux_recv_args {
836238104Sdes	int s;
837238104Sdes	void *msg;
838238104Sdes	int len;
839238104Sdes	int flags;
840238104Sdes};
841238104Sdes
842238104Sdesstatic int
843238104Sdeslinux_recv(struct thread *td, struct linux_recv_args *args)
844238104Sdes{
845238104Sdes	struct linux_recv_args linux_args;
846238104Sdes	struct recvfrom_args /* {
847238104Sdes		int s;
848238104Sdes		caddr_t buf;
849238104Sdes		int len;
850238104Sdes		int flags;
851238104Sdes		struct sockaddr *from;
852238104Sdes		socklen_t fromlenaddr;
853238104Sdes	} */ bsd_args;
854238104Sdes	int error;
855238104Sdes
856238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
857238104Sdes		return (error);
858238104Sdes
859238104Sdes	bsd_args.s = linux_args.s;
860238104Sdes	bsd_args.buf = linux_args.msg;
861238104Sdes	bsd_args.len = linux_args.len;
862238104Sdes	bsd_args.flags = linux_args.flags;
863238104Sdes	bsd_args.from = NULL;
864238104Sdes	bsd_args.fromlenaddr = 0;
865238104Sdes	return (recvfrom(td, &bsd_args));
866238104Sdes}
867238104Sdes
868238104Sdesstatic int
869238104Sdeslinux_sendto(struct thread *td, struct linux_sendto_args *args)
870238104Sdes{
871238104Sdes	struct linux_sendto_args linux_args;
872238104Sdes	struct msghdr msg;
873238104Sdes	struct iovec aiov;
874238104Sdes	int error;
875238104Sdes
876238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
877238104Sdes		return (error);
878238104Sdes
879238104Sdes	if (linux_check_hdrincl(td, linux_args.s) == 0)
880238104Sdes		/* IP_HDRINCL set, tweak the packet before sending */
881238104Sdes		return (linux_sendto_hdrincl(td, &linux_args));
882238104Sdes
883238104Sdes	msg.msg_name = linux_args.to;
884238104Sdes	msg.msg_namelen = linux_args.tolen;
885238104Sdes	msg.msg_iov = &aiov;
886238104Sdes	msg.msg_iovlen = 1;
887238104Sdes	msg.msg_control = NULL;
888238104Sdes	msg.msg_flags = 0;
889238104Sdes	aiov.iov_base = linux_args.msg;
890238104Sdes	aiov.iov_len = linux_args.len;
891238104Sdes	error = linux_sendit(td, linux_args.s, &msg, linux_args.flags);
892238104Sdes	return (error);
893238104Sdes}
894238104Sdes
895238104Sdesstruct linux_recvfrom_args {
896238104Sdes	int s;
897238104Sdes	void *buf;
898238104Sdes	int len;
899238104Sdes	int flags;
900238104Sdes	caddr_t from;
901238104Sdes	int *fromlen;
902238104Sdes};
903238104Sdes
904238104Sdesstatic int
905238104Sdeslinux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
906238104Sdes{
907238104Sdes	struct linux_recvfrom_args linux_args;
908238104Sdes	struct recvfrom_args /* {
909238104Sdes		int	s;
910238104Sdes		caddr_t	buf;
911238104Sdes		size_t	len;
912238104Sdes		int	flags;
913238104Sdes		struct sockaddr * __restrict from;
914238104Sdes		socklen_t * __restrict fromlenaddr;
915238104Sdes	} */ bsd_args;
916238104Sdes	int error;
917238104Sdes
918238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
919238104Sdes		return (error);
920238104Sdes
921238104Sdes	bsd_args.s = linux_args.s;
922238104Sdes	bsd_args.buf = linux_args.buf;
923238104Sdes	bsd_args.len = linux_args.len;
924238104Sdes	bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags);
925238104Sdes	/* XXX: */
926238104Sdes	bsd_args.from = (struct sockaddr * __restrict)linux_args.from;
927238104Sdes	bsd_args.fromlenaddr = linux_args.fromlen;	/* XXX */
928238104Sdes	error = orecvfrom(td, &bsd_args);
929238104Sdes	if (error)
930238104Sdes		return (error);
931238104Sdes	if (linux_args.from) {
932238104Sdes		error = linux_sa_put((struct osockaddr *) linux_args.from);
933238104Sdes		if (error)
934238104Sdes			return (error);
935238104Sdes	}
936238104Sdes	return (0);
937238104Sdes}
938238104Sdes
939238104Sdesstruct linux_sendmsg_args {
940238104Sdes	int s;
941238104Sdes	const struct msghdr *msg;
942238104Sdes	int flags;
943238104Sdes};
944238104Sdes
945238104Sdesstatic int
946238104Sdeslinux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
947238104Sdes{
948238104Sdes	struct linux_sendmsg_args linux_args;
949238104Sdes	struct msghdr msg;
950238104Sdes	struct iovec *iov;
951238104Sdes	int error;
952238104Sdes
953238104Sdes	error = copyin(args, &linux_args, sizeof(linux_args));
954238104Sdes	if (error)
955238104Sdes		return (error);
956238104Sdes	error = copyin(linux_args.msg, &msg, sizeof(msg));
957238104Sdes	if (error)
958238104Sdes		return (error);
959238104Sdes	error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
960238104Sdes	if (error)
961238104Sdes		return (error);
962238104Sdes	msg.msg_iov = iov;
963238104Sdes	msg.msg_flags = 0;
964238104Sdes	error = linux_sendit(td, linux_args.s, &msg, linux_args.flags);
965238104Sdes	free(iov, M_IOV);
966238104Sdes	return (error);
967238104Sdes}
968238104Sdes
969238104Sdesstruct linux_recvmsg_args {
970238104Sdes	int s;
971238104Sdes	struct msghdr *msg;
972238104Sdes	int flags;
973238104Sdes};
974238104Sdes
975238104Sdesstatic int
976238104Sdeslinux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
977238104Sdes{
978238104Sdes	struct linux_recvmsg_args linux_args;
979238104Sdes	struct recvmsg_args /* {
980238104Sdes		int	s;
981238104Sdes		struct	msghdr *msg;
982238104Sdes		int	flags;
983238104Sdes	} */ bsd_args;
984238104Sdes	struct msghdr msg;
985238104Sdes	struct cmsghdr *cmsg;
986238104Sdes	int error;
987238104Sdes
988238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
989238104Sdes		return (error);
990238104Sdes
991238104Sdes	bsd_args.s = linux_args.s;
992238104Sdes	bsd_args.msg = linux_args.msg;
993238104Sdes	bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags);
994238104Sdes	error = recvmsg(td, &bsd_args);
995238104Sdes	if (error)
996238104Sdes		return (error);
997238104Sdes
998238104Sdes	if (bsd_args.msg->msg_control != NULL) {
999238104Sdes		cmsg = (struct cmsghdr*)bsd_args.msg->msg_control;
1000269257Sdes		cmsg->cmsg_level = bsd_to_linux_sockopt_level(cmsg->cmsg_level);
1001238104Sdes	}
1002269257Sdes
1003238104Sdes	error = copyin(linux_args.msg, &msg, sizeof(msg));
1004238104Sdes	if (error)
1005238104Sdes		return (error);
1006238104Sdes	if (msg.msg_name && msg.msg_namelen > 2)
1007238104Sdes		error = linux_sa_put(msg.msg_name);
1008238104Sdes	return (error);
1009238104Sdes}
1010238104Sdes
1011238104Sdesstruct linux_shutdown_args {
1012238104Sdes	int s;
1013238104Sdes	int how;
1014238104Sdes};
1015238104Sdes
1016238104Sdesstatic int
1017238104Sdeslinux_shutdown(struct thread *td, struct linux_shutdown_args *args)
1018238104Sdes{
1019238104Sdes	struct linux_shutdown_args linux_args;
1020238104Sdes	struct shutdown_args /* {
1021238104Sdes		int s;
1022238104Sdes		int how;
1023238104Sdes	} */ bsd_args;
1024238104Sdes	int error;
1025238104Sdes
1026238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
1027238104Sdes		return (error);
1028238104Sdes
1029238104Sdes	bsd_args.s = linux_args.s;
1030238104Sdes	bsd_args.how = linux_args.how;
1031238104Sdes	return (shutdown(td, &bsd_args));
1032238104Sdes}
1033238104Sdes
1034238104Sdesstruct linux_setsockopt_args {
1035238104Sdes	int s;
1036238104Sdes	int level;
1037238104Sdes	int optname;
1038238104Sdes	void *optval;
1039238104Sdes	int optlen;
1040238104Sdes};
1041238104Sdes
1042238104Sdesstatic int
1043238104Sdeslinux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
1044238104Sdes{
1045238104Sdes	struct linux_setsockopt_args linux_args;
1046238104Sdes	struct setsockopt_args /* {
1047269257Sdes		int s;
1048238104Sdes		int level;
1049238104Sdes		int name;
1050238104Sdes		caddr_t val;
1051238104Sdes		int valsize;
1052238104Sdes	} */ bsd_args;
1053269257Sdes	int error, name;
1054238104Sdes
1055238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
1056238104Sdes		return (error);
1057238104Sdes
1058238104Sdes	bsd_args.s = linux_args.s;
1059238104Sdes	bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level);
1060238104Sdes	switch (bsd_args.level) {
1061269257Sdes	case SOL_SOCKET:
1062238104Sdes		name = linux_to_bsd_so_sockopt(linux_args.optname);
1063269257Sdes		break;
1064238104Sdes	case IPPROTO_IP:
1065238104Sdes		name = linux_to_bsd_ip_sockopt(linux_args.optname);
1066238104Sdes		break;
1067238104Sdes	case IPPROTO_TCP:
1068238104Sdes		/* Linux TCP option values match BSD's */
1069238104Sdes		name = linux_args.optname;
1070238104Sdes		break;
1071238104Sdes	default:
1072238104Sdes		name = -1;
1073238104Sdes		break;
1074238104Sdes	}
1075238104Sdes	if (name == -1)
1076238104Sdes		return (EINVAL);
1077238104Sdes
1078238104Sdes	bsd_args.name = name;
1079238104Sdes	bsd_args.val = linux_args.optval;
1080238104Sdes	bsd_args.valsize = linux_args.optlen;
1081238104Sdes	return (setsockopt(td, &bsd_args));
1082238104Sdes}
1083238104Sdes
1084238104Sdesstruct linux_getsockopt_args {
1085238104Sdes	int s;
1086238104Sdes	int level;
1087238104Sdes	int optname;
1088238104Sdes	void *optval;
1089238104Sdes	int *optlen;
1090238104Sdes};
1091238104Sdes
1092238104Sdesstatic int
1093238104Sdeslinux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
1094238104Sdes{
1095238104Sdes	struct linux_getsockopt_args linux_args;
1096238104Sdes	struct getsockopt_args /* {
1097238104Sdes		int s;
1098238104Sdes		int level;
1099238104Sdes		int name;
1100238104Sdes		caddr_t val;
1101238104Sdes		int *avalsize;
1102238104Sdes	} */ bsd_args;
1103238104Sdes	int error, name;
1104238104Sdes
1105238104Sdes	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
1106238104Sdes		return (error);
1107238104Sdes
1108238104Sdes	bsd_args.s = linux_args.s;
1109238104Sdes	bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level);
1110238104Sdes	switch (bsd_args.level) {
1111238104Sdes	case SOL_SOCKET:
1112238104Sdes		name = linux_to_bsd_so_sockopt(linux_args.optname);
1113238104Sdes		break;
1114238104Sdes	case IPPROTO_IP:
1115238104Sdes		name = linux_to_bsd_ip_sockopt(linux_args.optname);
1116238104Sdes		break;
1117238104Sdes	case IPPROTO_TCP:
1118238104Sdes		/* Linux TCP option values match BSD's */
1119238104Sdes		name = linux_args.optname;
1120238104Sdes		break;
1121238104Sdes	default:
1122238104Sdes		name = -1;
1123238104Sdes		break;
1124238104Sdes	}
1125238104Sdes	if (name == -1)
1126238104Sdes		return (EINVAL);
1127238104Sdes
1128238104Sdes	bsd_args.name = name;
1129238104Sdes	bsd_args.val = linux_args.optval;
1130238104Sdes	bsd_args.avalsize = linux_args.optlen;
1131238104Sdes	return (getsockopt(td, &bsd_args));
1132238104Sdes}
1133238104Sdes
1134238104Sdesint
1135238104Sdeslinux_socketcall(struct thread *td, struct linux_socketcall_args *args)
1136238104Sdes{
1137238104Sdes	void *arg = (void *)args->args;
1138238104Sdes
1139238104Sdes	switch (args->what) {
1140238104Sdes	case LINUX_SOCKET:
1141238104Sdes		return (linux_socket(td, arg));
1142238104Sdes	case LINUX_BIND:
1143238104Sdes		return (linux_bind(td, arg));
1144238104Sdes	case LINUX_CONNECT:
1145238104Sdes		return (linux_connect(td, arg));
1146238104Sdes	case LINUX_LISTEN:
1147238104Sdes		return (linux_listen(td, arg));
1148238104Sdes	case LINUX_ACCEPT:
1149238104Sdes		return (linux_accept(td, arg));
1150238104Sdes	case LINUX_GETSOCKNAME:
1151238104Sdes		return (linux_getsockname(td, arg));
1152238104Sdes	case LINUX_GETPEERNAME:
1153238104Sdes		return (linux_getpeername(td, arg));
1154238104Sdes	case LINUX_SOCKETPAIR:
1155238104Sdes		return (linux_socketpair(td, arg));
1156238104Sdes	case LINUX_SEND:
1157238104Sdes		return (linux_send(td, arg));
1158238104Sdes	case LINUX_RECV:
1159238104Sdes		return (linux_recv(td, arg));
1160238104Sdes	case LINUX_SENDTO:
1161238104Sdes		return (linux_sendto(td, arg));
1162238104Sdes	case LINUX_RECVFROM:
1163238104Sdes		return (linux_recvfrom(td, arg));
1164238104Sdes	case LINUX_SHUTDOWN:
1165238104Sdes		return (linux_shutdown(td, arg));
1166238104Sdes	case LINUX_SETSOCKOPT:
1167238104Sdes		return (linux_setsockopt(td, arg));
1168238104Sdes	case LINUX_GETSOCKOPT:
1169238104Sdes		return (linux_getsockopt(td, arg));
1170238104Sdes	case LINUX_SENDMSG:
1171238104Sdes		return (linux_sendmsg(td, arg));
1172246854Sdes	case LINUX_RECVMSG:
1173246854Sdes		return (linux_recvmsg(td, arg));
1174238104Sdes	}
1175246854Sdes
1176238104Sdes	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
1177246854Sdes	return (ENOSYS);
1178246854Sdes}
1179238104Sdes#endif	/*!__alpha__*/
1180246854Sdes