MFC r279538:

Fix group membership of cloned interfaces when one is moved by
if_vmove().

In if_vmove(), if_detach_internal() and if_attach_internal() were
called in series to detach and reattach the interface.  When
detaching, if_delgroup() was called and the interface leaves all of
the group membership.  And then upon attachment, if_addgroup(ifp,
IFG_ALL) was called and it joined only "all" group again.

This had a problem. Normally, a cloned interface automatically joins
a group whose name is ifc_name of the cloner in addition to "all"
upon creation.  However, if_vmove() removed the membership and did
not restore upon attachment.

Approved by:	re (gjb)
This commit is contained in:
hrs 2015-07-23 19:57:47 +00:00
parent 91dc3e4cd8
commit 123cf5c769
3 changed files with 71 additions and 8 deletions

View File

@ -175,8 +175,8 @@ static void do_link_state_change(void *, int);
static int if_getgroup(struct ifgroupreq *, struct ifnet *);
static int if_getgroupmembers(struct ifgroupreq *);
static void if_delgroups(struct ifnet *);
static void if_attach_internal(struct ifnet *, int);
static void if_detach_internal(struct ifnet *, int);
static void if_attach_internal(struct ifnet *, int, struct if_clone *);
static void if_detach_internal(struct ifnet *, int, struct if_clone **);
#ifdef INET6
/*
@ -571,6 +571,15 @@ ifq_delete(struct ifaltq *ifq)
* tasks, given that we are moving from one vnet to another an ifnet which
* has already been fully initialized.
*
* Note that if_detach_internal() removes group membership unconditionally
* even when vmove flag is set, and if_attach_internal() adds only IFG_ALL.
* Thus, when if_vmove() is applied to a cloned interface, group membership
* is lost while a cloned one always joins a group whose name is
* ifc->ifc_name. To recover this after if_detach_internal() and
* if_attach_internal(), the cloner should be specified to
* if_attach_internal() via ifc. If it is non-NULL, if_attach_internal()
* attempts to join a group whose name is ifc->ifc_name.
*
* XXX:
* - The decision to return void and thus require this function to
* succeed is questionable.
@ -581,7 +590,7 @@ void
if_attach(struct ifnet *ifp)
{
if_attach_internal(ifp, 0);
if_attach_internal(ifp, 0, NULL);
}
/*
@ -636,7 +645,7 @@ if_hw_tsomax_update(struct ifnet *ifp, struct ifnet_hw_tsomax *pmax)
}
static void
if_attach_internal(struct ifnet *ifp, int vmove)
if_attach_internal(struct ifnet *ifp, int vmove, struct if_clone *ifc)
{
unsigned socksize, ifasize;
int namelen, masklen;
@ -655,6 +664,10 @@ if_attach_internal(struct ifnet *ifp, int vmove)
if_addgroup(ifp, IFG_ALL);
/* Restore group membership for cloned interfaces. */
if (vmove && ifc != NULL)
if_clone_addgroup(ifp, ifc);
getmicrotime(&ifp->if_lastchange);
ifp->if_data.ifi_epoch = time_uptime;
ifp->if_data.ifi_datalen = sizeof(struct if_data);
@ -877,12 +890,12 @@ if_detach(struct ifnet *ifp)
{
CURVNET_SET_QUIET(ifp->if_vnet);
if_detach_internal(ifp, 0);
if_detach_internal(ifp, 0, NULL);
CURVNET_RESTORE();
}
static void
if_detach_internal(struct ifnet *ifp, int vmove)
if_detach_internal(struct ifnet *ifp, int vmove, struct if_clone **ifcp)
{
struct ifaddr *ifa;
struct radix_node_head *rnh;
@ -911,6 +924,10 @@ if_detach_internal(struct ifnet *ifp, int vmove)
return; /* XXX this should panic as well? */
}
/* Check if this is a cloned interface or not. */
if (vmove && ifcp != NULL)
*ifcp = if_clone_findifc(ifp);
/*
* Remove/wait for pending events.
*/
@ -1016,12 +1033,13 @@ void
if_vmove(struct ifnet *ifp, struct vnet *new_vnet)
{
u_short idx;
struct if_clone *ifc;
/*
* Detach from current vnet, but preserve LLADDR info, do not
* mark as dead etc. so that the ifnet can be reattached later.
*/
if_detach_internal(ifp, 1);
if_detach_internal(ifp, 1, &ifc);
/*
* Unlink the ifnet from ifindex_table[] in current vnet, and shrink
@ -1055,7 +1073,7 @@ if_vmove(struct ifnet *ifp, struct vnet *new_vnet)
ifnet_setbyindex_locked(ifp->if_index, ifp);
IFNET_WUNLOCK();
if_attach_internal(ifp, 1);
if_attach_internal(ifp, 1, ifc);
CURVNET_RESTORE();
}

View File

@ -524,6 +524,49 @@ done:
return (err);
}
/*
* if_clone_findifc() looks up ifnet from the current
* cloner list, and returns ifc if found. Note that ifc_refcnt
* is incremented.
*/
struct if_clone *
if_clone_findifc(struct ifnet *ifp)
{
struct if_clone *ifc, *ifc0;
struct ifnet *ifcifp;
ifc0 = NULL;
IF_CLONERS_LOCK();
LIST_FOREACH(ifc, &V_if_cloners, ifc_list) {
IF_CLONE_LOCK(ifc);
LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) {
if (ifp == ifcifp) {
ifc0 = ifc;
IF_CLONE_ADDREF_LOCKED(ifc);
break;
}
}
IF_CLONE_UNLOCK(ifc);
if (ifc0 != NULL)
break;
}
IF_CLONERS_UNLOCK();
return (ifc0);
}
/*
* if_clone_addgroup() decrements ifc_refcnt because it is called after
* if_clone_findifc().
*/
void
if_clone_addgroup(struct ifnet *ifp, struct if_clone *ifc)
{
if_addgroup(ifp, ifc->ifc_name);
IF_CLONE_REMREF(ifc);
}
/*
* A utility function to extract unit numbers from interface names of
* the form name###.

View File

@ -68,6 +68,8 @@ void vnet_if_clone_init(void);
int if_clone_create(char *, size_t, caddr_t);
int if_clone_destroy(const char *);
int if_clone_list(struct if_clonereq *);
struct if_clone *if_clone_findifc(struct ifnet *);
void if_clone_addgroup(struct ifnet *, struct if_clone *);
/* The below interface used only by epair(4). */
int if_clone_destroyif(struct if_clone *, struct ifnet *);