Port Linuxulator to IfAPI
Reviewed by: dchagin Sponsored by: Juniper Networks, Inc. Differential Revision: https://reviews.freebsd.org/D38349
This commit is contained in:
parent
68b47dcbe3
commit
52d984831d
@ -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)));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user