linux_socket.c revision 65108
1/*-
2 * Copyright (c) 1995 S�ren Schmidt
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software withough specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/compat/linux/linux_socket.c 65108 2000-08-26 05:12:16Z marcel $
29 */
30
31/* XXX we use functions that might not exist. */
32#include "opt_compat.h"
33
34#ifndef COMPAT_43
35#error "Unable to compile Linux-emulator due to missing COMPAT_43 option!"
36#endif
37
38#include <sys/param.h>
39#include <sys/proc.h>
40#include <sys/systm.h>
41#include <sys/sysproto.h>
42#include <sys/fcntl.h>
43#include <sys/socket.h>
44#include <sys/uio.h>
45
46#include <netinet/in.h>
47#include <netinet/in_systm.h>
48#include <netinet/ip.h>
49
50#include <machine/../linux/linux.h>
51#include <machine/../linux/linux_proto.h>
52#include <compat/linux/linux_util.h>
53
54static int
55linux_to_bsd_domain(int domain)
56{
57
58	switch (domain) {
59	case LINUX_AF_UNSPEC:
60		return (AF_UNSPEC);
61	case LINUX_AF_UNIX:
62		return (AF_LOCAL);
63	case LINUX_AF_INET:
64		return (AF_INET);
65	case LINUX_AF_AX25:
66		return (AF_CCITT);
67	case LINUX_AF_IPX:
68		return (AF_IPX);
69	case LINUX_AF_APPLETALK:
70		return (AF_APPLETALK);
71	}
72	return (-1);
73}
74
75static int
76linux_to_bsd_sockopt_level(int level)
77{
78
79	switch (level) {
80	case LINUX_SOL_SOCKET:
81		return (SOL_SOCKET);
82	}
83	return (level);
84}
85
86static int
87linux_to_bsd_ip_sockopt(int opt)
88{
89
90	switch (opt) {
91	case LINUX_IP_TOS:
92		return (IP_TOS);
93	case LINUX_IP_TTL:
94		return (IP_TTL);
95	case LINUX_IP_OPTIONS:
96		return (IP_OPTIONS);
97	case LINUX_IP_MULTICAST_IF:
98		return (IP_MULTICAST_IF);
99	case LINUX_IP_MULTICAST_TTL:
100		return (IP_MULTICAST_TTL);
101	case LINUX_IP_MULTICAST_LOOP:
102		return (IP_MULTICAST_LOOP);
103	case LINUX_IP_ADD_MEMBERSHIP:
104		return (IP_ADD_MEMBERSHIP);
105	case LINUX_IP_DROP_MEMBERSHIP:
106		return (IP_DROP_MEMBERSHIP);
107	case LINUX_IP_HDRINCL:
108		return (IP_HDRINCL);
109	}
110	return (-1);
111}
112
113static int
114linux_to_bsd_so_sockopt(int opt)
115{
116
117	switch (opt) {
118	case LINUX_SO_DEBUG:
119		return (SO_DEBUG);
120	case LINUX_SO_REUSEADDR:
121		return (SO_REUSEADDR);
122	case LINUX_SO_TYPE:
123		return (SO_TYPE);
124	case LINUX_SO_ERROR:
125		return (SO_ERROR);
126	case LINUX_SO_DONTROUTE:
127		return (SO_DONTROUTE);
128	case LINUX_SO_BROADCAST:
129		return (SO_BROADCAST);
130	case LINUX_SO_SNDBUF:
131		return (SO_SNDBUF);
132	case LINUX_SO_RCVBUF:
133		return (SO_RCVBUF);
134	case LINUX_SO_KEEPALIVE:
135		return (SO_KEEPALIVE);
136	case LINUX_SO_OOBINLINE:
137		return (SO_OOBINLINE);
138	case LINUX_SO_LINGER:
139		return (SO_LINGER);
140	}
141	return (-1);
142}
143
144/* Return 0 if IP_HDRINCL is set for the given socket. */
145static int
146linux_check_hdrincl(struct proc *p, int s)
147{
148	struct getsockopt_args /* {
149		int s;
150		int level;
151		int name;
152		caddr_t val;
153		int *avalsize;
154	} */ bsd_args;
155	int error;
156	caddr_t sg, val, valsize;
157	int size_val = sizeof val;
158	int optval;
159
160	sg = stackgap_init();
161	val = stackgap_alloc(&sg, sizeof(int));
162	valsize = stackgap_alloc(&sg, sizeof(int));
163
164	if ((error = copyout(&size_val, valsize, sizeof(size_val))))
165		return (error);
166
167	bsd_args.s = s;
168	bsd_args.level = IPPROTO_IP;
169	bsd_args.name = IP_HDRINCL;
170	bsd_args.val = val;
171	bsd_args.avalsize = (int *)valsize;
172	if ((error = getsockopt(p, &bsd_args)))
173		return (error);
174
175	if ((error = copyin(val, &optval, sizeof(optval))))
176		return (error);
177
178	return (optval == 0);
179}
180
181/*
182 * Updated sendto() when IP_HDRINCL is set:
183 * tweak endian-dependent fields in the IP packet.
184 */
185static int
186linux_sendto_hdrincl(struct proc *p, struct sendto_args *bsd_args)
187{
188/*
189 * linux_ip_copysize defines how many bytes we should copy
190 * from the beginning of the IP packet before we customize it for BSD.
191 * It should include all the fields we modify (ip_len and ip_off)
192 * and be as small as possible to minimize copying overhead.
193 */
194#define linux_ip_copysize	8
195
196	caddr_t sg;
197	struct ip *packet;
198	struct msghdr *msg;
199	struct iovec *iov;
200
201	int error;
202	struct  sendmsg_args /* {
203		int s;
204		caddr_t msg;
205		int flags;
206	} */ sendmsg_args;
207
208	/* Check the packet isn't too small before we mess with it */
209	if (bsd_args->len < linux_ip_copysize)
210		return (EINVAL);
211
212	/*
213	 * Tweaking the user buffer in place would be bad manners.
214	 * We create a corrected IP header with just the needed length,
215	 * then use an iovec to glue it to the rest of the user packet
216	 * when calling sendmsg().
217	 */
218	sg = stackgap_init();
219	packet = (struct ip *)stackgap_alloc(&sg, linux_ip_copysize);
220	msg = (struct msghdr *)stackgap_alloc(&sg, sizeof(*msg));
221	iov = (struct iovec *)stackgap_alloc(&sg, sizeof(*iov)*2);
222
223	/* Make a copy of the beginning of the packet to be sent */
224	if ((error = copyin(bsd_args->buf, packet, linux_ip_copysize)))
225		return (error);
226
227	/* Convert fields from Linux to BSD raw IP socket format */
228	packet->ip_len = bsd_args->len;
229	packet->ip_off = ntohs(packet->ip_off);
230
231	/* Prepare the msghdr and iovec structures describing the new packet */
232	msg->msg_name = bsd_args->to;
233	msg->msg_namelen = bsd_args->tolen;
234	msg->msg_iov = iov;
235	msg->msg_iovlen = 2;
236	msg->msg_control = NULL;
237	msg->msg_controllen = 0;
238	msg->msg_flags = 0;
239	iov[0].iov_base = (char *)packet;
240	iov[0].iov_len = linux_ip_copysize;
241	iov[1].iov_base = (char *)(bsd_args->buf) + linux_ip_copysize;
242	iov[1].iov_len = bsd_args->len - linux_ip_copysize;
243
244	sendmsg_args.s = bsd_args->s;
245	sendmsg_args.msg = (caddr_t)msg;
246	sendmsg_args.flags = bsd_args->flags;
247	return (sendmsg(p, &sendmsg_args));
248}
249
250struct linux_socket_args {
251	int domain;
252	int type;
253	int protocol;
254};
255
256static int
257linux_socket(struct proc *p, struct linux_socket_args *args)
258{
259	struct linux_socket_args linux_args;
260	struct socket_args /* {
261		int domain;
262		int type;
263		int protocol;
264	} */ bsd_args;
265	int error;
266	int retval_socket;
267
268	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
269		return (error);
270
271	bsd_args.protocol = linux_args.protocol;
272	bsd_args.type = linux_args.type;
273	bsd_args.domain = linux_to_bsd_domain(linux_args.domain);
274	if (bsd_args.domain == -1)
275		return (EINVAL);
276
277	retval_socket = socket(p, &bsd_args);
278	if (bsd_args.type == SOCK_RAW
279	    && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0)
280	    && bsd_args.domain == AF_INET
281	    && retval_socket >= 0) {
282		/* It's a raw IP socket: set the IP_HDRINCL option. */
283		struct setsockopt_args /* {
284			int s;
285			int level;
286			int name;
287			caddr_t val;
288			int valsize;
289		} */ bsd_setsockopt_args;
290		caddr_t sg;
291		int *hdrincl;
292
293		sg = stackgap_init();
294		hdrincl = (int *)stackgap_alloc(&sg, sizeof(*hdrincl));
295		*hdrincl = 1;
296		bsd_setsockopt_args.s = p->p_retval[0];
297		bsd_setsockopt_args.level = IPPROTO_IP;
298		bsd_setsockopt_args.name = IP_HDRINCL;
299		bsd_setsockopt_args.val = (caddr_t)hdrincl;
300		bsd_setsockopt_args.valsize = sizeof(*hdrincl);
301		/* We ignore any error returned by setsockopt() */
302		setsockopt(p, &bsd_setsockopt_args);
303		/* Copy back the return value from socket() */
304		p->p_retval[0] = bsd_setsockopt_args.s;
305	}
306
307	return (retval_socket);
308}
309
310struct linux_bind_args {
311	int s;
312	struct sockaddr *name;
313	int namelen;
314};
315
316static int
317linux_bind(struct proc *p, struct linux_bind_args *args)
318{
319	struct linux_bind_args linux_args;
320	struct bind_args /* {
321		int s;
322		caddr_t name;
323		int namelen;
324	} */ bsd_args;
325	int error;
326
327	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
328		return (error);
329
330	bsd_args.s = linux_args.s;
331	bsd_args.name = (caddr_t)linux_args.name;
332	bsd_args.namelen = linux_args.namelen;
333	return (bind(p, &bsd_args));
334}
335
336struct linux_connect_args {
337	int s;
338	struct sockaddr * name;
339	int namelen;
340};
341
342static int
343linux_connect(struct proc *p, struct linux_connect_args *args)
344{
345	struct linux_connect_args linux_args;
346	struct connect_args /* {
347		int s;
348		caddr_t name;
349		int namelen;
350	} */ bsd_args;
351	int error;
352
353	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
354		return (error);
355
356	bsd_args.s = linux_args.s;
357	bsd_args.name = (caddr_t)linux_args.name;
358	bsd_args.namelen = linux_args.namelen;
359	error = connect(p, &bsd_args);
360	if (error == EISCONN) {
361		/*
362		 * Linux doesn't return EISCONN the first time it occurs,
363		 * when on a non-blocking socket. Instead it returns the
364		 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
365		 */
366		struct fcntl_args /* {
367			int fd;
368			int cmd;
369			int arg;
370		} */ bsd_fcntl_args;
371		struct getsockopt_args /* {
372			int s;
373			int level;
374			int name;
375			caddr_t val;
376			int *avalsize;
377		} */ bsd_getsockopt_args;
378		void *status, *statusl;
379		int stat, statl = sizeof stat;
380		caddr_t sg;
381
382		/* Check for non-blocking */
383		bsd_fcntl_args.fd = linux_args.s;
384		bsd_fcntl_args.cmd = F_GETFL;
385		bsd_fcntl_args.arg = 0;
386		error = fcntl(p, &bsd_fcntl_args);
387		if (error == 0 && (p->p_retval[0] & O_NONBLOCK)) {
388			sg = stackgap_init();
389			status = stackgap_alloc(&sg, sizeof stat);
390			statusl = stackgap_alloc(&sg, sizeof statusl);
391
392			if ((error = copyout(&statl, statusl, sizeof statl)))
393				return (error);
394
395			bsd_getsockopt_args.s = linux_args.s;
396			bsd_getsockopt_args.level = SOL_SOCKET;
397			bsd_getsockopt_args.name = SO_ERROR;
398			bsd_getsockopt_args.val = status;
399			bsd_getsockopt_args.avalsize = statusl;
400
401			error = getsockopt(p, &bsd_getsockopt_args);
402			if (error)
403				return (error);
404
405			if ((error = copyin(status, &stat, sizeof stat)))
406				return (error);
407
408			p->p_retval[0] = stat;
409			return (0);
410		}
411	}
412
413	return (error);
414}
415
416struct linux_listen_args {
417	int s;
418	int backlog;
419};
420
421static int
422linux_listen(struct proc *p, struct linux_listen_args *args)
423{
424	struct linux_listen_args linux_args;
425	struct listen_args /* {
426		int s;
427		int backlog;
428	} */ bsd_args;
429	int error;
430
431	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
432		return (error);
433
434	bsd_args.s = linux_args.s;
435	bsd_args.backlog = linux_args.backlog;
436	return (listen(p, &bsd_args));
437}
438
439struct linux_accept_args {
440	int s;
441	struct sockaddr *addr;
442	int *namelen;
443};
444
445static int
446linux_accept(struct proc *p, struct linux_accept_args *args)
447{
448	struct linux_accept_args linux_args;
449	struct accept_args /* {
450		int s;
451		caddr_t name;
452		int *anamelen;
453	} */ bsd_args;
454	struct fcntl_args /* {
455		int fd;
456		int cmd;
457		long arg;
458	} */ f_args;
459	int error;
460
461	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
462		return (error);
463
464	bsd_args.s = linux_args.s;
465	bsd_args.name = (caddr_t)linux_args.addr;
466	bsd_args.anamelen = linux_args.namelen;
467	error = oaccept(p, &bsd_args);
468	if (error)
469		return (error);
470
471	/*
472	 * linux appears not to copy flags from the parent socket to the
473	 * accepted one, so we must clear the flags in the new descriptor.
474	 * Ignore any errors, because we already have an open fd.
475	 */
476	f_args.fd = p->p_retval[0];
477	f_args.cmd = F_SETFL;
478	f_args.arg = 0;
479	(void)fcntl(p, &f_args);
480	p->p_retval[0] = f_args.fd;
481	return (0);
482}
483
484struct linux_getsockname_args {
485	int s;
486	struct sockaddr *addr;
487	int *namelen;
488};
489
490static int
491linux_getsockname(struct proc *p, struct linux_getsockname_args *args)
492{
493	struct linux_getsockname_args linux_args;
494	struct getsockname_args /* {
495		int fdes;
496		caddr_t asa;
497		int *alen;
498	} */ bsd_args;
499	int error;
500
501	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
502		return (error);
503
504	bsd_args.fdes = linux_args.s;
505	bsd_args.asa = (caddr_t) linux_args.addr;
506	bsd_args.alen = linux_args.namelen;
507	return (ogetsockname(p, &bsd_args));
508}
509
510struct linux_getpeername_args {
511	int s;
512	struct sockaddr *addr;
513	int *namelen;
514};
515
516static int
517linux_getpeername(struct proc *p, struct linux_getpeername_args *args)
518{
519	struct linux_getpeername_args linux_args;
520	struct ogetpeername_args /* {
521		int fdes;
522		caddr_t asa;
523		int *alen;
524	} */ bsd_args;
525	int error;
526
527	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
528		return (error);
529
530	bsd_args.fdes = linux_args.s;
531	bsd_args.asa = (caddr_t) linux_args.addr;
532	bsd_args.alen = linux_args.namelen;
533	return (ogetpeername(p, &bsd_args));
534}
535
536struct linux_socketpair_args {
537	int domain;
538	int type;
539	int protocol;
540	int *rsv;
541};
542
543static int
544linux_socketpair(struct proc *p, struct linux_socketpair_args *args)
545{
546	struct linux_socketpair_args linux_args;
547	struct socketpair_args /* {
548		int domain;
549		int type;
550		int protocol;
551		int *rsv;
552	} */ bsd_args;
553	int error;
554
555	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
556		return (error);
557
558	bsd_args.domain = linux_to_bsd_domain(linux_args.domain);
559	if (bsd_args.domain == -1)
560		return (EINVAL);
561
562	bsd_args.type = linux_args.type;
563	bsd_args.protocol = linux_args.protocol;
564	bsd_args.rsv = linux_args.rsv;
565	return (socketpair(p, &bsd_args));
566}
567
568struct linux_send_args {
569	int s;
570	void *msg;
571	int len;
572	int flags;
573};
574
575static int
576linux_send(struct proc *p, struct linux_send_args *args)
577{
578	struct linux_send_args linux_args;
579	struct osend_args /* {
580		int s;
581		caddr_t buf;
582		int len;
583		int flags;
584	} */ bsd_args;
585	int error;
586
587	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
588		return (error);
589
590	bsd_args.s = linux_args.s;
591	bsd_args.buf = linux_args.msg;
592	bsd_args.len = linux_args.len;
593	bsd_args.flags = linux_args.flags;
594	return (osend(p, &bsd_args));
595}
596
597struct linux_recv_args {
598	int s;
599	void *msg;
600	int len;
601	int flags;
602};
603
604static int
605linux_recv(struct proc *p, struct linux_recv_args *args)
606{
607	struct linux_recv_args linux_args;
608	struct orecv_args /* {
609		int s;
610		caddr_t buf;
611		int len;
612		int flags;
613	} */ bsd_args;
614	int error;
615
616	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
617		return (error);
618
619	bsd_args.s = linux_args.s;
620	bsd_args.buf = linux_args.msg;
621	bsd_args.len = linux_args.len;
622	bsd_args.flags = linux_args.flags;
623	return (orecv(p, &bsd_args));
624}
625
626struct linux_sendto_args {
627	int s;
628	void *msg;
629	int len;
630	int flags;
631	caddr_t to;
632	int tolen;
633};
634
635static int
636linux_sendto(struct proc *p, struct linux_sendto_args *args)
637{
638	struct linux_sendto_args linux_args;
639	struct sendto_args /* {
640		int s;
641		caddr_t buf;
642		size_t len;
643		int flags;
644		caddr_t to;
645		int tolen;
646	} */ bsd_args;
647	int error;
648
649	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
650		return (error);
651
652	bsd_args.s = linux_args.s;
653	bsd_args.buf = linux_args.msg;
654	bsd_args.len = linux_args.len;
655	bsd_args.flags = linux_args.flags;
656	bsd_args.to = linux_args.to;
657	bsd_args.tolen = linux_args.tolen;
658
659	if (linux_check_hdrincl(p, linux_args.s) == 0)
660		/* IP_HDRINCL set, tweak the packet before sending */
661		return (linux_sendto_hdrincl(p, &bsd_args));
662
663	return (sendto(p, &bsd_args));
664}
665
666struct linux_recvfrom_args {
667	int s;
668	void *buf;
669	int len;
670	int flags;
671	caddr_t from;
672	int *fromlen;
673};
674
675static int
676linux_recvfrom(struct proc *p, struct linux_recvfrom_args *args)
677{
678	struct linux_recvfrom_args linux_args;
679	struct recvfrom_args /* {
680		int s;
681		caddr_t buf;
682		size_t len;
683		int flags;
684		caddr_t from;
685		int *fromlenaddr;
686	} */ bsd_args;
687	int error;
688
689	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
690		return (error);
691
692	bsd_args.s = linux_args.s;
693	bsd_args.buf = linux_args.buf;
694	bsd_args.len = linux_args.len;
695	bsd_args.flags = linux_args.flags;
696	bsd_args.from = linux_args.from;
697	bsd_args.fromlenaddr = linux_args.fromlen;
698	return (orecvfrom(p, &bsd_args));
699}
700
701struct linux_shutdown_args {
702	int s;
703	int how;
704};
705
706static int
707linux_shutdown(struct proc *p, struct linux_shutdown_args *args)
708{
709	struct linux_shutdown_args linux_args;
710	struct shutdown_args /* {
711		int s;
712		int how;
713	} */ bsd_args;
714	int error;
715
716	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
717		return (error);
718
719	bsd_args.s = linux_args.s;
720	bsd_args.how = linux_args.how;
721	return (shutdown(p, &bsd_args));
722}
723
724struct linux_setsockopt_args {
725	int s;
726	int level;
727	int optname;
728	void *optval;
729	int optlen;
730};
731
732static int
733linux_setsockopt(struct proc *p, struct linux_setsockopt_args *args)
734{
735	struct linux_setsockopt_args linux_args;
736	struct setsockopt_args /* {
737		int s;
738		int level;
739		int name;
740		caddr_t val;
741		int valsize;
742	} */ bsd_args;
743	int error, name;
744
745	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
746		return (error);
747
748	bsd_args.s = linux_args.s;
749	bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level);
750	switch (bsd_args.level) {
751	case SOL_SOCKET:
752		name = linux_to_bsd_so_sockopt(linux_args.optname);
753		break;
754	case IPPROTO_IP:
755		name = linux_to_bsd_ip_sockopt(linux_args.optname);
756		break;
757	case IPPROTO_TCP:
758		/* Linux TCP option values match BSD's */
759		name = linux_args.optname;
760		break;
761	default:
762		name = -1;
763		break;
764	}
765	if (name == -1)
766		return (EINVAL);
767
768	bsd_args.name = name;
769	bsd_args.val = linux_args.optval;
770	bsd_args.valsize = linux_args.optlen;
771	return (setsockopt(p, &bsd_args));
772}
773
774struct linux_getsockopt_args {
775	int s;
776	int level;
777	int optname;
778	void *optval;
779	int *optlen;
780};
781
782static int
783linux_getsockopt(struct proc *p, struct linux_getsockopt_args *args)
784{
785	struct linux_getsockopt_args linux_args;
786	struct getsockopt_args /* {
787		int s;
788		int level;
789		int name;
790		caddr_t val;
791		int *avalsize;
792	} */ bsd_args;
793	int error, name;
794
795	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
796		return (error);
797
798	bsd_args.s = linux_args.s;
799	bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level);
800	switch (bsd_args.level) {
801	case SOL_SOCKET:
802		name = linux_to_bsd_so_sockopt(linux_args.optname);
803		break;
804	case IPPROTO_IP:
805		name = linux_to_bsd_ip_sockopt(linux_args.optname);
806		break;
807	case IPPROTO_TCP:
808		/* Linux TCP option values match BSD's */
809		name = linux_args.optname;
810		break;
811	default:
812		name = -1;
813		break;
814	}
815	if (name == -1)
816		return (EINVAL);
817
818	bsd_args.name = name;
819	bsd_args.val = linux_args.optval;
820	bsd_args.avalsize = linux_args.optlen;
821	return (getsockopt(p, &bsd_args));
822}
823
824int
825linux_socketcall(struct proc *p, struct linux_socketcall_args *args)
826{
827
828	switch (args->what) {
829	case LINUX_SOCKET:
830		return (linux_socket(p, args->args));
831	case LINUX_BIND:
832		return (linux_bind(p, args->args));
833	case LINUX_CONNECT:
834		return (linux_connect(p, args->args));
835	case LINUX_LISTEN:
836		return (linux_listen(p, args->args));
837	case LINUX_ACCEPT:
838		return (linux_accept(p, args->args));
839	case LINUX_GETSOCKNAME:
840		return (linux_getsockname(p, args->args));
841	case LINUX_GETPEERNAME:
842		return (linux_getpeername(p, args->args));
843	case LINUX_SOCKETPAIR:
844		return (linux_socketpair(p, args->args));
845	case LINUX_SEND:
846		return (linux_send(p, args->args));
847	case LINUX_RECV:
848		return (linux_recv(p, args->args));
849	case LINUX_SENDTO:
850		return (linux_sendto(p, args->args));
851	case LINUX_RECVFROM:
852		return (linux_recvfrom(p, args->args));
853	case LINUX_SHUTDOWN:
854		return (linux_shutdown(p, args->args));
855	case LINUX_SETSOCKOPT:
856		return (linux_setsockopt(p, args->args));
857	case LINUX_GETSOCKOPT:
858		return (linux_getsockopt(p, args->args));
859	case LINUX_SENDMSG:
860		do {
861			int error;
862			int level;
863			caddr_t control;
864			struct {
865				int s;
866				const struct msghdr *msg;
867				int flags;
868			} *uap = args->args;
869
870			error = copyin(&uap->msg->msg_control, &control,
871			    sizeof(caddr_t));
872			if (error)
873				return (error);
874
875			if (control == NULL)
876				goto done;
877
878			error = copyin(&((struct cmsghdr*)control)->cmsg_level,
879			    &level, sizeof(int));
880			if (error)
881				return (error);
882
883			if (level == 1) {
884				/*
885				 * Linux thinks that SOL_SOCKET is 1; we know
886				 * that it's really 0xffff, of course.
887				 */
888				level = SOL_SOCKET;
889				error = copyout(&level,
890				    &((struct cmsghdr *)control)->cmsg_level,
891				    sizeof(int));
892				if (error)
893					return (error);
894			}
895		done:
896			return (sendmsg(p, args->args));
897		} while (0);
898	case LINUX_RECVMSG:
899		return (recvmsg(p, args->args));
900	}
901
902	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
903	return (ENOSYS);
904}
905