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