Fix for PR4008: Destroying a vlan interface with ifconfig that is used by a carp interface crashes whole system carpdetach would get stuck in a loop at splimp() while processing the sadl/AF_LINK address for an interface, this patch makes it skip that - it will be deleted when the carp interface gets destroyed later on. Also fix exposed panics from array underflows when deleting memberships and interfaces being up when they are destroyed. Chris Pascoe 2004/12/06 Index: netinet/ip_carp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.c,v retrieving revision 1.72 diff -u -r1.72 ip_carp.c --- netinet/ip_carp.c 30 Nov 2004 00:17:18 -0000 1.72 +++ netinet/ip_carp.c 6 Dec 2004 06:06:41 -0000 @@ -683,7 +683,8 @@ ifpromisc(sc->sc_ifp, 0); /* Clear IPv4 multicast */ - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); + while (imo->imo_num_memberships) + in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); imo->imo_multicast_ifp = NULL; /* Clear IPv6 multicast */ @@ -697,8 +698,10 @@ } #endif - sc->sc_ifp->if_carp = NULL; - FREE(cif, M_IFADDR); + if (!--cif->vhif_nvrs) { + sc->sc_ifp->if_carp = NULL; + FREE(cif, M_IFADDR); + } } } @@ -714,13 +717,16 @@ void carpdetach(struct carp_softc *sc) { - struct ifaddr *ifa; + struct ifaddr *ifa, *next_ifa; timeout_del(&sc->sc_ad_tmo); timeout_del(&sc->sc_md_tmo); timeout_del(&sc->sc_md6_tmo); - while ((ifa = TAILQ_FIRST(&sc->sc_ac.ac_if.if_addrlist)) != NULL) + for (ifa = TAILQ_FIRST(&sc->sc_ac.ac_if.if_addrlist); + ifa != NULL; ifa = next_ifa) { + next_ifa = TAILQ_NEXT(ifa, ifa_list); + if (ifa->ifa_addr->sa_family == AF_INET) { struct in_ifaddr *ia = ifatoia(ifa); @@ -732,7 +738,13 @@ ifa, ifa_list); TAILQ_REMOVE(&in_ifaddr, ia, ia_list); IFAFREE((&ia->ia_ifa)); - } + } else if (ifa->ifa_ifp == &sc->sc_ac.ac_if && + ifa->ifa_addr->sa_family == AF_LINK) + continue; + else + printf("carpdetach not aware of sa_family %d, " + "expect a panic\n", ifa->ifa_addr->sa_family); + } } /* Detach an interface from the carp. */ @@ -1306,7 +1318,7 @@ return (0); } - /* we have to do it by hands to check we won't match on us */ + /* we have to do this by hand to ensure we don't match on ourselves */ ia_if = NULL; own = 0; for (ia = TAILQ_FIRST(&in_ifaddr); ia; ia = TAILQ_NEXT(ia, ia_list)) { @@ -1404,7 +1416,8 @@ return (0); cleanup: - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); + while (imo->imo_num_memberships) + in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); return (error); } @@ -1420,12 +1433,15 @@ timeout_del(&sc->sc_ad_tmo); sc->sc_ac.ac_if.if_flags &= ~(IFF_UP|IFF_RUNNING); sc->sc_vhid = -1; - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); + while (imo->imo_num_memberships) + in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); imo->imo_multicast_ifp = NULL; TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); if (!--cif->vhif_nvrs) { sc->sc_ifp->if_carp = NULL; FREE(cif, M_IFADDR); + sc->sc_ia = NULL; + sc->sc_ifp = NULL; } } @@ -1453,7 +1469,7 @@ return (0); } - /* we have to do it by hands to check we won't match on us */ + /* we have to do this by hand to ensure we don't match on ourselves */ ia_if = NULL; own = 0; for (ia = in6_ifaddr; ia; ia = ia->ia_next) { int i; @@ -1610,6 +1626,8 @@ if (!--cif->vhif_nvrs) { sc->sc_ifp->if_carp = NULL; FREE(cif, M_IFADDR); + sc->sc_ia6 = NULL; + sc->sc_ifp = NULL; } } Index: net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.93 diff -u -r1.93 if.c --- net/if.c 14 Oct 2004 21:28:15 -0000 1.93 +++ net/if.c 6 Dec 2004 05:17:07 -0000 @@ -678,6 +682,7 @@ { struct if_clone *ifc; struct ifnet *ifp; + int s; ifc = if_clone_lookup(name, NULL); if (ifc == NULL) @@ -690,6 +695,12 @@ if (ifc->ifc_destroy == NULL) return (EOPNOTSUPP); + if (ifp->if_flags & IFF_UP) { + s = splsoftnet(); + if_down(ifp); + splx(s); + } + return ((*ifc->ifc_destroy)(ifp)); }