diff --git a/sys/compat/linux/linux.c b/sys/compat/linux/linux.c index a40f110634f7..da2f641bb791 100644 --- a/sys/compat/linux/linux.c +++ b/sys/compat/linux/linux.c @@ -1,897 +1,621 @@ /*- * Copyright (c) 2015 Dmitry Chagin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_inet6.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include -#include -#include -#include -#include #include - #include #include #include #include #include #include -_Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ"); _Static_assert(sizeof(struct sockaddr) == sizeof(struct l_sockaddr), "Linux struct sockaddr size"); _Static_assert(offsetof(struct sockaddr, sa_data) == offsetof(struct l_sockaddr, sa_data), "Linux struct sockaddr layout"); -static bool use_real_ifnames = false; -SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN, - &use_real_ifnames, 0, - "Use FreeBSD interface names instead of generating ethN aliases"); - static int bsd_to_linux_sigtbl[LINUX_SIGTBLSZ] = { LINUX_SIGHUP, /* SIGHUP */ LINUX_SIGINT, /* SIGINT */ LINUX_SIGQUIT, /* SIGQUIT */ LINUX_SIGILL, /* SIGILL */ LINUX_SIGTRAP, /* SIGTRAP */ LINUX_SIGABRT, /* SIGABRT */ 0, /* SIGEMT */ LINUX_SIGFPE, /* SIGFPE */ LINUX_SIGKILL, /* SIGKILL */ LINUX_SIGBUS, /* SIGBUS */ LINUX_SIGSEGV, /* SIGSEGV */ LINUX_SIGSYS, /* SIGSYS */ LINUX_SIGPIPE, /* SIGPIPE */ LINUX_SIGALRM, /* SIGALRM */ LINUX_SIGTERM, /* SIGTERM */ LINUX_SIGURG, /* SIGURG */ LINUX_SIGSTOP, /* SIGSTOP */ LINUX_SIGTSTP, /* SIGTSTP */ LINUX_SIGCONT, /* SIGCONT */ LINUX_SIGCHLD, /* SIGCHLD */ LINUX_SIGTTIN, /* SIGTTIN */ LINUX_SIGTTOU, /* SIGTTOU */ LINUX_SIGIO, /* SIGIO */ LINUX_SIGXCPU, /* SIGXCPU */ LINUX_SIGXFSZ, /* SIGXFSZ */ LINUX_SIGVTALRM,/* SIGVTALRM */ LINUX_SIGPROF, /* SIGPROF */ LINUX_SIGWINCH, /* SIGWINCH */ 0, /* SIGINFO */ LINUX_SIGUSR1, /* SIGUSR1 */ LINUX_SIGUSR2 /* SIGUSR2 */ }; #define LINUX_SIGPWREMU (SIGRTMIN + (LINUX_SIGRTMAX - LINUX_SIGRTMIN) + 1) static int linux_to_bsd_sigtbl[LINUX_SIGTBLSZ] = { SIGHUP, /* LINUX_SIGHUP */ SIGINT, /* LINUX_SIGINT */ SIGQUIT, /* LINUX_SIGQUIT */ SIGILL, /* LINUX_SIGILL */ SIGTRAP, /* LINUX_SIGTRAP */ SIGABRT, /* LINUX_SIGABRT */ SIGBUS, /* LINUX_SIGBUS */ SIGFPE, /* LINUX_SIGFPE */ SIGKILL, /* LINUX_SIGKILL */ SIGUSR1, /* LINUX_SIGUSR1 */ SIGSEGV, /* LINUX_SIGSEGV */ SIGUSR2, /* LINUX_SIGUSR2 */ SIGPIPE, /* LINUX_SIGPIPE */ SIGALRM, /* LINUX_SIGALRM */ SIGTERM, /* LINUX_SIGTERM */ SIGBUS, /* LINUX_SIGSTKFLT */ SIGCHLD, /* LINUX_SIGCHLD */ SIGCONT, /* LINUX_SIGCONT */ SIGSTOP, /* LINUX_SIGSTOP */ SIGTSTP, /* LINUX_SIGTSTP */ SIGTTIN, /* LINUX_SIGTTIN */ SIGTTOU, /* LINUX_SIGTTOU */ SIGURG, /* LINUX_SIGURG */ SIGXCPU, /* LINUX_SIGXCPU */ SIGXFSZ, /* LINUX_SIGXFSZ */ SIGVTALRM, /* LINUX_SIGVTALARM */ SIGPROF, /* LINUX_SIGPROF */ SIGWINCH, /* LINUX_SIGWINCH */ SIGIO, /* LINUX_SIGIO */ /* * FreeBSD does not have SIGPWR signal, map Linux SIGPWR signal * to the first unused FreeBSD signal number. Since Linux supports * signals from 1 to 64 we are ok here as our SIGRTMIN = 65. */ LINUX_SIGPWREMU,/* LINUX_SIGPWR */ SIGSYS /* LINUX_SIGSYS */ }; static struct cdev *dev_shm_cdev; static struct cdevsw dev_shm_cdevsw = { .d_version = D_VERSION, .d_name = "dev_shm", }; /* * Map Linux RT signals to the FreeBSD RT signals. */ static inline int linux_to_bsd_rt_signal(int sig) { return (SIGRTMIN + sig - LINUX_SIGRTMIN); } static inline int bsd_to_linux_rt_signal(int sig) { return (sig - SIGRTMIN + LINUX_SIGRTMIN); } int linux_to_bsd_signal(int sig) { KASSERT(sig > 0 && sig <= LINUX_SIGRTMAX, ("invalid Linux signal %d\n", sig)); if (sig < LINUX_SIGRTMIN) return (linux_to_bsd_sigtbl[_SIG_IDX(sig)]); return (linux_to_bsd_rt_signal(sig)); } int bsd_to_linux_signal(int sig) { if (sig <= LINUX_SIGTBLSZ) return (bsd_to_linux_sigtbl[_SIG_IDX(sig)]); if (sig == LINUX_SIGPWREMU) return (LINUX_SIGPWR); return (bsd_to_linux_rt_signal(sig)); } int linux_to_bsd_sigaltstack(int lsa) { int bsa = 0; if (lsa & LINUX_SS_DISABLE) bsa |= SS_DISABLE; /* * Linux ignores SS_ONSTACK flag for ss * parameter while FreeBSD prohibits it. */ return (bsa); } int bsd_to_linux_sigaltstack(int bsa) { int lsa = 0; if (bsa & SS_DISABLE) lsa |= LINUX_SS_DISABLE; if (bsa & SS_ONSTACK) lsa |= LINUX_SS_ONSTACK; return (lsa); } void linux_to_bsd_sigset(l_sigset_t *lss, sigset_t *bss) { int b, l; SIGEMPTYSET(*bss); for (l = 1; l <= LINUX_SIGRTMAX; l++) { if (LINUX_SIGISMEMBER(*lss, l)) { b = linux_to_bsd_signal(l); if (b) SIGADDSET(*bss, b); } } } void bsd_to_linux_sigset(sigset_t *bss, l_sigset_t *lss) { int b, l; LINUX_SIGEMPTYSET(*lss); for (b = 1; b <= SIGRTMAX; b++) { if (SIGISMEMBER(*bss, b)) { l = bsd_to_linux_signal(b); if (l) LINUX_SIGADDSET(*lss, l); } } } -/* - * Translate a FreeBSD interface name to a Linux interface name - * by interface name, and return the number of bytes copied to lxname. - */ -int -ifname_bsd_to_linux_name(const char *bsdname, char *lxname, size_t len) -{ - struct epoch_tracker et; - struct ifnet *ifp; - int ret; - - CURVNET_ASSERT_SET(); - - ret = 0; - NET_EPOCH_ENTER(et); - ifp = ifunit(bsdname); - if (ifp != NULL) - ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); - NET_EPOCH_EXIT(et); - return (ret); -} - -/* - * Translate a FreeBSD interface name to a Linux interface name - * by interface index, and return the number of bytes copied to lxname. - */ -int -ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) -{ - struct epoch_tracker et; - struct ifnet *ifp; - int ret; - - ret = 0; - CURVNET_SET(TD_TO_VNET(curthread)); - NET_EPOCH_ENTER(et); - ifp = ifnet_byindex(idx); - if (ifp != NULL) - ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); - NET_EPOCH_EXIT(et); - CURVNET_RESTORE(); - return (ret); -} - -/* - * Translate a FreeBSD interface name to a Linux interface name, - * and return the number of bytes copied to lxname, 0 if interface - * not found, -1 on error. - */ -struct ifname_bsd_to_linux_ifp_cb_s { - struct ifnet *ifp; - int ethno; - char *lxname; - size_t len; -}; - -static int -ifname_bsd_to_linux_ifp_cb(if_t ifp, void *arg) -{ - struct ifname_bsd_to_linux_ifp_cb_s *cbs = arg; - - if (ifp == cbs->ifp) - return (snprintf(cbs->lxname, cbs->len, "eth%d", cbs->ethno)); - if (IFP_IS_ETH(ifp)) - cbs->ethno++; - return (0); -} - -int -ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len) -{ - struct ifname_bsd_to_linux_ifp_cb_s arg = { - .ifp = ifp, - .ethno = 0, - .lxname = lxname, - .len = len, - }; - - NET_EPOCH_ASSERT(); - - /* - * Linux loopback interface name is lo (not lo0), - * we translate lo to lo0, loX to loX. - */ - if (IFP_IS_LOOP(ifp) && strncmp(if_name(ifp), "lo0", IFNAMSIZ) == 0) - return (strlcpy(lxname, "lo", len)); - - /* Short-circuit non ethernet interfaces. */ - if (!IFP_IS_ETH(ifp) || linux_use_real_ifname(ifp)) - return (strlcpy(lxname, if_name(ifp), len)); - - /* Determine the (relative) unit number for ethernet interfaces. */ - return (if_foreach(ifname_bsd_to_linux_ifp_cb, &arg)); -} - -/* - * Translate a Linux interface name to a FreeBSD interface name, - * and return the associated ifnet structure - * bsdname and lxname need to be least IFNAMSIZ bytes long, but - * can point to the same buffer. - */ -struct ifname_linux_to_ifp_cb_s { - bool is_lo; - bool is_eth; - int ethno; - int unit; - const char *lxname; - if_t ifp; -}; - -static int -ifname_linux_to_ifp_cb(if_t ifp, void *arg) -{ - struct ifname_linux_to_ifp_cb_s *cbs = arg; - - NET_EPOCH_ASSERT(); - - /* - * Allow Linux programs to use FreeBSD names. Don't presume - * we never have an interface named "eth", so don't make - * the test optional based on is_eth. - */ - if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) - goto out; - if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->ethno) - goto out; - if (cbs->is_lo && IFP_IS_LOOP(ifp)) - goto out; - if (IFP_IS_ETH(ifp)) - cbs->ethno++; - return (0); - -out: - cbs->ifp = ifp; - return (1); -} - -struct ifnet * -ifname_linux_to_ifp(struct thread *td, const char *lxname) -{ - struct ifname_linux_to_ifp_cb_s arg = { - .ethno = 0, - .lxname = lxname, - .ifp = NULL, - }; - int len; - char *ep; - - NET_EPOCH_ASSERT(); - - for (len = 0; len < LINUX_IFNAMSIZ; ++len) - if (!isalpha(lxname[len]) || lxname[len] == '\0') - break; - if (len == 0 || len == LINUX_IFNAMSIZ) - return (NULL); - /* - * Linux loopback interface name is lo (not lo0), - * we translate lo to lo0, loX to loX. - */ - arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); - arg.unit = (int)strtoul(lxname + len, &ep, 10); - if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && - arg.is_lo == 0) - return (NULL); - arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); - - if_foreach(ifname_linux_to_ifp_cb, &arg); - return (arg.ifp); -} - -int -ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) -{ - struct epoch_tracker et; - struct ifnet *ifp; - - CURVNET_SET(TD_TO_VNET(td)); - NET_EPOCH_ENTER(et); - ifp = ifname_linux_to_ifp(td, lxname); - if (ifp != NULL && bsdname != NULL) - strlcpy(bsdname, if_name(ifp), IFNAMSIZ); - NET_EPOCH_EXIT(et); - CURVNET_RESTORE(); - return (ifp != NULL ? 0 : EINVAL); -} - -unsigned short -linux_ifflags(struct ifnet *ifp) -{ - unsigned short flags; - - NET_EPOCH_ASSERT(); - - flags = if_getflags(ifp) | if_getdrvflags(ifp); - return (bsd_to_linux_ifflags(flags)); -} - -unsigned short -bsd_to_linux_ifflags(int fl) -{ - unsigned short flags = 0; - - if (fl & IFF_UP) - flags |= LINUX_IFF_UP; - if (fl & IFF_BROADCAST) - flags |= LINUX_IFF_BROADCAST; - if (fl & IFF_DEBUG) - flags |= LINUX_IFF_DEBUG; - if (fl & IFF_LOOPBACK) - flags |= LINUX_IFF_LOOPBACK; - if (fl & IFF_POINTOPOINT) - flags |= LINUX_IFF_POINTOPOINT; - if (fl & IFF_DRV_RUNNING) - flags |= LINUX_IFF_RUNNING; - if (fl & IFF_NOARP) - flags |= LINUX_IFF_NOARP; - if (fl & IFF_PROMISC) - flags |= LINUX_IFF_PROMISC; - if (fl & IFF_ALLMULTI) - flags |= LINUX_IFF_ALLMULTI; - if (fl & IFF_MULTICAST) - flags |= LINUX_IFF_MULTICAST; - return (flags); -} - -static u_int -linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) -{ - struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; - struct l_sockaddr *lsa = arg; - - if (count > 0) - return (0); - if (sdl->sdl_type != IFT_ETHER) - return (0); - bzero(lsa, sizeof(*lsa)); - lsa->sa_family = LINUX_ARPHRD_ETHER; - bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); - return (1); -} - -int -linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) -{ - - NET_EPOCH_ASSERT(); - - if (IFP_IS_LOOP(ifp)) { - bzero(lsa, sizeof(*lsa)); - lsa->sa_family = LINUX_ARPHRD_LOOPBACK; - return (0); - } - if (!IFP_IS_ETH(ifp)) - return (ENOENT); - if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) - return (0); - return (ENOENT); -} - sa_family_t linux_to_bsd_domain(sa_family_t domain) { switch (domain) { case LINUX_AF_UNSPEC: return (AF_UNSPEC); case LINUX_AF_UNIX: return (AF_LOCAL); case LINUX_AF_INET: return (AF_INET); case LINUX_AF_INET6: return (AF_INET6); case LINUX_AF_AX25: return (AF_CCITT); case LINUX_AF_IPX: return (AF_IPX); case LINUX_AF_APPLETALK: return (AF_APPLETALK); case LINUX_AF_NETLINK: return (AF_NETLINK); } return (AF_UNKNOWN); } sa_family_t bsd_to_linux_domain(sa_family_t domain) { switch (domain) { case AF_UNSPEC: return (LINUX_AF_UNSPEC); case AF_LOCAL: return (LINUX_AF_UNIX); case AF_INET: return (LINUX_AF_INET); case AF_INET6: return (LINUX_AF_INET6); case AF_CCITT: return (LINUX_AF_AX25); case AF_IPX: return (LINUX_AF_IPX); case AF_APPLETALK: return (LINUX_AF_APPLETALK); case AF_NETLINK: return (LINUX_AF_NETLINK); } return (AF_UNKNOWN); } /* * Based on the fact that: * 1. Native and Linux storage of struct sockaddr * and struct sockaddr_in6 are equal. * 2. On Linux sa_family is the first member of all struct sockaddr. */ int bsd_to_linux_sockaddr(const struct sockaddr *sa, struct l_sockaddr **lsa, socklen_t len) { struct l_sockaddr *kosa; sa_family_t bdom; *lsa = NULL; if (len < 2 || len > UCHAR_MAX) return (EINVAL); bdom = bsd_to_linux_domain(sa->sa_family); if (bdom == AF_UNKNOWN) return (EAFNOSUPPORT); kosa = malloc(len, M_LINUX, M_WAITOK); bcopy(sa, kosa, len); kosa->sa_family = bdom; *lsa = kosa; return (0); } /* * If sap is NULL, then osa points at already copied in linux sockaddr that * should be edited in place. Otherwise memory is allocated, sockaddr * copied in and returned in *sap. */ int linux_to_bsd_sockaddr(struct l_sockaddr *osa, struct sockaddr **sap, socklen_t *len) { struct sockaddr *sa; struct l_sockaddr *kosa; #ifdef INET6 struct sockaddr_in6 *sin6; bool oldv6size; #endif char *name; int salen, bdom, error, hdrlen, namelen; if (*len < 2 || *len > UCHAR_MAX) return (EINVAL); salen = *len; #ifdef INET6 oldv6size = false; /* * Check for old (pre-RFC2553) sockaddr_in6. We may accept it * if it's a v4-mapped address, so reserve the proper space * for it. */ if (salen == sizeof(struct sockaddr_in6) - sizeof(uint32_t)) { salen += sizeof(uint32_t); oldv6size = true; } #endif if (sap != NULL) { kosa = malloc(salen, M_SONAME, M_WAITOK); if ((error = copyin(osa, kosa, *len))) goto out; } else kosa = osa; bdom = linux_to_bsd_domain(kosa->sa_family); if (bdom == AF_UNKNOWN) { error = EAFNOSUPPORT; goto out; } #ifdef INET6 /* * Older Linux IPv6 code uses obsolete RFC2133 struct sockaddr_in6, * which lacks the scope id compared with RFC2553 one. If we detect * the situation, reject the address and write a message to system log. * * Still accept addresses for which the scope id is not used. */ if (oldv6size) { if (bdom == AF_INET6) { sin6 = (struct sockaddr_in6 *)kosa; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) || (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && !IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && !IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) && !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) { sin6->sin6_scope_id = 0; } else { linux_msg(curthread, "obsolete pre-RFC2553 sockaddr_in6 rejected"); error = EINVAL; goto out; } } else salen -= sizeof(uint32_t); } #endif if (bdom == AF_INET) { if (salen < sizeof(struct sockaddr_in)) { error = EINVAL; goto out; } salen = sizeof(struct sockaddr_in); } if (bdom == AF_LOCAL && salen > sizeof(struct sockaddr_un)) { hdrlen = offsetof(struct sockaddr_un, sun_path); name = ((struct sockaddr_un *)kosa)->sun_path; if (*name == '\0') { /* * Linux abstract namespace starts with a NULL byte. * XXX We do not support abstract namespace yet. */ namelen = strnlen(name + 1, salen - hdrlen - 1) + 1; } else namelen = strnlen(name, salen - hdrlen); salen = hdrlen + namelen; if (salen > sizeof(struct sockaddr_un)) { error = ENAMETOOLONG; goto out; } } if (bdom == AF_NETLINK) { if (salen < sizeof(struct sockaddr_nl)) { error = EINVAL; goto out; } salen = sizeof(struct sockaddr_nl); } sa = (struct sockaddr *)kosa; sa->sa_family = bdom; sa->sa_len = salen; if (sap != NULL) { *sap = sa; *len = salen; } return (0); out: if (sap != NULL) free(kosa, M_SONAME); return (error); } void linux_dev_shm_create(void) { int error; error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &dev_shm_cdev, &dev_shm_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0, "shm/.mountpoint"); if (error != 0) { printf("%s: failed to create device node, error %d\n", __func__, error); } } void linux_dev_shm_destroy(void) { destroy_dev(dev_shm_cdev); } int bsd_to_linux_bits_(int value, struct bsd_to_linux_bitmap *bitmap, size_t mapcnt, int no_value) { int bsd_mask, bsd_value, linux_mask, linux_value; int linux_ret; size_t i; bool applied; applied = false; linux_ret = 0; for (i = 0; i < mapcnt; ++i) { bsd_mask = bitmap[i].bsd_mask; bsd_value = bitmap[i].bsd_value; if (bsd_mask == 0) bsd_mask = bsd_value; linux_mask = bitmap[i].linux_mask; linux_value = bitmap[i].linux_value; if (linux_mask == 0) linux_mask = linux_value; /* * If a mask larger than just the value is set, we explicitly * want to make sure that only this bit we mapped within that * mask is set. */ if ((value & bsd_mask) == bsd_value) { linux_ret = (linux_ret & ~linux_mask) | linux_value; applied = true; } } if (!applied) return (no_value); return (linux_ret); } int linux_to_bsd_bits_(int value, struct bsd_to_linux_bitmap *bitmap, size_t mapcnt, int no_value) { int bsd_mask, bsd_value, linux_mask, linux_value; int bsd_ret; size_t i; bool applied; applied = false; bsd_ret = 0; for (i = 0; i < mapcnt; ++i) { bsd_mask = bitmap[i].bsd_mask; bsd_value = bitmap[i].bsd_value; if (bsd_mask == 0) bsd_mask = bsd_value; linux_mask = bitmap[i].linux_mask; linux_value = bitmap[i].linux_value; if (linux_mask == 0) linux_mask = linux_value; /* * If a mask larger than just the value is set, we explicitly * want to make sure that only this bit we mapped within that * mask is set. */ if ((value & linux_mask) == linux_value) { bsd_ret = (bsd_ret & ~bsd_mask) | bsd_value; applied = true; } } if (!applied) return (no_value); return (bsd_ret); } void linux_to_bsd_poll_events(struct thread *td, int fd, short lev, short *bev) { struct file *fp; int error; short bits = 0; if (lev & LINUX_POLLIN) bits |= POLLIN; if (lev & LINUX_POLLPRI) bits |= POLLPRI; if (lev & LINUX_POLLOUT) bits |= POLLOUT; if (lev & LINUX_POLLERR) bits |= POLLERR; if (lev & LINUX_POLLHUP) bits |= POLLHUP; if (lev & LINUX_POLLNVAL) bits |= POLLNVAL; if (lev & LINUX_POLLRDNORM) bits |= POLLRDNORM; if (lev & LINUX_POLLRDBAND) bits |= POLLRDBAND; if (lev & LINUX_POLLWRBAND) bits |= POLLWRBAND; if (lev & LINUX_POLLWRNORM) bits |= POLLWRNORM; if (lev & LINUX_POLLRDHUP) { /* * It seems that the Linux silencly ignores POLLRDHUP * on non-socket file descriptors unlike FreeBSD, where * events bits is more strictly checked (POLLSTANDARD). */ error = fget_unlocked(td, fd, &cap_no_rights, &fp); if (error == 0) { /* * XXX. On FreeBSD POLLRDHUP applies only to * stream sockets. */ if (fp->f_type == DTYPE_SOCKET) bits |= POLLRDHUP; fdrop(fp, td); } } if (lev & LINUX_POLLMSG) LINUX_RATELIMIT_MSG_OPT1("unsupported POLLMSG, events(%d)", lev); if (lev & LINUX_POLLREMOVE) LINUX_RATELIMIT_MSG_OPT1("unsupported POLLREMOVE, events(%d)", lev); *bev = bits; } void bsd_to_linux_poll_events(short bev, short *lev) { short bits = 0; if (bev & POLLIN) bits |= LINUX_POLLIN; if (bev & POLLPRI) bits |= LINUX_POLLPRI; if (bev & (POLLOUT | POLLWRNORM)) /* * POLLWRNORM is equal to POLLOUT on FreeBSD, * but not on Linux */ bits |= LINUX_POLLOUT; if (bev & POLLERR) bits |= LINUX_POLLERR; if (bev & POLLHUP) bits |= LINUX_POLLHUP; if (bev & POLLNVAL) bits |= LINUX_POLLNVAL; if (bev & POLLRDNORM) bits |= LINUX_POLLRDNORM; if (bev & POLLRDBAND) bits |= LINUX_POLLRDBAND; if (bev & POLLWRBAND) bits |= LINUX_POLLWRBAND; if (bev & POLLRDHUP) bits |= LINUX_POLLRDHUP; *lev = bits; } - -bool -linux_use_real_ifname(const struct ifnet *ifp) -{ - - return (use_real_ifnames); -} diff --git a/sys/compat/linux/linux.h b/sys/compat/linux/linux.h index 6d276b8a69f4..625aee2be127 100644 --- a/sys/compat/linux/linux.h +++ b/sys/compat/linux/linux.h @@ -1,375 +1,365 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2015 Dmitry Chagin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _LINUX_MI_H_ #define _LINUX_MI_H_ /* * Machine independent set of types for the Linux types. */ typedef uint32_t l_dev_t; /* * Linux dev_t conversion routines. * * As of version 2.6.0 of the Linux kernel, dev_t is a 32-bit quantity * with 12 bits set asaid for the major number and 20 for the minor number. * The in-kernel dev_t encoded as MMMmmmmm, where M is a hex digit of the * major number and m is a hex digit of the minor number. * The user-space dev_t encoded as mmmM MMmm, where M and m is the major * and minor numbers accordingly. This is downward compatible with legacy * systems where dev_t is 16 bits wide, encoded as MMmm. * In glibc dev_t is a 64-bit quantity, with 32-bit major and minor numbers, * encoded as MMMM Mmmm mmmM MMmm. This is downward compatible with the Linux * kernel and with legacy systems where dev_t is 16 bits wide. * * In the FreeBSD dev_t is a 64-bit quantity. The major and minor numbers * are encoded as MMMmmmMm, therefore conversion of the device numbers between * Linux user-space and FreeBSD kernel required. */ static __inline l_dev_t linux_encode_dev(int _major, int _minor) { return ((_minor & 0xff) | ((_major & 0xfff) << 8) | (((_minor & ~0xff) << 12) & 0xfff00000)); } static __inline l_dev_t linux_new_encode_dev(dev_t _dev) { return (_dev == NODEV ? 0 : linux_encode_dev(major(_dev), minor(_dev))); } static __inline int linux_encode_major(dev_t _dev) { return (_dev == NODEV ? 0 : major(_dev) & 0xfff); } static __inline int linux_encode_minor(dev_t _dev) { return (_dev == NODEV ? 0 : minor(_dev) & 0xfffff); } static __inline int linux_decode_major(l_dev_t _dev) { return ((_dev & 0xfff00) >> 8); } static __inline int linux_decode_minor(l_dev_t _dev) { return ((_dev & 0xff) | ((_dev & 0xfff00000) >> 12)); } static __inline dev_t linux_decode_dev(l_dev_t _dev) { return (makedev(linux_decode_major(_dev), linux_decode_minor(_dev))); } /* * Private Brandinfo flags */ #define LINUX_BI_FUTEX_REQUEUE 0x01000000 /* * poll() */ #define LINUX_POLLIN 0x0001 #define LINUX_POLLPRI 0x0002 #define LINUX_POLLOUT 0x0004 #define LINUX_POLLERR 0x0008 #define LINUX_POLLHUP 0x0010 #define LINUX_POLLNVAL 0x0020 #define LINUX_POLLRDNORM 0x0040 #define LINUX_POLLRDBAND 0x0080 #define LINUX_POLLWRNORM 0x0100 #define LINUX_POLLWRBAND 0x0200 #define LINUX_POLLMSG 0x0400 #define LINUX_POLLREMOVE 0x1000 #define LINUX_POLLRDHUP 0x2000 #define LINUX_IFHWADDRLEN 6 #define LINUX_IFNAMSIZ 16 struct l_sockaddr { unsigned short sa_family; char sa_data[14]; }; #define LINUX_ARPHRD_ETHER 1 #define LINUX_ARPHRD_LOOPBACK 772 /* * Supported address families */ #define LINUX_AF_UNSPEC 0 #define LINUX_AF_UNIX 1 #define LINUX_AF_INET 2 #define LINUX_AF_AX25 3 #define LINUX_AF_IPX 4 #define LINUX_AF_APPLETALK 5 #define LINUX_AF_INET6 10 #define LINUX_AF_NETLINK 16 #define LINUX_NETLINK_ROUTE 0 #define LINUX_NETLINK_SOCK_DIAG 4 #define LINUX_NETLINK_NFLOG 5 #define LINUX_NETLINK_SELINUX 7 #define LINUX_NETLINK_AUDIT 9 #define LINUX_NETLINK_FIB_LOOKUP 10 #define LINUX_NETLINK_NETFILTER 12 #define LINUX_NETLINK_KOBJECT_UEVENT 15 /* * net device flags */ #define LINUX_IFF_UP 0x0001 #define LINUX_IFF_BROADCAST 0x0002 #define LINUX_IFF_DEBUG 0x0004 #define LINUX_IFF_LOOPBACK 0x0008 #define LINUX_IFF_POINTOPOINT 0x0010 #define LINUX_IFF_NOTRAILERS 0x0020 #define LINUX_IFF_RUNNING 0x0040 #define LINUX_IFF_NOARP 0x0080 #define LINUX_IFF_PROMISC 0x0100 #define LINUX_IFF_ALLMULTI 0x0200 #define LINUX_IFF_MASTER 0x0400 #define LINUX_IFF_SLAVE 0x0800 #define LINUX_IFF_MULTICAST 0x1000 #define LINUX_IFF_PORTSEL 0x2000 #define LINUX_IFF_AUTOMEDIA 0x4000 #define LINUX_IFF_DYNAMIC 0x8000 /* sigaltstack */ #define LINUX_SS_ONSTACK 1 #define LINUX_SS_DISABLE 2 int linux_to_bsd_sigaltstack(int lsa); int bsd_to_linux_sigaltstack(int bsa); /* sigset */ typedef struct { uint64_t __mask; } l_sigset_t; /* primitives to manipulate sigset_t */ #define LINUX_SIGEMPTYSET(set) (set).__mask = 0 #define LINUX_SIGISMEMBER(set, sig) (1ULL & ((set).__mask >> _SIG_IDX(sig))) #define LINUX_SIGADDSET(set, sig) (set).__mask |= 1ULL << _SIG_IDX(sig) void linux_to_bsd_sigset(l_sigset_t *, sigset_t *); void bsd_to_linux_sigset(sigset_t *, l_sigset_t *); /* signaling */ #define LINUX_SIGHUP 1 #define LINUX_SIGINT 2 #define LINUX_SIGQUIT 3 #define LINUX_SIGILL 4 #define LINUX_SIGTRAP 5 #define LINUX_SIGABRT 6 #define LINUX_SIGIOT LINUX_SIGABRT #define LINUX_SIGBUS 7 #define LINUX_SIGFPE 8 #define LINUX_SIGKILL 9 #define LINUX_SIGUSR1 10 #define LINUX_SIGSEGV 11 #define LINUX_SIGUSR2 12 #define LINUX_SIGPIPE 13 #define LINUX_SIGALRM 14 #define LINUX_SIGTERM 15 #define LINUX_SIGSTKFLT 16 #define LINUX_SIGCHLD 17 #define LINUX_SIGCONT 18 #define LINUX_SIGSTOP 19 #define LINUX_SIGTSTP 20 #define LINUX_SIGTTIN 21 #define LINUX_SIGTTOU 22 #define LINUX_SIGURG 23 #define LINUX_SIGXCPU 24 #define LINUX_SIGXFSZ 25 #define LINUX_SIGVTALRM 26 #define LINUX_SIGPROF 27 #define LINUX_SIGWINCH 28 #define LINUX_SIGIO 29 #define LINUX_SIGPOLL LINUX_SIGIO #define LINUX_SIGPWR 30 #define LINUX_SIGSYS 31 #define LINUX_SIGTBLSZ 31 #define LINUX_SIGRTMIN 32 #define LINUX_SIGRTMAX 64 #define LINUX_SIG_VALID(sig) ((sig) <= LINUX_SIGRTMAX && (sig) > 0) int linux_to_bsd_signal(int sig); int bsd_to_linux_signal(int sig); /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 void linux_dev_shm_create(void); void linux_dev_shm_destroy(void); /* * mask=0 is not sensible for this application, so it will be taken to mean * a mask equivalent to the value. Otherwise, (word & mask) == value maps to * (word & ~mask) | value in a bitfield for the platform we're converting to. */ struct bsd_to_linux_bitmap { int bsd_mask; int bsd_value; int linux_mask; int linux_value; }; int bsd_to_linux_bits_(int value, struct bsd_to_linux_bitmap *bitmap, size_t mapcnt, int no_value); int linux_to_bsd_bits_(int value, struct bsd_to_linux_bitmap *bitmap, size_t mapcnt, int no_value); /* * These functions are used for simplification of BSD <-> Linux bit conversions. * Given `value`, a bit field, these functions will walk the given bitmap table * and set the appropriate bits for the target platform. If any bits were * successfully converted, then the return value is the equivalent of value * represented with the bit values appropriate for the target platform. * Otherwise, the value supplied as `no_value` is returned. */ #define bsd_to_linux_bits(_val, _bmap, _noval) \ bsd_to_linux_bits_((_val), (_bmap), nitems((_bmap)), (_noval)) #define linux_to_bsd_bits(_val, _bmap, _noval) \ linux_to_bsd_bits_((_val), (_bmap), nitems((_bmap)), (_noval)) /* * Easy mapping helpers. BITMAP_EASY_LINUX represents a single bit to be * translated, and the FreeBSD and Linux values are supplied. BITMAP_1t1_LINUX * is the extreme version of this, where not only is it a single bit, but the * name of the macro used to represent the Linux version of a bit literally has * LINUX_ prepended to the normal name. */ #define BITMAP_EASY_LINUX(_name, _linux_name) \ { \ .bsd_value = (_name), \ .linux_value = (_linux_name), \ } #define BITMAP_1t1_LINUX(_name) BITMAP_EASY_LINUX(_name, LINUX_##_name) int bsd_to_linux_errno(int error); void linux_check_errtbl(void); #define STATX_BASIC_STATS 0x07ff #define STATX_BTIME 0x0800 #define STATX_ALL 0x0fff #define STATX_ATTR_COMPRESSED 0x0004 #define STATX_ATTR_IMMUTABLE 0x0010 #define STATX_ATTR_APPEND 0x0020 #define STATX_ATTR_NODUMP 0x0040 #define STATX_ATTR_ENCRYPTED 0x0800 #define STATX_ATTR_AUTOMOUNT 0x1000 struct l_statx_timestamp { int64_t tv_sec; int32_t tv_nsec; int32_t __spare0; }; struct l_statx { uint32_t stx_mask; uint32_t stx_blksize; uint64_t stx_attributes; uint32_t stx_nlink; uint32_t stx_uid; uint32_t stx_gid; uint16_t stx_mode; uint16_t __spare0[1]; uint64_t stx_ino; uint64_t stx_size; uint64_t stx_blocks; uint64_t stx_attributes_mask; struct l_statx_timestamp stx_atime; struct l_statx_timestamp stx_btime; struct l_statx_timestamp stx_ctime; struct l_statx_timestamp stx_mtime; uint32_t stx_rdev_major; uint32_t stx_rdev_minor; uint32_t stx_dev_major; uint32_t stx_dev_minor; uint64_t stx_mnt_id; uint64_t __spare2[13]; }; /* * statfs f_flags */ #define LINUX_ST_RDONLY 0x0001 #define LINUX_ST_NOSUID 0x0002 #define LINUX_ST_NODEV 0x0004 /* No native analogue */ #define LINUX_ST_NOEXEC 0x0008 #define LINUX_ST_SYNCHRONOUS 0x0010 #define LINUX_ST_VALID 0x0020 #define LINUX_ST_MANDLOCK 0x0040 /* No native analogue */ #define LINUX_ST_NOATIME 0x0400 #define LINUX_ST_NODIRATIME 0x0800 /* No native analogue */ #define LINUX_ST_RELATIME 0x1000 /* No native analogue */ #define LINUX_ST_NOSYMFOLLOW 0x2000 #ifndef lower_32_bits #define lower_32_bits(n) ((uint32_t)((n) & 0xffffffff)) #endif #ifdef KTRACE #define linux_ktrsigset(s, l) \ ktrstruct("l_sigset_t", (s), l) #endif -/* - * Criteria for interface name translation - */ -#define IFP_IS_ETH(ifp) (if_gettype(ifp) == IFT_ETHER) -#define IFP_IS_LOOP(ifp) (if_gettype(ifp) == IFT_LOOP) - -struct ifnet; - -bool linux_use_real_ifname(const struct ifnet *); - void linux_netlink_register(void); void linux_netlink_deregister(void); #endif /* _LINUX_MI_H_ */ diff --git a/sys/compat/linux/linux_if.c b/sys/compat/linux/linux_if.c new file mode 100644 index 000000000000..29e86d71aa5a --- /dev/null +++ b/sys/compat/linux/linux_if.c @@ -0,0 +1,310 @@ +/*- + * Copyright (c) 2015 Dmitry Chagin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +_Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ"); + +static bool use_real_ifnames = false; +SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN, + &use_real_ifnames, 0, + "Use FreeBSD interface names instead of generating ethN aliases"); + +/* + * Criteria for interface name translation + */ +#define IFP_IS_ETH(ifp) (if_gettype(ifp) == IFT_ETHER) +#define IFP_IS_LOOP(ifp) (if_gettype(ifp) == IFT_LOOP) + +/* + * Translate a FreeBSD interface name to a Linux interface name + * by interface name, and return the number of bytes copied to lxname. + */ +int +ifname_bsd_to_linux_name(const char *bsdname, char *lxname, size_t len) +{ + struct epoch_tracker et; + struct ifnet *ifp; + int ret; + + CURVNET_ASSERT_SET(); + + ret = 0; + NET_EPOCH_ENTER(et); + ifp = ifunit(bsdname); + if (ifp != NULL) + ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); + NET_EPOCH_EXIT(et); + return (ret); +} + +/* + * Translate a FreeBSD interface name to a Linux interface name + * by interface index, and return the number of bytes copied to lxname. + */ +int +ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) +{ + struct epoch_tracker et; + struct ifnet *ifp; + int ret; + + ret = 0; + CURVNET_SET(TD_TO_VNET(curthread)); + NET_EPOCH_ENTER(et); + ifp = ifnet_byindex(idx); + if (ifp != NULL) + ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); + NET_EPOCH_EXIT(et); + CURVNET_RESTORE(); + return (ret); +} + +/* + * Translate a FreeBSD interface name to a Linux interface name, + * and return the number of bytes copied to lxname, 0 if interface + * not found, -1 on error. + */ +struct ifname_bsd_to_linux_ifp_cb_s { + struct ifnet *ifp; + int ethno; + char *lxname; + size_t len; +}; + +static int +ifname_bsd_to_linux_ifp_cb(if_t ifp, void *arg) +{ + struct ifname_bsd_to_linux_ifp_cb_s *cbs = arg; + + if (ifp == cbs->ifp) + return (snprintf(cbs->lxname, cbs->len, "eth%d", cbs->ethno)); + if (IFP_IS_ETH(ifp)) + cbs->ethno++; + return (0); +} + +int +ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len) +{ + struct ifname_bsd_to_linux_ifp_cb_s arg = { + .ifp = ifp, + .ethno = 0, + .lxname = lxname, + .len = len, + }; + + NET_EPOCH_ASSERT(); + + /* + * Linux loopback interface name is lo (not lo0), + * we translate lo to lo0, loX to loX. + */ + if (IFP_IS_LOOP(ifp) && strncmp(if_name(ifp), "lo0", IFNAMSIZ) == 0) + return (strlcpy(lxname, "lo", len)); + + /* Short-circuit non ethernet interfaces. */ + if (!IFP_IS_ETH(ifp) || use_real_ifnames) + return (strlcpy(lxname, if_name(ifp), len)); + + /* Determine the (relative) unit number for ethernet interfaces. */ + return (if_foreach(ifname_bsd_to_linux_ifp_cb, &arg)); +} + +/* + * Translate a Linux interface name to a FreeBSD interface name, + * and return the associated ifnet structure + * bsdname and lxname need to be least IFNAMSIZ bytes long, but + * can point to the same buffer. + */ +struct ifname_linux_to_ifp_cb_s { + bool is_lo; + bool is_eth; + int ethno; + int unit; + const char *lxname; + if_t ifp; +}; + +static int +ifname_linux_to_ifp_cb(if_t ifp, void *arg) +{ + struct ifname_linux_to_ifp_cb_s *cbs = arg; + + NET_EPOCH_ASSERT(); + + /* + * Allow Linux programs to use FreeBSD names. Don't presume + * we never have an interface named "eth", so don't make + * the test optional based on is_eth. + */ + if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) + goto out; + if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->ethno) + goto out; + if (cbs->is_lo && IFP_IS_LOOP(ifp)) + goto out; + if (IFP_IS_ETH(ifp)) + cbs->ethno++; + return (0); + +out: + cbs->ifp = ifp; + return (1); +} + +struct ifnet * +ifname_linux_to_ifp(struct thread *td, const char *lxname) +{ + struct ifname_linux_to_ifp_cb_s arg = { + .ethno = 0, + .lxname = lxname, + .ifp = NULL, + }; + int len; + char *ep; + + NET_EPOCH_ASSERT(); + + for (len = 0; len < LINUX_IFNAMSIZ; ++len) + if (!isalpha(lxname[len]) || lxname[len] == '\0') + break; + if (len == 0 || len == LINUX_IFNAMSIZ) + return (NULL); + /* + * Linux loopback interface name is lo (not lo0), + * we translate lo to lo0, loX to loX. + */ + arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); + arg.unit = (int)strtoul(lxname + len, &ep, 10); + if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && + arg.is_lo == 0) + return (NULL); + arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); + + if_foreach(ifname_linux_to_ifp_cb, &arg); + return (arg.ifp); +} + +int +ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) +{ + struct epoch_tracker et; + struct ifnet *ifp; + + CURVNET_SET(TD_TO_VNET(td)); + NET_EPOCH_ENTER(et); + ifp = ifname_linux_to_ifp(td, lxname); + if (ifp != NULL && bsdname != NULL) + strlcpy(bsdname, if_name(ifp), IFNAMSIZ); + NET_EPOCH_EXIT(et); + CURVNET_RESTORE(); + return (ifp != NULL ? 0 : EINVAL); +} + +unsigned short +linux_ifflags(struct ifnet *ifp) +{ + unsigned short flags; + + NET_EPOCH_ASSERT(); + + flags = if_getflags(ifp) | if_getdrvflags(ifp); + return (bsd_to_linux_ifflags(flags)); +} + +unsigned short +bsd_to_linux_ifflags(int fl) +{ + unsigned short flags = 0; + + if (fl & IFF_UP) + flags |= LINUX_IFF_UP; + if (fl & IFF_BROADCAST) + flags |= LINUX_IFF_BROADCAST; + if (fl & IFF_DEBUG) + flags |= LINUX_IFF_DEBUG; + if (fl & IFF_LOOPBACK) + flags |= LINUX_IFF_LOOPBACK; + if (fl & IFF_POINTOPOINT) + flags |= LINUX_IFF_POINTOPOINT; + if (fl & IFF_DRV_RUNNING) + flags |= LINUX_IFF_RUNNING; + if (fl & IFF_NOARP) + flags |= LINUX_IFF_NOARP; + if (fl & IFF_PROMISC) + flags |= LINUX_IFF_PROMISC; + if (fl & IFF_ALLMULTI) + flags |= LINUX_IFF_ALLMULTI; + if (fl & IFF_MULTICAST) + flags |= LINUX_IFF_MULTICAST; + return (flags); +} + +static u_int +linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) +{ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; + struct l_sockaddr *lsa = arg; + + if (count > 0) + return (0); + if (sdl->sdl_type != IFT_ETHER) + return (0); + bzero(lsa, sizeof(*lsa)); + lsa->sa_family = LINUX_ARPHRD_ETHER; + bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); + return (1); +} + +int +linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) +{ + + NET_EPOCH_ASSERT(); + + if (IFP_IS_LOOP(ifp)) { + bzero(lsa, sizeof(*lsa)); + lsa->sa_family = LINUX_ARPHRD_LOOPBACK; + return (0); + } + if (!IFP_IS_ETH(ifp)) + return (ENOENT); + if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) + return (0); + return (ENOENT); +} diff --git a/sys/modules/linux_common/Makefile b/sys/modules/linux_common/Makefile index f9f4c791e9d1..291fc6d25f8c 100644 --- a/sys/modules/linux_common/Makefile +++ b/sys/modules/linux_common/Makefile @@ -1,26 +1,25 @@ .PATH: ${SRCTOP}/sys/compat/linux .if ${MACHINE_CPUARCH} == "amd64" .PATH: ${SRCTOP}/sys/x86/linux .endif KMOD= linux_common SRCS= linux_common.c linux_mib.c linux_mmap.c linux_util.c linux_emul.c \ - linux_dummy.c linux_errno.c linux_netlink.c \ + linux_dummy.c linux_errno.c linux_netlink.c linux_if.c \ linux.c device_if.h vnode_if.h bus_if.h opt_inet6.h opt_inet.h .if ${MACHINE_CPUARCH} == "amd64" SRCS+= linux_x86.c linux_vdso_selector_x86.c .endif EXPORT_SYMS= EXPORT_SYMS+= linux_get_osname EXPORT_SYMS+= linux_get_osrelease -EXPORT_SYMS+= linux_use_real_ifname .if !defined(KERNBUILDDIR) .warning Building Linuxulator outside of a kernel does not make sense .endif EXPORT_SYMS= YES .include