linux_socket.c revision 69539
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 69539 2000-12-03 01:30:31Z 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
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};
342int linux_connect(struct proc *, struct linux_connect_args *);
343#endif /* !__alpha__*/
344
345int
346linux_connect(struct proc *p, struct linux_connect_args *args)
347{
348	struct linux_connect_args linux_args;
349	struct connect_args /* {
350		int s;
351		caddr_t name;
352		int namelen;
353	} */ bsd_args;
354	int error;
355
356#ifdef __alpha__
357	bcopy(args, &linux_args, sizeof(linux_args));
358#else
359	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
360		return (error);
361#endif /* __alpha__ */
362
363	bsd_args.s = linux_args.s;
364	bsd_args.name = (caddr_t)linux_args.name;
365	bsd_args.namelen = linux_args.namelen;
366	error = connect(p, &bsd_args);
367	if (error == EISCONN) {
368		/*
369		 * Linux doesn't return EISCONN the first time it occurs,
370		 * when on a non-blocking socket. Instead it returns the
371		 * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
372		 */
373		struct fcntl_args /* {
374			int fd;
375			int cmd;
376			int arg;
377		} */ bsd_fcntl_args;
378		struct getsockopt_args /* {
379			int s;
380			int level;
381			int name;
382			caddr_t val;
383			int *avalsize;
384		} */ bsd_getsockopt_args;
385		void *status, *statusl;
386		int stat, statl = sizeof stat;
387		caddr_t sg;
388
389		/* Check for non-blocking */
390		bsd_fcntl_args.fd = linux_args.s;
391		bsd_fcntl_args.cmd = F_GETFL;
392		bsd_fcntl_args.arg = 0;
393		error = fcntl(p, &bsd_fcntl_args);
394		if (error == 0 && (p->p_retval[0] & O_NONBLOCK)) {
395			sg = stackgap_init();
396			status = stackgap_alloc(&sg, sizeof stat);
397			statusl = stackgap_alloc(&sg, sizeof statusl);
398
399			if ((error = copyout(&statl, statusl, sizeof statl)))
400				return (error);
401
402			bsd_getsockopt_args.s = linux_args.s;
403			bsd_getsockopt_args.level = SOL_SOCKET;
404			bsd_getsockopt_args.name = SO_ERROR;
405			bsd_getsockopt_args.val = status;
406			bsd_getsockopt_args.avalsize = statusl;
407
408			error = getsockopt(p, &bsd_getsockopt_args);
409			if (error)
410				return (error);
411
412			if ((error = copyin(status, &stat, sizeof stat)))
413				return (error);
414
415			p->p_retval[0] = stat;
416			return (0);
417		}
418	}
419
420	return (error);
421}
422
423#ifndef __alpha__
424
425struct linux_listen_args {
426	int s;
427	int backlog;
428};
429
430static int
431linux_listen(struct proc *p, struct linux_listen_args *args)
432{
433	struct linux_listen_args linux_args;
434	struct listen_args /* {
435		int s;
436		int backlog;
437	} */ bsd_args;
438	int error;
439
440	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
441		return (error);
442
443	bsd_args.s = linux_args.s;
444	bsd_args.backlog = linux_args.backlog;
445	return (listen(p, &bsd_args));
446}
447
448struct linux_accept_args {
449	int s;
450	struct sockaddr *addr;
451	int *namelen;
452};
453
454static int
455linux_accept(struct proc *p, struct linux_accept_args *args)
456{
457	struct linux_accept_args linux_args;
458	struct accept_args /* {
459		int s;
460		caddr_t name;
461		int *anamelen;
462	} */ bsd_args;
463	struct fcntl_args /* {
464		int fd;
465		int cmd;
466		long arg;
467	} */ f_args;
468	int error;
469
470	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
471		return (error);
472
473	bsd_args.s = linux_args.s;
474	bsd_args.name = (caddr_t)linux_args.addr;
475	bsd_args.anamelen = linux_args.namelen;
476	error = oaccept(p, &bsd_args);
477	if (error)
478		return (error);
479
480	/*
481	 * linux appears not to copy flags from the parent socket to the
482	 * accepted one, so we must clear the flags in the new descriptor.
483	 * Ignore any errors, because we already have an open fd.
484	 */
485	f_args.fd = p->p_retval[0];
486	f_args.cmd = F_SETFL;
487	f_args.arg = 0;
488	(void)fcntl(p, &f_args);
489	p->p_retval[0] = f_args.fd;
490	return (0);
491}
492
493struct linux_getsockname_args {
494	int s;
495	struct sockaddr *addr;
496	int *namelen;
497};
498
499static int
500linux_getsockname(struct proc *p, struct linux_getsockname_args *args)
501{
502	struct linux_getsockname_args linux_args;
503	struct getsockname_args /* {
504		int fdes;
505		caddr_t asa;
506		int *alen;
507	} */ bsd_args;
508	int error;
509
510	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
511		return (error);
512
513	bsd_args.fdes = linux_args.s;
514	bsd_args.asa = (caddr_t) linux_args.addr;
515	bsd_args.alen = linux_args.namelen;
516	return (ogetsockname(p, &bsd_args));
517}
518
519struct linux_getpeername_args {
520	int s;
521	struct sockaddr *addr;
522	int *namelen;
523};
524
525static int
526linux_getpeername(struct proc *p, struct linux_getpeername_args *args)
527{
528	struct linux_getpeername_args linux_args;
529	struct ogetpeername_args /* {
530		int fdes;
531		caddr_t asa;
532		int *alen;
533	} */ bsd_args;
534	int error;
535
536	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
537		return (error);
538
539	bsd_args.fdes = linux_args.s;
540	bsd_args.asa = (caddr_t) linux_args.addr;
541	bsd_args.alen = linux_args.namelen;
542	return (ogetpeername(p, &bsd_args));
543}
544
545struct linux_socketpair_args {
546	int domain;
547	int type;
548	int protocol;
549	int *rsv;
550};
551
552static int
553linux_socketpair(struct proc *p, struct linux_socketpair_args *args)
554{
555	struct linux_socketpair_args linux_args;
556	struct socketpair_args /* {
557		int domain;
558		int type;
559		int protocol;
560		int *rsv;
561	} */ bsd_args;
562	int error;
563
564	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
565		return (error);
566
567	bsd_args.domain = linux_to_bsd_domain(linux_args.domain);
568	if (bsd_args.domain == -1)
569		return (EINVAL);
570
571	bsd_args.type = linux_args.type;
572	bsd_args.protocol = linux_args.protocol;
573	bsd_args.rsv = linux_args.rsv;
574	return (socketpair(p, &bsd_args));
575}
576
577struct linux_send_args {
578	int s;
579	void *msg;
580	int len;
581	int flags;
582};
583
584static int
585linux_send(struct proc *p, struct linux_send_args *args)
586{
587	struct linux_send_args linux_args;
588	struct osend_args /* {
589		int s;
590		caddr_t buf;
591		int len;
592		int flags;
593	} */ bsd_args;
594	int error;
595
596	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
597		return (error);
598
599	bsd_args.s = linux_args.s;
600	bsd_args.buf = linux_args.msg;
601	bsd_args.len = linux_args.len;
602	bsd_args.flags = linux_args.flags;
603	return (osend(p, &bsd_args));
604}
605
606struct linux_recv_args {
607	int s;
608	void *msg;
609	int len;
610	int flags;
611};
612
613static int
614linux_recv(struct proc *p, struct linux_recv_args *args)
615{
616	struct linux_recv_args linux_args;
617	struct orecv_args /* {
618		int s;
619		caddr_t buf;
620		int len;
621		int flags;
622	} */ bsd_args;
623	int error;
624
625	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
626		return (error);
627
628	bsd_args.s = linux_args.s;
629	bsd_args.buf = linux_args.msg;
630	bsd_args.len = linux_args.len;
631	bsd_args.flags = linux_args.flags;
632	return (orecv(p, &bsd_args));
633}
634
635struct linux_sendto_args {
636	int s;
637	void *msg;
638	int len;
639	int flags;
640	caddr_t to;
641	int tolen;
642};
643
644static int
645linux_sendto(struct proc *p, struct linux_sendto_args *args)
646{
647	struct linux_sendto_args linux_args;
648	struct sendto_args /* {
649		int s;
650		caddr_t buf;
651		size_t len;
652		int flags;
653		caddr_t to;
654		int tolen;
655	} */ bsd_args;
656	int error;
657
658	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
659		return (error);
660
661	bsd_args.s = linux_args.s;
662	bsd_args.buf = linux_args.msg;
663	bsd_args.len = linux_args.len;
664	bsd_args.flags = linux_args.flags;
665	bsd_args.to = linux_args.to;
666	bsd_args.tolen = linux_args.tolen;
667
668	if (linux_check_hdrincl(p, linux_args.s) == 0)
669		/* IP_HDRINCL set, tweak the packet before sending */
670		return (linux_sendto_hdrincl(p, &bsd_args));
671
672	return (sendto(p, &bsd_args));
673}
674
675struct linux_recvfrom_args {
676	int s;
677	void *buf;
678	int len;
679	int flags;
680	caddr_t from;
681	int *fromlen;
682};
683
684static int
685linux_recvfrom(struct proc *p, struct linux_recvfrom_args *args)
686{
687	struct linux_recvfrom_args linux_args;
688	struct recvfrom_args /* {
689		int s;
690		caddr_t buf;
691		size_t len;
692		int flags;
693		caddr_t from;
694		int *fromlenaddr;
695	} */ bsd_args;
696	int error;
697
698	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
699		return (error);
700
701	bsd_args.s = linux_args.s;
702	bsd_args.buf = linux_args.buf;
703	bsd_args.len = linux_args.len;
704	bsd_args.flags = linux_args.flags;
705	bsd_args.from = linux_args.from;
706	bsd_args.fromlenaddr = linux_args.fromlen;
707	return (orecvfrom(p, &bsd_args));
708}
709
710struct linux_shutdown_args {
711	int s;
712	int how;
713};
714
715static int
716linux_shutdown(struct proc *p, struct linux_shutdown_args *args)
717{
718	struct linux_shutdown_args linux_args;
719	struct shutdown_args /* {
720		int s;
721		int how;
722	} */ bsd_args;
723	int error;
724
725	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
726		return (error);
727
728	bsd_args.s = linux_args.s;
729	bsd_args.how = linux_args.how;
730	return (shutdown(p, &bsd_args));
731}
732
733struct linux_setsockopt_args {
734	int s;
735	int level;
736	int optname;
737	void *optval;
738	int optlen;
739};
740
741static int
742linux_setsockopt(struct proc *p, struct linux_setsockopt_args *args)
743{
744	struct linux_setsockopt_args linux_args;
745	struct setsockopt_args /* {
746		int s;
747		int level;
748		int name;
749		caddr_t val;
750		int valsize;
751	} */ bsd_args;
752	int error, name;
753
754	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
755		return (error);
756
757	bsd_args.s = linux_args.s;
758	bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level);
759	switch (bsd_args.level) {
760	case SOL_SOCKET:
761		name = linux_to_bsd_so_sockopt(linux_args.optname);
762		break;
763	case IPPROTO_IP:
764		name = linux_to_bsd_ip_sockopt(linux_args.optname);
765		break;
766	case IPPROTO_TCP:
767		/* Linux TCP option values match BSD's */
768		name = linux_args.optname;
769		break;
770	default:
771		name = -1;
772		break;
773	}
774	if (name == -1)
775		return (EINVAL);
776
777	bsd_args.name = name;
778	bsd_args.val = linux_args.optval;
779	bsd_args.valsize = linux_args.optlen;
780	return (setsockopt(p, &bsd_args));
781}
782
783struct linux_getsockopt_args {
784	int s;
785	int level;
786	int optname;
787	void *optval;
788	int *optlen;
789};
790
791static int
792linux_getsockopt(struct proc *p, struct linux_getsockopt_args *args)
793{
794	struct linux_getsockopt_args linux_args;
795	struct getsockopt_args /* {
796		int s;
797		int level;
798		int name;
799		caddr_t val;
800		int *avalsize;
801	} */ bsd_args;
802	int error, name;
803
804	if ((error = copyin(args, &linux_args, sizeof(linux_args))))
805		return (error);
806
807	bsd_args.s = linux_args.s;
808	bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level);
809	switch (bsd_args.level) {
810	case SOL_SOCKET:
811		name = linux_to_bsd_so_sockopt(linux_args.optname);
812		break;
813	case IPPROTO_IP:
814		name = linux_to_bsd_ip_sockopt(linux_args.optname);
815		break;
816	case IPPROTO_TCP:
817		/* Linux TCP option values match BSD's */
818		name = linux_args.optname;
819		break;
820	default:
821		name = -1;
822		break;
823	}
824	if (name == -1)
825		return (EINVAL);
826
827	bsd_args.name = name;
828	bsd_args.val = linux_args.optval;
829	bsd_args.avalsize = linux_args.optlen;
830	return (getsockopt(p, &bsd_args));
831}
832
833int
834linux_socketcall(struct proc *p, struct linux_socketcall_args *args)
835{
836
837	switch (args->what) {
838	case LINUX_SOCKET:
839		return (linux_socket(p, args->args));
840	case LINUX_BIND:
841		return (linux_bind(p, args->args));
842	case LINUX_CONNECT:
843		return (linux_connect(p, args->args));
844	case LINUX_LISTEN:
845		return (linux_listen(p, args->args));
846	case LINUX_ACCEPT:
847		return (linux_accept(p, args->args));
848	case LINUX_GETSOCKNAME:
849		return (linux_getsockname(p, args->args));
850	case LINUX_GETPEERNAME:
851		return (linux_getpeername(p, args->args));
852	case LINUX_SOCKETPAIR:
853		return (linux_socketpair(p, args->args));
854	case LINUX_SEND:
855		return (linux_send(p, args->args));
856	case LINUX_RECV:
857		return (linux_recv(p, args->args));
858	case LINUX_SENDTO:
859		return (linux_sendto(p, args->args));
860	case LINUX_RECVFROM:
861		return (linux_recvfrom(p, args->args));
862	case LINUX_SHUTDOWN:
863		return (linux_shutdown(p, args->args));
864	case LINUX_SETSOCKOPT:
865		return (linux_setsockopt(p, args->args));
866	case LINUX_GETSOCKOPT:
867		return (linux_getsockopt(p, args->args));
868	case LINUX_SENDMSG:
869		do {
870			int error;
871			int level;
872			caddr_t control;
873			struct {
874				int s;
875				const struct msghdr *msg;
876				int flags;
877			} *uap = args->args;
878
879			error = copyin(&uap->msg->msg_control, &control,
880			    sizeof(caddr_t));
881			if (error)
882				return (error);
883
884			if (control == NULL)
885				goto done;
886
887			error = copyin(&((struct cmsghdr*)control)->cmsg_level,
888			    &level, sizeof(int));
889			if (error)
890				return (error);
891
892			if (level == 1) {
893				/*
894				 * Linux thinks that SOL_SOCKET is 1; we know
895				 * that it's really 0xffff, of course.
896				 */
897				level = SOL_SOCKET;
898				error = copyout(&level,
899				    &((struct cmsghdr *)control)->cmsg_level,
900				    sizeof(int));
901				if (error)
902					return (error);
903			}
904		done:
905			return (sendmsg(p, args->args));
906		} while (0);
907	case LINUX_RECVMSG:
908		return (recvmsg(p, args->args));
909	}
910
911	uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
912	return (ENOSYS);
913}
914#endif	/*!__alpha__*/
915