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