diff options
| author | jtk <jtk@NetBSD.org> | 1999-07-17 22:03:55 +0000 |
|---|---|---|
| committer | jtk <jtk@NetBSD.org> | 1999-07-17 22:03:55 +0000 |
| commit | 92bf84ec683bd90b63f9b5b7691726c7cc5e9f9a (patch) | |
| tree | d1b52e8f1228c72faf86e581d42e0eac519b5cd9 /sys/compat/linux/common/linux_socket.c | |
| parent | c13299119af5614ddf9b399e9d58abeae5aa5a4b (diff) | |
implement emulation of SIOCGIFHWADDR, PR 7997
Diffstat (limited to 'sys/compat/linux/common/linux_socket.c')
| -rw-r--r-- | sys/compat/linux/common/linux_socket.c | 118 |
1 files changed, 117 insertions, 1 deletions
diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index 2c053b0e000..ac644a6cabe 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_socket.c,v 1.20 1999/05/05 20:01:03 thorpej Exp $ */ +/* $NetBSD: linux_socket.c,v 1.21 1999/07/17 22:03:55 jtk Exp $ */ /*- * Copyright (c) 1995, 1998 The NetBSD Foundation, Inc. @@ -54,6 +54,8 @@ #include <sys/socket.h> #include <sys/socketvar.h> #include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <sys/mount.h> @@ -74,6 +76,10 @@ #include <compat/linux/linux_syscallargs.h> +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + /* * The calls in this file are entered either via the linux_socketcall() * interface or, on the Alpha, as individual syscalls. The @@ -88,6 +94,7 @@ int linux_to_bsd_so_sockopt __P((int)); int linux_to_bsd_ip_sockopt __P((int)); int linux_to_bsd_tcp_sockopt __P((int)); int linux_to_bsd_udp_sockopt __P((int)); +int linux_getifhwaddr __P((struct proc *, register_t *, u_int, void *)); /* * Convert between Linux and BSD socket domain values @@ -442,6 +449,112 @@ linux_sys_getsockopt(p, v, retval) return sys_getsockopt(p, &bga, retval); } +#define IF_NAME_LEN 16 + +int +linux_getifhwaddr(p, retval, fd, data) + struct proc *p; + register_t *retval; + u_int fd; + void *data; +{ + /* Not the full structure, just enough to map what we do here */ + struct linux_ifreq { + char if_name[IF_NAME_LEN]; + struct osockaddr hwaddr; + } lreq; + struct filedesc *fdp; + struct file *fp; + struct ifaddr *ifa; + struct ifnet *ifp; + struct sockaddr_dl *sadl; + int error, found; + int index, ifnum; + + /* + * We can't emulate this ioctl by calling sys_ioctl() to run + * SIOCGIFCONF, because the user buffer is not of the right + * type to take those results. We can't use kernel buffers to + * receive the results, as the implementation of sys_ioctl() + * and ifconf() [which implements SIOCGIFCONF] use + * copyin()/copyout() which will fail on kernel addresses. + * + * So, we must duplicate code from sys_ioctl() and ifconf(). Ugh. + */ + + fdp = p->p_fd; + if (fd >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[fd]) == NULL || + (fp->f_iflags & FIF_WANTCLOSE) != 0) + return (EBADF); + + FILE_USE(fp); + if ((fp->f_flag & (FREAD | FWRITE)) == 0) { + error = EBADF; + goto out; + } + + error = copyin(data, (caddr_t)&lreq, sizeof(lreq)); + if (error) + goto out; + lreq.if_name[IF_NAME_LEN-1] = '\0'; /* just in case */ + + /* + * Only support finding addresses for "ethX". Should we + * do otherwise? XXX + */ + if (lreq.if_name[0] == 'e' && + lreq.if_name[1] == 't' && + lreq.if_name[2] == 'h') { + for (ifnum = 0, index = 3; + lreq.if_name[index] != '\0' && index < IF_NAME_LEN; + index++) { + ifnum *= 10; + ifnum += lreq.if_name[index] - '0'; + } + + error = EINVAL; /* in case we don't find one */ + for (ifp = ifnet.tqh_first, found = 0; + ifp != 0 && !found; + ifp = ifp->if_list.tqe_next) { + memcpy(lreq.if_name, ifp->if_xname, + MIN(IF_NAME_LEN, IFNAMSIZ)); + if ((ifa = ifp->if_addrlist.tqh_first) == 0) + /* no addresses on this interface */ + continue; + else + for (; ifa != 0; ifa = ifa->ifa_list.tqe_next) { + sadl = (struct sockaddr_dl *)ifa->ifa_addr; + /* only return ethernet addresses */ + /* XXX what about FDDI, etc. ? */ + if (sadl->sdl_family != AF_LINK || + sadl->sdl_type != IFT_ETHER) + continue; + if (ifnum--) + /* not the reqested iface */ + continue; + memcpy((caddr_t)&lreq.hwaddr.sa_data, + LLADDR(sadl), + MIN(sadl->sdl_alen, + sizeof(lreq.hwaddr.sa_data))); + lreq.hwaddr.sa_family = + sadl->sdl_family; + error = copyout((caddr_t)&lreq, data, + sizeof(lreq)); + found = 1; + break; + } + } + } else + /* not an "eth*" name */ + error = EINVAL; + +out: + FILE_UNUSE(fp, p); + return error; +} +#undef IF_NAME_LEN 16 + int linux_ioctl_socket(p, uap, retval) register struct proc *p; @@ -483,6 +596,9 @@ linux_ioctl_socket(p, uap, retval) case LINUX_SIOCDELMULTI: SCARG(&ia, com) = SIOCDELMULTI; break; + case LINUX_SIOCGIFHWADDR: + return linux_getifhwaddr(p, retval, SCARG(uap, fd), + SCARG(uap, data)); default: return EINVAL; } |
