1 /*	$NetBSD: linux32_socket.c,v 1.8 2008/06/27 12:38:25 njoly Exp $ */
2 
3 /*-
4  * Copyright (c) 2006 Emmanuel Dreyfus, all rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
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. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by Emmanuel Dreyfus
17  * 4. The name of the author may not be used to endorse or promote
18  *    products derived from this software without specific prior written
19  *    permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS''
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 
36 __KERNEL_RCSID(0, "$NetBSD: linux32_socket.c,v 1.8 2008/06/27 12:38:25 njoly Exp $");
37 
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/fstypes.h>
41 #include <sys/signal.h>
42 #include <sys/dirent.h>
43 #include <sys/kernel.h>
44 #include <sys/fcntl.h>
45 #include <sys/select.h>
46 #include <sys/proc.h>
47 #include <sys/ucred.h>
48 #include <sys/swap.h>
49 #include <sys/file.h>
50 #include <sys/vnode.h>
51 #include <sys/filedesc.h>
52 
53 #include <machine/types.h>
54 
55 #include <net/if.h>
56 #include <net/if_dl.h>
57 #include <net/if_types.h>
58 #include <net/route.h>
59 
60 #include <netinet/in.h>
61 #include <netinet/ip_mroute.h>
62 
63 #include <sys/syscallargs.h>
64 
65 #include <compat/netbsd32/netbsd32.h>
66 #include <compat/netbsd32/netbsd32_ioctl.h>
67 #include <compat/netbsd32/netbsd32_conv.h>
68 #include <compat/netbsd32/netbsd32_syscallargs.h>
69 
70 #include <compat/sys/socket.h>
71 #include <compat/sys/sockio.h>
72 
73 #include <compat/linux/common/linux_types.h>
74 #include <compat/linux/common/linux_types.h>
75 #include <compat/linux/common/linux_signal.h>
76 #include <compat/linux/common/linux_machdep.h>
77 #include <compat/linux/common/linux_misc.h>
78 #include <compat/linux/common/linux_oldolduname.h>
79 #include <compat/linux/common/linux_ioctl.h>
80 #include <compat/linux/common/linux_sockio.h>
81 #include <compat/linux/linux_syscallargs.h>
82 
83 #include <compat/linux32/common/linux32_types.h>
84 #include <compat/linux32/common/linux32_signal.h>
85 #include <compat/linux32/common/linux32_machdep.h>
86 #include <compat/linux32/common/linux32_sysctl.h>
87 #include <compat/linux32/common/linux32_socketcall.h>
88 #include <compat/linux32/common/linux32_sockio.h>
89 #include <compat/linux32/common/linux32_ioctl.h>
90 #include <compat/linux32/linux32_syscallargs.h>
91 
92 int linux32_getifhwaddr(struct lwp *, register_t *, u_int, void *);
93 
94 int
95 linux32_sys_socketpair(struct lwp *l, const struct linux32_sys_socketpair_args *uap, register_t *retval)
96 {
97 	/* {
98 		syscallarg(int) domain;
99 		syscallarg(int) type;
100 		syscallarg(int) protocol;
101 		syscallarg(netbsd32_intp) rsv;
102 	} */
103 	struct linux_sys_socketpair_args ua;
104 
105 	NETBSD32TO64_UAP(domain);
106 	NETBSD32TO64_UAP(type);
107 	NETBSD32TO64_UAP(protocol);
108 	NETBSD32TOP_UAP(rsv, int)
109 
110 	return linux_sys_socketpair(l, &ua, retval);
111 }
112 
113 int
114 linux32_sys_sendto(struct lwp *l, const struct linux32_sys_sendto_args *uap, register_t *retval)
115 {
116 	/* {
117 		syscallarg(int) s;
118 		syscallarg(netbsd32_voidp) msg;
119 		syscallarg(int) len;
120 		syscallarg(int) flags;
121 		syscallarg(netbsd32_osockaddrp_t) to;
122 		syscallarg(int) tolen;
123 	} */
124 	struct linux_sys_sendto_args ua;
125 
126 	NETBSD32TO64_UAP(s);
127 	NETBSD32TOP_UAP(msg, void);
128 	NETBSD32TO64_UAP(len);
129 	NETBSD32TO64_UAP(flags);
130 	NETBSD32TOP_UAP(to, struct osockaddr);
131 	NETBSD32TO64_UAP(tolen);
132 
133 	return linux_sys_sendto(l, &ua, retval);
134 }
135 
136 
137 int
138 linux32_sys_recvfrom(struct lwp *l, const struct linux32_sys_recvfrom_args *uap, register_t *retval)
139 {
140 	/* {
141 		syscallarg(int) s;
142 		syscallarg(netbsd32_voidp) buf;
143 		syscallarg(netbsd32_size_t) len;
144 		syscallarg(int) flags;
145 		syscallarg(netbsd32_osockaddrp_t) from;
146 		syscallarg(netbsd32_intp) fromlenaddr;
147 	} */
148 	struct linux_sys_recvfrom_args ua;
149 
150 	NETBSD32TO64_UAP(s);
151 	NETBSD32TOP_UAP(buf, void);
152 	NETBSD32TO64_UAP(len);
153 	NETBSD32TO64_UAP(flags);
154 	NETBSD32TOP_UAP(from, struct osockaddr);
155 	NETBSD32TOP_UAP(fromlenaddr, unsigned int);
156 
157 	return linux_sys_recvfrom(l, &ua, retval);
158 }
159 
160 int
161 linux32_sys_setsockopt(struct lwp *l, const struct linux32_sys_setsockopt_args *uap, register_t *retval)
162 {
163 	/* {
164 		syscallarg(int) s;
165 		syscallarg(int) level;
166 		syscallarg(int) optname;
167 		syscallarg(netbsd32_voidp) optval;
168 		syscallarg(int) optlen;
169 	} */
170 	struct linux_sys_setsockopt_args ua;
171 
172 	NETBSD32TO64_UAP(s);
173 	NETBSD32TO64_UAP(level);
174 	NETBSD32TO64_UAP(optname);
175 	NETBSD32TOP_UAP(optval, void);
176 	NETBSD32TO64_UAP(optlen);
177 
178 	return linux_sys_setsockopt(l, &ua, retval);
179 }
180 
181 
182 int
183 linux32_sys_getsockopt(struct lwp *l, const struct linux32_sys_getsockopt_args *uap, register_t *retval)
184 {
185 	/* {
186 		syscallarg(int) s;
187 		syscallarg(int) level;
188 		syscallarg(int) optname;
189 		syscallarg(netbsd32_voidp) optval;
190 		syscallarg(netbsd32_intp) optlen;
191 	} */
192 	struct linux_sys_getsockopt_args ua;
193 
194 	NETBSD32TO64_UAP(s);
195 	NETBSD32TO64_UAP(level);
196 	NETBSD32TO64_UAP(optname);
197 	NETBSD32TOP_UAP(optval, void);
198 	NETBSD32TOP_UAP(optlen, int);
199 
200 	return linux_sys_getsockopt(l, &ua, retval);
201 }
202 
203 int
204 linux32_sys_socket(struct lwp *l, const struct linux32_sys_socket_args *uap, register_t *retval)
205 {
206 	/* {
207 		syscallarg(int) domain;
208 		syscallarg(int) type;
209 		syscallarg(int) protocol;
210 	} */
211 	struct linux_sys_socket_args ua;
212 
213 	NETBSD32TO64_UAP(domain);
214 	NETBSD32TO64_UAP(type);
215 	NETBSD32TO64_UAP(protocol);
216 
217 	return linux_sys_socket(l, &ua, retval);
218 }
219 
220 int
221 linux32_sys_bind(struct lwp *l, const struct linux32_sys_bind_args *uap, register_t *retval)
222 {
223 	/* {
224 		syscallarg(int) s;
225 		syscallarg(netbsd32_osockaddrp_t) name;
226 		syscallarg(int) namelen;
227 	} */
228 	struct linux_sys_bind_args ua;
229 
230 	NETBSD32TO64_UAP(s);
231 	NETBSD32TOP_UAP(name, struct osockaddr)
232 	NETBSD32TO64_UAP(namelen);
233 
234 	return linux_sys_bind(l, &ua, retval);
235 }
236 
237 int
238 linux32_sys_connect(struct lwp *l, const struct linux32_sys_connect_args *uap, register_t *retval)
239 {
240 	/* {
241 		syscallarg(int) s;
242 		syscallarg(netbsd32_osockaddrp_t) name;
243 		syscallarg(int) namelen;
244 	} */
245 	struct linux_sys_connect_args ua;
246 
247 	NETBSD32TO64_UAP(s);
248 	NETBSD32TOP_UAP(name, struct osockaddr)
249 	NETBSD32TO64_UAP(namelen);
250 
251 #ifdef DEBUG_LINUX
252 	printf("linux32_sys_connect: s = %d, name = %p, namelen = %d\n",
253 		SCARG(&ua, s), SCARG(&ua, name), SCARG(&ua, namelen));
254 #endif
255 
256 	return linux_sys_connect(l, &ua, retval);
257 }
258 
259 int
260 linux32_sys_accept(struct lwp *l, const struct linux32_sys_accept_args *uap, register_t *retval)
261 {
262 	/* {
263 		syscallarg(int) s;
264 		syscallarg(netbsd32_osockaddrp_t) name;
265 		syscallarg(netbsd32_intp) anamelen;
266 	} */
267 	struct linux_sys_accept_args ua;
268 
269 	NETBSD32TO64_UAP(s);
270 	NETBSD32TOP_UAP(name, struct osockaddr)
271 	NETBSD32TOP_UAP(anamelen, int);
272 
273 	return linux_sys_accept(l, &ua, retval);
274 }
275 
276 int
277 linux32_sys_getpeername(struct lwp *l, const struct linux32_sys_getpeername_args *uap, register_t *retval)
278 {
279 	/* {
280 		syscallarg(int) fdes;
281 		syscallarg(netbsd32_sockaddrp_t) asa;
282 		syscallarg(netbsd32_intp) alen;
283 	} */
284 	struct linux_sys_getpeername_args ua;
285 
286 	NETBSD32TO64_UAP(fdes);
287 	NETBSD32TOP_UAP(asa, struct sockaddr)
288 	NETBSD32TOP_UAP(alen, int);
289 
290 	return linux_sys_getpeername(l, &ua, retval);
291 }
292 
293 int
294 linux32_sys_getsockname(struct lwp *l, const struct linux32_sys_getsockname_args *uap, register_t *retval)
295 {
296 	/* {
297 		syscallarg(int) fdec;
298 		syscallarg(netbsd32_charp) asa;
299 		syscallarg(netbsd32_intp) alen;
300 	} */
301 	struct linux_sys_getsockname_args ua;
302 
303 	NETBSD32TO64_UAP(fdec);
304 	NETBSD32TOP_UAP(asa, char)
305 	NETBSD32TOP_UAP(alen, int);
306 
307 	return linux_sys_getsockname(l, &ua, retval);
308 }
309 
310 int
311 linux32_sys_sendmsg(struct lwp *l, const struct linux32_sys_sendmsg_args *uap, register_t *retval)
312 {
313 	/* {
314 		syscallarg(int) s;
315 		syscallarg(netbsd32_msghdrp_t) msg;
316 		syscallarg(int) flags;
317 	} */
318 	struct linux_sys_sendmsg_args ua;
319 
320 	NETBSD32TO64_UAP(s);
321 	NETBSD32TOP_UAP(msg, struct msghdr);
322 	NETBSD32TO64_UAP(flags);
323 
324 	return linux_sys_sendmsg(l, &ua, retval);
325 }
326 
327 int
328 linux32_sys_recvmsg(struct lwp *l, const struct linux32_sys_recvmsg_args *uap, register_t *retval)
329 {
330 	/* {
331 		syscallarg(int) s;
332 		syscallarg(netbsd32_msghdrp_t) msg;
333 		syscallarg(int) flags;
334 	} */
335 	struct linux_sys_recvmsg_args ua;
336 
337 	NETBSD32TO64_UAP(s);
338 	NETBSD32TOP_UAP(msg, struct msghdr);
339 	NETBSD32TO64_UAP(flags);
340 
341 	return linux_sys_recvmsg(l, &ua, retval);
342 }
343 
344 int
345 linux32_sys_send(struct lwp *l, const struct linux32_sys_send_args *uap, register_t *retval)
346 {
347 	/* {
348 		syscallarg(int) s;
349 		syscallarg(netbsd32_voidp) buf;
350 		syscallarg(int) len;
351 		syscallarg(int) flags;
352 	} */
353 	struct sys_sendto_args ua;
354 
355 	NETBSD32TO64_UAP(s);
356 	NETBSD32TOP_UAP(buf, void);
357 	NETBSD32TO64_UAP(len);
358 	NETBSD32TO64_UAP(flags);
359 	SCARG(&ua, to) = NULL;
360 	SCARG(&ua, tolen) = 0;
361 
362 	return sys_sendto(l, &ua, retval);
363 }
364 
365 int
366 linux32_sys_recv(struct lwp *l, const struct linux32_sys_recv_args *uap, register_t *retval)
367 {
368 	/* {
369 		syscallarg(int) s;
370 		syscallarg(netbsd32_voidp) buf;
371 		syscallarg(int) len;
372 		syscallarg(int) flags;
373 	} */
374 	struct sys_recvfrom_args ua;
375 
376 	NETBSD32TO64_UAP(s);
377 	NETBSD32TOP_UAP(buf, void);
378 	NETBSD32TO64_UAP(len);
379 	NETBSD32TO64_UAP(flags);
380 	SCARG(&ua, from) = NULL;
381 	SCARG(&ua, fromlenaddr) = NULL;
382 
383 	return sys_recvfrom(l, &ua, retval);
384 }
385 
386 int
387 linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd,
388     void *data)
389 {
390 	struct linux32_ifreq lreq;
391 	file_t *fp;
392 	struct ifaddr *ifa;
393 	struct ifnet *ifp;
394 	struct sockaddr_dl *sadl;
395 	int error, found;
396 	int index, ifnum;
397 
398 	/*
399 	 * We can't emulate this ioctl by calling sys_ioctl() to run
400 	 * SIOCGIFCONF, because the user buffer is not of the right
401 	 * type to take those results.  We can't use kernel buffers to
402 	 * receive the results, as the implementation of sys_ioctl()
403 	 * and ifconf() [which implements SIOCGIFCONF] use
404 	 * copyin()/copyout() which will fail on kernel addresses.
405 	 *
406 	 * So, we must duplicate code from sys_ioctl() and ifconf().  Ugh.
407 	 */
408 
409 	if ((fp = fd_getfile(fd)) == NULL)
410 		return (EBADF);
411 
412 	KERNEL_LOCK(1, NULL);
413 
414 	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
415 		error = EBADF;
416 		goto out;
417 	}
418 
419 	error = copyin(data, &lreq, sizeof(lreq));
420 	if (error)
421 		goto out;
422 	lreq.ifr_name[LINUX32_IFNAMSIZ-1] = '\0';		/* just in case */
423 
424 	/*
425 	 * Try real interface name first, then fake "ethX"
426 	 */
427 	found = 0;
428 	IFNET_FOREACH(ifp) {
429 		if (found)
430 			break;
431 		if (strcmp(lreq.ifr_name, ifp->if_xname))
432 			/* not this interface */
433 			continue;
434 		found=1;
435 		if (IFADDR_EMPTY(ifp)) {
436 			error = ENODEV;
437 			goto out;
438 		}
439 		IFADDR_FOREACH(ifa, ifp) {
440 			sadl = satosdl(ifa->ifa_addr);
441 			/* only return ethernet addresses */
442 			/* XXX what about FDDI, etc. ? */
443 			if (sadl->sdl_family != AF_LINK ||
444 			    sadl->sdl_type != IFT_ETHER)
445 				continue;
446 			memcpy(&lreq.ifr_hwaddr.sa_data, CLLADDR(sadl),
447 			       MIN(sadl->sdl_alen,
448 				   sizeof(lreq.ifr_hwaddr.sa_data)));
449 			lreq.ifr_hwaddr.sa_family =
450 				sadl->sdl_family;
451 			error = copyout(&lreq, data, sizeof(lreq));
452 			goto out;
453 		}
454 	}
455 
456 	if (strncmp(lreq.ifr_name, "eth", 3) == 0) {
457 		for (ifnum = 0, index = 3;
458 		     lreq.ifr_name[index] != '\0' && index < LINUX32_IFNAMSIZ;
459 		     index++) {
460 			ifnum *= 10;
461 			ifnum += lreq.ifr_name[index] - '0';
462 		}
463 
464 		error = EINVAL;			/* in case we don't find one */
465 		found = 0;
466 		IFNET_FOREACH(ifp) {
467 			if (found)
468 				break;
469 			memcpy(lreq.ifr_name, ifp->if_xname,
470 			       MIN(LINUX32_IFNAMSIZ, IFNAMSIZ));
471 			IFADDR_FOREACH(ifa, ifp) {
472 				sadl = satosdl(ifa->ifa_addr);
473 				/* only return ethernet addresses */
474 				/* XXX what about FDDI, etc. ? */
475 				if (sadl->sdl_family != AF_LINK ||
476 				    sadl->sdl_type != IFT_ETHER)
477 					continue;
478 				if (ifnum--)
479 					/* not the reqested iface */
480 					continue;
481 				memcpy(&lreq.ifr_hwaddr.sa_data,
482 				       CLLADDR(sadl),
483 				       MIN(sadl->sdl_alen,
484 					   sizeof(lreq.ifr_hwaddr.sa_data)));
485 				lreq.ifr_hwaddr.sa_family =
486 					sadl->sdl_family;
487 				error = copyout(&lreq, data, sizeof(lreq));
488 				found = 1;
489 				break;
490 			}
491 		}
492 	} else {
493 		/* unknown interface, not even an "eth*" name */
494 		error = ENODEV;
495 	}
496 
497 out:
498 	KERNEL_UNLOCK_ONE(NULL);
499 	fd_putfile(fd);
500 	return error;
501 }
502 
503 int
504 linux32_ioctl_socket(struct lwp *l, const struct linux32_sys_ioctl_args *uap, register_t *retval)
505 {
506 	/* {
507 		syscallarg(int) fd;
508 		syscallarg(u_long) com;
509 		syscallarg(void *) data;
510 	} */
511 	u_long com;
512 	int error = 0, isdev = 0, dosys = 1;
513 	struct netbsd32_ioctl_args ia;
514 	file_t *fp;
515 	struct vnode *vp;
516 	int (*ioctlf)(file_t *, u_long, void *);
517 	struct ioctl_pt pt;
518 
519 	if ((fp = fd_getfile(SCARG(uap, fd))) == NULL)
520 		return (EBADF);
521 
522 	if (fp->f_type == DTYPE_VNODE) {
523 		vp = (struct vnode *)fp->f_data;
524 		isdev = vp->v_type == VCHR;
525 	}
526 
527 	/*
528 	 * Don't try to interpret socket ioctl calls that are done
529 	 * on a device filedescriptor, just pass them through, to
530 	 * emulate Linux behaviour. Use PTIOCLINUX so that the
531 	 * device will only handle these if it's prepared to do
532 	 * so, to avoid unexpected things from happening.
533 	 */
534 	if (isdev) {
535 		dosys = 0;
536 		ioctlf = fp->f_ops->fo_ioctl;
537 		pt.com = SCARG(uap, com);
538 		pt.data = (void *)NETBSD32PTR64(SCARG(uap, data));
539 		error = ioctlf(fp, PTIOCLINUX, &pt);
540 		/*
541 		 * XXX hack: if the function returns EJUSTRETURN,
542 		 * it has stuffed a sysctl return value in pt.data.
543 		 */
544 		if (error == EJUSTRETURN) {
545 			retval[0] = (register_t)pt.data;
546 			error = 0;
547 		}
548 		goto out;
549 	}
550 
551 	com = SCARG(uap, com);
552 	retval[0] = 0;
553 
554 	switch (com) {
555 	case LINUX_SIOCGIFCONF:
556 		SCARG(&ia, com) = OOSIOCGIFCONF32;
557 		break;
558 	case LINUX_SIOCGIFFLAGS:
559 		SCARG(&ia, com) = OSIOCGIFFLAGS;
560 		break;
561 	case LINUX_SIOCSIFFLAGS:
562 		SCARG(&ia, com) = OSIOCSIFFLAGS;
563 		break;
564 	case LINUX_SIOCGIFADDR:
565 		SCARG(&ia, com) = OOSIOCGIFADDR;
566 		break;
567 	case LINUX_SIOCGIFDSTADDR:
568 		SCARG(&ia, com) = OOSIOCGIFDSTADDR;
569 		break;
570 	case LINUX_SIOCGIFBRDADDR:
571 		SCARG(&ia, com) = OOSIOCGIFBRDADDR;
572 		break;
573 	case LINUX_SIOCGIFNETMASK:
574 		SCARG(&ia, com) = OOSIOCGIFNETMASK;
575 		break;
576 	case LINUX_SIOCADDMULTI:
577 		SCARG(&ia, com) = OSIOCADDMULTI;
578 		break;
579 	case LINUX_SIOCDELMULTI:
580 		SCARG(&ia, com) = OSIOCDELMULTI;
581 		break;
582 	case LINUX_SIOCGIFHWADDR:
583 		error = linux32_getifhwaddr(l, retval, SCARG(uap, fd),
584 		    SCARG_P32(uap, data));
585 		dosys = 0;
586 		break;
587 	default:
588 		error = EINVAL;
589 	}
590 
591  out:
592  	fd_putfile(SCARG(uap, fd));
593 
594 	if (error == 0 && dosys) {
595 		SCARG(&ia, fd) = SCARG(uap, fd);
596 		SCARG(&ia, data) = SCARG(uap, data);
597 		error = netbsd32_ioctl(curlwp, &ia, retval);
598 	}
599 
600 	return error;
601 }
602