diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c --- a/sys/compat/linprocfs/linprocfs.c +++ b/sys/compat/linprocfs/linprocfs.c @@ -1474,26 +1474,79 @@ return (error); } +struct linux_ifname_cb_s { + struct ifnet *ifp; + int ethno; + char *buffer; + size_t buflen; +}; + +static int +linux_ifname_cb(if_t ifp, void *arg) +{ + struct linux_ifname_cb_s *cbs = arg; + + if (ifp == cbs->ifp) + return (snprintf(cbs->buffer, cbs->buflen, "eth%d", cbs->ethno)); + if (!linux_use_real_ifname(ifp)) + cbs->ethno++; + return (0); +} + static int linux_ifname(struct ifnet *ifp, char *buffer, size_t buflen) { - struct ifnet *ifscan; - int ethno; + struct linux_ifname_cb_s arg; IFNET_RLOCK_ASSERT(); + arg.ifp = ifp; + arg.buffer = buffer; + arg.buflen = buflen; + arg.ethno = 0; + /* Short-circuit non ethernet interfaces */ if (linux_use_real_ifname(ifp)) - return (strlcpy(buffer, ifp->if_xname, buflen)); + return (strlcpy(buffer, if_name(ifp), buflen)); /* Determine the (relative) unit number for ethernet interfaces */ - ethno = 0; - CK_STAILQ_FOREACH(ifscan, &V_ifnet, if_link) { - if (ifscan == ifp) - return (snprintf(buffer, buflen, "eth%d", ethno)); - if (!linux_use_real_ifname(ifscan)) - ethno++; - } + return (if_foreach_ifnet(linux_ifname_cb, &arg)); +} + +static int +linprocfs_donetdev_cb(if_t ifp, void *arg) +{ + char ifname[16]; /* XXX LINUX_IFNAMSIZ */ + struct sbuf *sb = arg; + + linux_ifname(ifp, ifname, sizeof ifname); + sbuf_printf(sb, "%6.6s: ", ifname); + sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ", + (uintmax_t )if_getcounter(ifp, IFCOUNTER_IBYTES), + (uintmax_t )if_getcounter(ifp, IFCOUNTER_IPACKETS), + (uintmax_t )if_getcounter(ifp, IFCOUNTER_IERRORS), + (uintmax_t )if_getcounter(ifp, IFCOUNTER_IQDROPS), + /* rx_missed_errors */ + 0UL, /* rx_fifo_errors */ + 0UL, /* rx_length_errors + + * rx_over_errors + + * rx_crc_errors + + * rx_frame_errors */ + 0UL, /* rx_compressed */ + (uintmax_t )if_getcounter(ifp, IFCOUNTER_IMCASTS)); + /* XXX-BZ rx only? */ + sbuf_printf(sb, "%8ju %7ju %4ju %4ju %4lu %5ju %7lu %10lu\n", + (uintmax_t )if_getcounter(ifp, IFCOUNTER_OBYTES), + (uintmax_t )if_getcounter(ifp, IFCOUNTER_OPACKETS), + (uintmax_t )if_getcounter(ifp, IFCOUNTER_OERRORS), + (uintmax_t )if_getcounter(ifp, IFCOUNTER_OQDROPS), + 0UL, /* tx_fifo_errors */ + (uintmax_t )if_getcounter(ifp, IFCOUNTER_COLLISIONS), + 0UL, /* tx_carrier_errors + + * tx_aborted_errors + + * tx_window_errors + + * tx_heartbeat_errors*/ + 0UL); /* tx_compressed */ return (0); } @@ -1504,9 +1557,6 @@ static int linprocfs_donetdev(PFS_FILL_ARGS) { - char ifname[16]; /* XXX LINUX_IFNAMSIZ */ - struct ifnet *ifp; - sbuf_printf(sb, "%6s|%58s|%s\n" "%6s|%58s|%58s\n", "Inter-", " Receive", " Transmit", @@ -1516,36 +1566,7 @@ CURVNET_SET(TD_TO_VNET(curthread)); IFNET_RLOCK(); - CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - linux_ifname(ifp, ifname, sizeof ifname); - sbuf_printf(sb, "%6.6s: ", ifname); - sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ", - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IBYTES), - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IPACKETS), - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IERRORS), - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IQDROPS), - /* rx_missed_errors */ - 0UL, /* rx_fifo_errors */ - 0UL, /* rx_length_errors + - * rx_over_errors + - * rx_crc_errors + - * rx_frame_errors */ - 0UL, /* rx_compressed */ - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IMCASTS)); - /* XXX-BZ rx only? */ - sbuf_printf(sb, "%8ju %7ju %4ju %4ju %4lu %5ju %7lu %10lu\n", - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OBYTES), - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OPACKETS), - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OERRORS), - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OQDROPS), - 0UL, /* tx_fifo_errors */ - (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_COLLISIONS), - 0UL, /* tx_carrier_errors + - * tx_aborted_errors + - * tx_window_errors + - * tx_heartbeat_errors*/ - 0UL); /* tx_compressed */ - } + if_foreach_ifnet(linprocfs_donetdev_cb, sb); IFNET_RUNLOCK(); CURVNET_RESTORE(); diff --git a/sys/compat/linsysfs/linsysfs.c b/sys/compat/linsysfs/linsysfs.c --- a/sys/compat/linsysfs/linsysfs.c +++ b/sys/compat/linsysfs/linsysfs.c @@ -116,7 +116,7 @@ ifp = ifname_linux_to_bsd(td, pn->pn_parent->pn_name, NULL); if (ifp == NULL) return (ENOENT); - sbuf_printf(sb, "%u\n", ifp->if_index); + sbuf_printf(sb, "%u\n", if_getindex(ifp)); return (0); } @@ -128,7 +128,7 @@ ifp = ifname_linux_to_bsd(td, pn->pn_parent->pn_name, NULL); if (ifp == NULL) return (ENOENT); - sbuf_printf(sb, "%u\n", ifp->if_mtu); + sbuf_printf(sb, "%u\n", if_getmtu(ifp)); return (0); } diff --git a/sys/compat/linux/linux.c b/sys/compat/linux/linux.c --- a/sys/compat/linux/linux.c +++ b/sys/compat/linux/linux.c @@ -237,6 +237,36 @@ } } +struct ifname_linux_to_bsd_cb_s { + if_t ifp; + const char *lxname; + int unit; + int index; + bool is_lo; + bool is_eth; +}; + +static int +ifname_linux_to_bsd_cb(if_t ifp, void *arg) +{ + struct ifname_linux_to_bsd_cb_s *cbs = arg; + + /* + * 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. + */ + cbs->ifp = ifp; + if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) + return (-1); + if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->index++) + return (-1); + if (cbs->is_lo && IFP_IS_LOOP(ifp)) + return (-1); + cbs->ifp = NULL; + + return (0); +} /* * Translate a Linux interface name to a FreeBSD interface name, * and return the associated ifnet structure @@ -246,46 +276,33 @@ struct ifnet * ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) { - struct ifnet *ifp; - int len, unit; + struct ifname_linux_to_bsd_cb_s cbs = {}; + int len; char *ep; - int index; - bool is_eth, is_lo; + cbs.lxname = lxname; 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) */ - is_lo = (len == 2 && strncmp(lxname, "lo", len) == 0); - unit = (int)strtoul(lxname + len, &ep, 10); + cbs.is_lo = (len == 2 && strncmp(lxname, "lo", len) == 0); + cbs.unit = (int)strtoul(lxname + len, &ep, 10); if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && - is_lo == 0) + cbs.is_lo == 0) return (NULL); - index = 0; - is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); + cbs.index = 0; + cbs.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); CURVNET_SET(TD_TO_VNET(td)); IFNET_RLOCK(); - CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - /* - * 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(ifp->if_xname, lxname, LINUX_IFNAMSIZ) == 0) - break; - if (is_eth && IFP_IS_ETH(ifp) && unit == index++) - break; - if (is_lo && IFP_IS_LOOP(ifp)) - break; - } + if_foreach_ifnet(ifname_linux_to_bsd_cb, &cbs); IFNET_RUNLOCK(); CURVNET_RESTORE(); - if (ifp != NULL && bsdname != NULL) - strlcpy(bsdname, ifp->if_xname, IFNAMSIZ); - return (ifp); + if (cbs.ifp != NULL && bsdname != NULL) + strlcpy(bsdname, if_name(cbs.ifp), IFNAMSIZ); + return (cbs.ifp); } void @@ -293,7 +310,7 @@ { unsigned short fl; - fl = (ifp->if_flags | ifp->if_drv_flags) & 0xffff; + fl = (if_getflags(ifp) | if_getdrvflags(ifp)) & 0xffff; *flags = 0; if (fl & IFF_UP) *flags |= LINUX_IFF_UP; @@ -317,11 +334,28 @@ *flags |= LINUX_IFF_MULTICAST; } +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) { - struct ifaddr *ifa; - struct sockaddr_dl *sdl; if (IFP_IS_LOOP(ifp)) { bzero(lsa, sizeof(*lsa)); @@ -332,16 +366,8 @@ if (!IFP_IS_ETH(ifp)) return (ENOENT); - CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - sdl = (struct sockaddr_dl*)ifa->ifa_addr; - if (sdl != NULL && (sdl->sdl_family == AF_LINK) && - (sdl->sdl_type == IFT_ETHER)) { - bzero(lsa, sizeof(*lsa)); - lsa->sa_family = LINUX_ARPHRD_ETHER; - bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); - return (0); - } - } + if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) + return (0); return (ENOENT); } diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c --- a/sys/compat/linux/linux_ioctl.c +++ b/sys/compat/linux/linux_ioctl.c @@ -2102,46 +2102,138 @@ /* * Implement the SIOCGIFNAME ioctl */ +struct linux_ioctl_ifname_cb_s { + struct l_ifreq ifr; + int index; + int ethno; +}; + +static int +linux_ioctl_ifname_cb(if_t ifp, void *arg) +{ + struct linux_ioctl_ifname_cb_s *cbs = arg; + + if (cbs->ifr.ifr_ifindex == cbs->index) { + if (!linux_use_real_ifname(ifp)) + snprintf(cbs->ifr.ifr_name, LINUX_IFNAMSIZ, + "eth%d", cbs->ethno); + else + strlcpy(cbs->ifr.ifr_name, if_name(ifp), + LINUX_IFNAMSIZ); + return (-1); + } + if (!linux_use_real_ifname(ifp)) + cbs->ethno++; + cbs->index++; + + return (0); +} static int linux_ioctl_ifname(struct thread *td, struct l_ifreq *uifr) { + struct linux_ioctl_ifname_cb_s cbs; struct l_ifreq ifr; - struct ifnet *ifp; - int error, ethno, index; + int error; - error = copyin(uifr, &ifr, sizeof(ifr)); + error = copyin(uifr, &cbs.ifr, sizeof(cbs.ifr)); if (error != 0) return (error); CURVNET_SET(TD_TO_VNET(curthread)); IFNET_RLOCK(); - index = 1; /* ifr.ifr_ifindex starts from 1 */ - ethno = 0; - error = ENODEV; - CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - if (ifr.ifr_ifindex == index) { - if (!linux_use_real_ifname(ifp)) - snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, - "eth%d", ethno); - else - strlcpy(ifr.ifr_name, ifp->if_xname, - LINUX_IFNAMSIZ); - error = 0; - break; - } - if (!linux_use_real_ifname(ifp)) - ethno++; - index++; - } - IFNET_RUNLOCK(); + cbs.index = 1; /* ifr.ifr_ifindex starts from 1 */ + cbs.ethno = 0; + error = if_foreach_ifnet(linux_ioctl_ifname_cb, &cbs); + if (error == 0) + error = ENODEV; + IFNET_RUNLOCK(); + if (error == -1) error = copyout(&ifr, uifr, sizeof(ifr)); CURVNET_RESTORE(); return (error); } +static u_int +linux_ifconf_ifaddr_cb(void *arg, struct ifaddr *ifa, u_int count) +{ +#ifdef COMPAT_LINUX32 + struct l_ifconf *ifc; +#else + struct ifconf *ifc; +#endif + ifc = arg; + ifc->ifc_len += sizeof(struct l_ifreq); + + return (1); +} + +static int +linux_ifconf_ifnet_cb(if_t ifp, void *arg) +{ + if_foreach_addr_type(ifp, AF_INET, linux_ifconf_ifaddr_cb, arg); + + return (0); +} + +struct linux_ifconfig_ifaddr_cb2_s { + struct l_ifreq ifr; + struct sbuf *sb; + size_t max_len; + size_t valid_len; + int ethno; +}; + +static u_int +linux_ifconf_ifaddr_cb2(void *arg, struct ifaddr *ifa, u_int len) +{ + struct linux_ifconfig_ifaddr_cb2_s *cbs = arg; + struct sockaddr *sa = ifa->ifa_addr; + + cbs->ifr.ifr_addr.sa_family = LINUX_AF_INET; + memcpy(cbs->ifr.ifr_addr.sa_data, sa->sa_data, + sizeof(cbs->ifr.ifr_addr.sa_data)); + sbuf_bcat(cbs->sb, &cbs->ifr, sizeof(cbs->ifr)); + cbs->max_len += sizeof(cbs->ifr); + + if (sbuf_error(cbs->sb) == 0) + cbs->valid_len = sbuf_len(cbs->sb); + + return (len); +} + +static int +linux_ifconf_ifnet_cb2(if_t ifp, void *arg) +{ + struct linux_ifconfig_ifaddr_cb2_s *cbs = arg; + int addrs = 0; + + bzero(&cbs->ifr, sizeof(cbs->ifr)); + if (IFP_IS_ETH(ifp)) + snprintf(cbs->ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d", + cbs->ethno++); + else + strlcpy(cbs->ifr.ifr_name, if_name(ifp), LINUX_IFNAMSIZ); + + /* Walk the address list */ + addrs = if_foreach_addr_type(ifp, AF_INET, + linux_ifconf_ifaddr_cb2, cbs); + if (sbuf_error(cbs->sb) == 0) + cbs->valid_len = sbuf_len(cbs->sb); + if (addrs == 0) { + bzero((caddr_t)&cbs->ifr.ifr_addr, sizeof(cbs->ifr.ifr_addr)); + sbuf_bcat(cbs->sb, &cbs->ifr, sizeof(cbs->ifr)); + cbs->max_len += sizeof(cbs->ifr); + + if (sbuf_error(cbs->sb) == 0) + cbs->valid_len = sbuf_len(cbs->sb); + } + + return (0); +} + /* * Implement the SIOCGIFCONF ioctl */ @@ -2154,30 +2246,22 @@ #else struct ifconf ifc; #endif - struct l_ifreq ifr; - struct ifnet *ifp; - struct ifaddr *ifa; + struct linux_ifconfig_ifaddr_cb2_s cbs; struct sbuf *sb; - int error, ethno, full = 0, valid_len, max_len; + int error, full = 0; error = copyin(uifc, &ifc, sizeof(ifc)); if (error != 0) return (error); - max_len = maxphys - 1; + cbs.max_len = maxphys - 1; CURVNET_SET(TD_TO_VNET(td)); /* handle the 'request buffer size' case */ if ((l_uintptr_t)ifc.ifc_buf == PTROUT(NULL)) { ifc.ifc_len = 0; IFNET_RLOCK(); - CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - struct sockaddr *sa = ifa->ifa_addr; - if (sa->sa_family == AF_INET) - ifc.ifc_len += sizeof(ifr); - } - } + if_foreach_ifnet(linux_ifconf_ifnet_cb, &ifc); IFNET_RUNLOCK(); error = copyout(&ifc, uifc, sizeof(ifc)); CURVNET_RESTORE(); @@ -2190,61 +2274,28 @@ } again: + cbs.ethno = 0; /* Keep track of eth interfaces */ - ethno = 0; - if (ifc.ifc_len <= max_len) { - max_len = ifc.ifc_len; + if (ifc.ifc_len <= cbs.max_len) { + cbs.max_len = ifc.ifc_len; full = 1; } - sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN); - max_len = 0; - valid_len = 0; + sb = sbuf_new(NULL, NULL, cbs.max_len + 1, SBUF_FIXEDLEN); + cbs.max_len = 0; + cbs.valid_len = 0; + cbs.sb = sb; /* Return all AF_INET addresses of all interfaces */ IFNET_RLOCK(); - CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - int addrs = 0; - - bzero(&ifr, sizeof(ifr)); - if (IFP_IS_ETH(ifp)) - snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d", - ethno++); - else - strlcpy(ifr.ifr_name, ifp->if_xname, LINUX_IFNAMSIZ); - - /* Walk the address list */ - CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - struct sockaddr *sa = ifa->ifa_addr; - - if (sa->sa_family == AF_INET) { - ifr.ifr_addr.sa_family = LINUX_AF_INET; - memcpy(ifr.ifr_addr.sa_data, sa->sa_data, - sizeof(ifr.ifr_addr.sa_data)); - sbuf_bcat(sb, &ifr, sizeof(ifr)); - max_len += sizeof(ifr); - addrs++; - } - - if (sbuf_error(sb) == 0) - valid_len = sbuf_len(sb); - } - if (addrs == 0) { - bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); - sbuf_bcat(sb, &ifr, sizeof(ifr)); - max_len += sizeof(ifr); - - if (sbuf_error(sb) == 0) - valid_len = sbuf_len(sb); - } - } + if_foreach_ifnet(linux_ifconf_ifnet_cb2, &cbs); IFNET_RUNLOCK(); - if (valid_len != max_len && !full) { + if (cbs.valid_len != cbs.max_len && !full) { sbuf_delete(sb); goto again; } - ifc.ifc_len = valid_len; + ifc.ifc_len = cbs.valid_len; sbuf_finish(sb); error = copyout(sbuf_data(sb), PTRIN(ifc.ifc_buf), ifc.ifc_len); if (error == 0) diff --git a/sys/compat/linux/linux_util.h b/sys/compat/linux/linux_util.h --- a/sys/compat/linux/linux_util.h +++ b/sys/compat/linux/linux_util.h @@ -42,7 +42,6 @@ #include #include #include -#include #include MALLOC_DECLARE(M_LINUX); @@ -123,8 +122,8 @@ /* * Criteria for interface name translation */ -#define IFP_IS_ETH(ifp) ((ifp)->if_type == IFT_ETHER) -#define IFP_IS_LOOP(ifp) ((ifp)->if_type == IFT_LOOP) +#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 *ifp); diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c --- a/sys/compat/linux/linux_util.c +++ b/sys/compat/linux/linux_util.c @@ -328,5 +328,5 @@ bool linux_use_real_ifname(const struct ifnet *ifp) { - return (use_real_ifnames || !IFP_IS_ETH(ifp)); + return (use_real_ifnames || !IFP_IS_ETH((__DECONST(if_t, ifp)))); }