Port Linuxulator to IfAPI

Reviewed by:	dchagin
Sponsored by:	Juniper Networks, Inc.
Differential Revision: https://reviews.freebsd.org/D38349
This commit is contained in:
Justin Hibbits 2023-02-01 16:30:22 -05:00
parent 68b47dcbe3
commit 52d984831d
3 changed files with 195 additions and 118 deletions

View File

@ -241,6 +241,36 @@ bsd_to_linux_sigset(sigset_t *bss, l_sigset_t *lss)
}
}
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
@ -250,46 +280,33 @@ bsd_to_linux_sigset(sigset_t *bss, l_sigset_t *lss)
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(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
@ -297,7 +314,7 @@ linux_ifflags(struct ifnet *ifp, short *flags)
{
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;
@ -321,11 +338,28 @@ linux_ifflags(struct ifnet *ifp, short *flags)
*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));
@ -336,16 +370,8 @@ linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa)
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);
}
@ -732,5 +758,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)));
}

View File

@ -287,8 +287,8 @@ struct l_statx {
/*
* 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;

View File

@ -2087,46 +2087,138 @@ linux_ioctl_console(struct thread *td, struct linux_ioctl_args *args)
/*
* 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(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
*/
@ -2139,30 +2231,22 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
#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(linux_ifconf_ifnet_cb, &ifc);
IFNET_RUNLOCK();
error = copyout(&ifc, uifc, sizeof(ifc));
CURVNET_RESTORE();
@ -2175,61 +2259,28 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
}
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(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)