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