MFC r269492:
Improve locking of multicast addresses in VLAN and LAGG interfaces. This fixes several scenarios of reproducible panics, cause by races between multicast address changes and interface destruction.
This commit is contained in:
parent
b8303841b8
commit
0959ad1632
@ -1219,39 +1219,39 @@ lagg_ether_cmdmulti(struct lagg_port *lp, int set)
|
||||
struct ifnet *ifp = lp->lp_ifp;
|
||||
struct ifnet *scifp = sc->sc_ifp;
|
||||
struct lagg_mc *mc;
|
||||
struct ifmultiaddr *ifma, *rifma = NULL;
|
||||
struct sockaddr_dl sdl;
|
||||
struct ifmultiaddr *ifma;
|
||||
int error;
|
||||
|
||||
LAGG_WLOCK_ASSERT(sc);
|
||||
|
||||
bzero((char *)&sdl, sizeof(sdl));
|
||||
sdl.sdl_len = sizeof(sdl);
|
||||
sdl.sdl_family = AF_LINK;
|
||||
sdl.sdl_type = IFT_ETHER;
|
||||
sdl.sdl_alen = ETHER_ADDR_LEN;
|
||||
sdl.sdl_index = ifp->if_index;
|
||||
|
||||
if (set) {
|
||||
IF_ADDR_WLOCK(scifp);
|
||||
TAILQ_FOREACH(ifma, &scifp->if_multiaddrs, ifma_link) {
|
||||
if (ifma->ifma_addr->sa_family != AF_LINK)
|
||||
continue;
|
||||
bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
|
||||
LLADDR(&sdl), ETHER_ADDR_LEN);
|
||||
|
||||
error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma);
|
||||
mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT);
|
||||
if (mc == NULL) {
|
||||
IF_ADDR_WUNLOCK(scifp);
|
||||
return (ENOMEM);
|
||||
}
|
||||
bcopy(ifma->ifma_addr, &mc->mc_addr,
|
||||
ifma->ifma_addr->sa_len);
|
||||
mc->mc_addr.sdl_index = ifp->if_index;
|
||||
mc->mc_ifma = NULL;
|
||||
SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries);
|
||||
}
|
||||
IF_ADDR_WUNLOCK(scifp);
|
||||
SLIST_FOREACH (mc, &lp->lp_mc_head, mc_entries) {
|
||||
error = if_addmulti(ifp,
|
||||
(struct sockaddr *)&mc->mc_addr, &mc->mc_ifma);
|
||||
if (error)
|
||||
return (error);
|
||||
mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT);
|
||||
if (mc == NULL)
|
||||
return (ENOMEM);
|
||||
mc->mc_ifma = rifma;
|
||||
SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries);
|
||||
}
|
||||
} else {
|
||||
while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) {
|
||||
SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries);
|
||||
if_delmulti_ifma(mc->mc_ifma);
|
||||
if (mc->mc_ifma && !lp->lp_detaching)
|
||||
if_delmulti_ifma(mc->mc_ifma);
|
||||
free(mc, M_DEVBUF);
|
||||
}
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ struct lagg_lb {
|
||||
};
|
||||
|
||||
struct lagg_mc {
|
||||
struct sockaddr_dl mc_addr;
|
||||
struct ifmultiaddr *mc_ifma;
|
||||
SLIST_ENTRY(lagg_mc) mc_entries;
|
||||
};
|
||||
|
@ -458,48 +458,48 @@ trunk_destroy(struct ifvlantrunk *trunk)
|
||||
* traffic that it doesn't really want, which ends up being discarded
|
||||
* later by the upper protocol layers. Unfortunately, there's no way
|
||||
* to avoid this: there really is only one physical interface.
|
||||
*
|
||||
* XXX: There is a possible race here if more than one thread is
|
||||
* modifying the multicast state of the vlan interface at the same time.
|
||||
*/
|
||||
static int
|
||||
vlan_setmulti(struct ifnet *ifp)
|
||||
{
|
||||
struct ifnet *ifp_p;
|
||||
struct ifmultiaddr *ifma, *rifma = NULL;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifvlan *sc;
|
||||
struct vlan_mc_entry *mc;
|
||||
int error;
|
||||
|
||||
/*VLAN_LOCK_ASSERT();*/
|
||||
|
||||
/* Find the parent. */
|
||||
sc = ifp->if_softc;
|
||||
TRUNK_LOCK_ASSERT(TRUNK(sc));
|
||||
ifp_p = PARENT(sc);
|
||||
|
||||
CURVNET_SET_QUIET(ifp_p->if_vnet);
|
||||
|
||||
/* First, remove any existing filter entries. */
|
||||
while ((mc = SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) {
|
||||
error = if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr);
|
||||
if (error)
|
||||
return (error);
|
||||
SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
|
||||
(void)if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr);
|
||||
free(mc, M_VLAN);
|
||||
}
|
||||
|
||||
/* Now program new ones. */
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
if (ifma->ifma_addr->sa_family != AF_LINK)
|
||||
continue;
|
||||
mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT);
|
||||
if (mc == NULL)
|
||||
if (mc == NULL) {
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
return (ENOMEM);
|
||||
}
|
||||
bcopy(ifma->ifma_addr, &mc->mc_addr, ifma->ifma_addr->sa_len);
|
||||
mc->mc_addr.sdl_index = ifp_p->if_index;
|
||||
SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
|
||||
}
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
SLIST_FOREACH (mc, &sc->vlan_mc_listhead, mc_entries) {
|
||||
error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr,
|
||||
&rifma);
|
||||
NULL);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
@ -1372,7 +1372,7 @@ vlan_unconfig_locked(struct ifnet *ifp, int departing)
|
||||
* Check if we were the last.
|
||||
*/
|
||||
if (trunk->refcnt == 0) {
|
||||
trunk->parent->if_vlantrunk = NULL;
|
||||
parent->if_vlantrunk = NULL;
|
||||
/*
|
||||
* XXXGL: If some ithread has already entered
|
||||
* vlan_input() and is now blocked on the trunk
|
||||
@ -1566,6 +1566,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
struct ifreq *ifr;
|
||||
struct ifaddr *ifa;
|
||||
struct ifvlan *ifv;
|
||||
struct ifvlantrunk *trunk;
|
||||
struct vlanreq vlr;
|
||||
int error = 0;
|
||||
|
||||
@ -1710,8 +1711,12 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
* If we don't have a parent, just remember the membership for
|
||||
* when we do.
|
||||
*/
|
||||
if (TRUNK(ifv) != NULL)
|
||||
trunk = TRUNK(ifv);
|
||||
if (trunk != NULL) {
|
||||
TRUNK_LOCK(trunk);
|
||||
error = vlan_setmulti(ifp);
|
||||
TRUNK_UNLOCK(trunk);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
Loading…
x
Reference in New Issue
Block a user