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