o Introduce carp_multicast_cleanup(), which removes and frees

multicast addresses from carp interface. [1]
o Rewrite carpdetach(), so that it does the following things: [1]
  - Stops callouts.
  - Decrements carp_suppress_preempt, if needed.
  - Downs interface and sets CARP state to INIT.
  - Calls carp_multicast_cleanup().
  - Detaches softc from carp_if and if we are the last frees
    the carp_if.
o Use new carpdetach() in carp_clone_destroy().
o In carp_ifdetach() acquire the carp_if lock and cleanup all
  interfaces hanging on carp_if. [1]
o Make carp_ifdetach() static and use EVENT(9) to call it
  from if_detach(). [2]
o In carp_setrun() exit if the softc doesn't have a valid pointer
  to parent. [1]

Obtained from:	OpenBSD [1]
Submitted by:	Dan Lukes <dan obluda.cz> [2]
PR:		kern/82908 [2]
This commit is contained in:
glebius 2006-03-21 14:29:48 +00:00
parent 53d6233f74
commit aca7253de4
2 changed files with 107 additions and 91 deletions

View File

@ -209,6 +209,7 @@ static void carp_set_state(struct carp_softc *, int);
static int carp_addrcount(struct carp_if *, struct in_ifaddr *, int);
enum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING };
static void carp_multicast_cleanup(struct carp_softc *);
static int carp_set_addr(struct carp_softc *, struct sockaddr_in *);
static int carp_del_addr(struct carp_softc *, struct sockaddr_in *);
static void carp_carpdev_state_locked(struct carp_if *);
@ -223,6 +224,8 @@ static LIST_HEAD(, carp_softc) carpif_list;
static struct mtx carp_mtx;
IFC_SIMPLE_DECLARE(carp, 0);
static eventhandler_tag if_detach_event_tag;
static __inline u_int16_t
carp_cksum(struct mbuf *m, int len)
{
@ -399,55 +402,12 @@ static void
carp_clone_destroy(struct ifnet *ifp)
{
struct carp_softc *sc = ifp->if_softc;
struct carp_if *cif;
struct ip_moptions *imo = &sc->sc_imo;
#ifdef INET6
struct ip6_moptions *im6o = &sc->sc_im6o;
#endif
/* carpdetach(sc); */
/*
* If an interface is destroyed which is suppressing the preemption,
* decrease the global counter, otherwise the host will never get
* out of the carp supressing state.
*/
if (sc->sc_suppress)
carp_suppress_preempt--;
sc->sc_suppress = 0;
callout_stop(&sc->sc_ad_tmo);
callout_stop(&sc->sc_md_tmo);
callout_stop(&sc->sc_md6_tmo);
if (imo->imo_num_memberships) {
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
imo->imo_multicast_ifp = NULL;
}
#ifdef INET6
while (!LIST_EMPTY(&im6o->im6o_memberships)) {
struct in6_multi_mship *imm =
LIST_FIRST(&im6o->im6o_memberships);
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
}
im6o->im6o_multicast_ifp = NULL;
#endif
/* Remove ourself from parents if_carp queue */
if (sc->sc_carpdev && (cif = sc->sc_carpdev->if_carp)) {
CARP_LOCK(cif);
TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
if (!--cif->vhif_nvrs) {
sc->sc_carpdev->if_carp = NULL;
CARP_LOCK_DESTROY(cif);
FREE(cif, M_CARP);
ifpromisc(sc->sc_carpdev, 0);
sc->sc_carpdev = NULL;
} else {
CARP_UNLOCK(cif);
}
}
if (sc->sc_carpdev)
CARP_SCLOCK(sc);
carpdetach(sc);
if (sc->sc_carpdev)
CARP_SCUNLOCK(sc);
mtx_lock(&carp_mtx);
LIST_REMOVE(sc, sc_next);
@ -458,6 +418,62 @@ carp_clone_destroy(struct ifnet *ifp)
free(sc, M_CARP);
}
static void
carpdetach(struct carp_softc *sc)
{
struct carp_if *cif;
callout_stop(&sc->sc_ad_tmo);
callout_stop(&sc->sc_md_tmo);
callout_stop(&sc->sc_md6_tmo);
if (sc->sc_suppress)
carp_suppress_preempt--;
sc->sc_suppress = 0;
if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS)
carp_suppress_preempt--;
sc->sc_sendad_errors = 0;
carp_set_state(sc, INIT);
SC2IFP(sc)->if_flags &= ~IFF_UP;
carp_setrun(sc, 0);
carp_multicast_cleanup(sc);
if (sc->sc_carpdev != NULL) {
cif = (struct carp_if *)sc->sc_carpdev->if_carp;
CARP_LOCK_ASSERT(cif);
TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
if (!--cif->vhif_nvrs) {
ifpromisc(sc->sc_carpdev, 0);
sc->sc_carpdev->if_carp = NULL;
CARP_LOCK_DESTROY(cif);
FREE(cif, M_IFADDR);
}
}
sc->sc_carpdev = NULL;
}
/* Detach an interface from the carp. */
static void
carp_ifdetach(void *arg __unused, struct ifnet *ifp)
{
struct carp_if *cif = (struct carp_if *)ifp->if_carp;
struct carp_softc *sc, *nextsc;
if (cif == NULL)
return;
/*
* XXX: At the end of for() cycle the lock will be destroyed.
*/
CARP_LOCK(cif);
for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) {
nextsc = TAILQ_NEXT(sc, sc_list);
carpdetach(sc);
}
}
/*
* process input packet.
* we have rearranged checks order compared to the rfc,
@ -751,42 +767,6 @@ carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af)
return;
}
static void
carpdetach(struct carp_softc *sc)
{
struct ifaddr *ifa;
callout_stop(&sc->sc_ad_tmo);
callout_stop(&sc->sc_md_tmo);
callout_stop(&sc->sc_md6_tmo);
while ((ifa = TAILQ_FIRST(&SC2IFP(sc)->if_addrlist)) != NULL)
if (ifa->ifa_addr->sa_family == AF_INET) {
struct in_ifaddr *ia = ifatoia(ifa);
carp_del_addr(sc, &ia->ia_addr);
/* ripped screaming from in_control(SIOCDIFADDR) */
in_ifscrub(SC2IFP(sc), ia);
TAILQ_REMOVE(&SC2IFP(sc)->if_addrlist, ifa, ifa_link);
TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link);
IFAFREE((&ia->ia_ifa));
}
}
/* Detach an interface from the carp. */
void
carp_ifdetach(struct ifnet *ifp)
{
struct carp_softc *sc;
struct carp_if *cif = (struct carp_if *)ifp->if_carp;
CARP_LOCK(cif);
TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list)
carpdetach(sc);
CARP_UNLOCK(cif);
}
static int
carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch)
{
@ -1307,7 +1287,11 @@ carp_setrun(struct carp_softc *sc, sa_family_t af)
{
struct timeval tv;
if (sc->sc_carpdev)
if (sc->sc_carpdev == NULL) {
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
carp_set_state(sc, INIT);
return;
} else
CARP_SCLOCK_ASSERT(sc);
if (SC2IFP(sc)->if_flags & IFF_UP &&
@ -1374,6 +1358,37 @@ carp_setrun(struct carp_softc *sc, sa_family_t af)
}
}
void
carp_multicast_cleanup(struct carp_softc *sc)
{
struct ip_moptions *imo = &sc->sc_imo;
#ifdef INET6
struct ip6_moptions *im6o = &sc->sc_im6o;
#endif
u_int16_t n = imo->imo_num_memberships;
/* Clean up our own multicast memberships */
while (n-- > 0) {
if (imo->imo_membership[n] != NULL) {
in_delmulti(imo->imo_membership[n]);
imo->imo_membership[n] = NULL;
}
}
imo->imo_num_memberships = 0;
imo->imo_multicast_ifp = NULL;
#ifdef INET6
while (!LIST_EMPTY(&im6o->im6o_memberships)) {
struct in6_multi_mship *imm =
LIST_FIRST(&im6o->im6o_memberships);
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
}
im6o->im6o_multicast_ifp = NULL;
#endif
}
static int
carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin)
{
@ -2134,26 +2149,28 @@ carp_sc_state_locked(struct carp_softc *sc)
static int
carp_modevent(module_t mod, int type, void *data)
{
int error = 0;
switch (type) {
case MOD_LOAD:
if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event,
carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
if (if_detach_event_tag == NULL)
return (ENOMEM);
mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF);
LIST_INIT(&carpif_list);
if_clone_attach(&carp_cloner);
break;
case MOD_UNLOAD:
EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag);
if_clone_detach(&carp_cloner);
mtx_destroy(&carp_mtx);
break;
default:
error = EINVAL;
break;
return (EINVAL);
}
return error;
return (0);
}
static moduledata_t carp_mod = {

View File

@ -148,7 +148,6 @@ struct carpreq {
}
#ifdef _KERNEL
void carp_ifdetach (struct ifnet *);
void carp_carpdev_state(void *);
void carp_input (struct mbuf *, int);
int carp6_input (struct mbuf **, int *, int);