From bd0d33586d720a222d806f484f13da68e3369fe1 Mon Sep 17 00:00:00 2001 From: thompsa Date: Thu, 14 Jun 2012 21:36:16 +0000 Subject: [PATCH] MFC r236916 Fix a panic I introduced in r234487, the bridge softc pointer is set to null early in the detach so rearrange things not to explode. Reported by: David Roffiaen, Gustau Perez Querol git-svn-id: svn://svn.freebsd.org/base/stable/8@237104 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sys/net/if_bridge.c | 36 ++++++++++++++++---------- sys/netinet/ip_carp.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index c5be15e62..003e4d13b 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -334,6 +334,7 @@ static int bridge_ip6_checkbasic(struct mbuf **mp); static int bridge_fragment(struct ifnet *, struct mbuf *, struct ether_header *, int, struct llc *); static void bridge_linkstate(struct ifnet *ifp); +static void bridge_linkcheck(struct bridge_softc *sc); extern void (*bridge_linkstate_p)(struct ifnet *ifp); @@ -964,6 +965,7 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); } + bridge_linkcheck(sc); bridge_mutecaps(sc); /* recalcuate now this interface is removed */ bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); KASSERT(bif->bif_addrcnt == 0, @@ -993,7 +995,6 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, bridge_set_ifcap(sc, bif, bif->bif_savedcaps); } bstp_destroy(&bif->bif_stp); /* prepare to free */ - bridge_linkstate(ifs); BRIDGE_LOCK(sc); free(bif, M_DEVBUF); } @@ -1092,18 +1093,17 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) /* Set interface capabilities to the intersection set of all members */ bridge_mutecaps(sc); + bridge_linkcheck(sc); - BRIDGE_UNLOCK(sc); - /* Update the linkstate for the bridge */ - bridge_linkstate(ifs); /* Place the interface into promiscuous mode */ switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: + BRIDGE_UNLOCK(sc); error = ifpromisc(ifs, 1); + BRIDGE_LOCK(sc); break; } - BRIDGE_LOCK(sc); if (error) bridge_delete_member(sc, bif, 0); out: @@ -3485,8 +3485,7 @@ static void bridge_linkstate(struct ifnet *ifp) { struct bridge_softc *sc = ifp->if_bridge; - struct bridge_iflist *bif, *bif2; - int new_link, hasls; + struct bridge_iflist *bif; BRIDGE_LOCK(sc); bif = bridge_lookup_member_if(sc, ifp); @@ -3494,13 +3493,26 @@ bridge_linkstate(struct ifnet *ifp) BRIDGE_UNLOCK(sc); return; } + bridge_linkcheck(sc); + BRIDGE_UNLOCK(sc); + + bstp_linkstate(&bif->bif_stp); +} + +static void +bridge_linkcheck(struct bridge_softc *sc) +{ + struct bridge_iflist *bif; + int new_link, hasls; + + BRIDGE_LOCK_ASSERT(sc); new_link = LINK_STATE_DOWN; hasls = 0; /* Our link is considered up if at least one of our ports is active */ - LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { - if (bif2->bif_ifp->if_capabilities & IFCAP_LINKSTATE) + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE) hasls++; - if (bif2->bif_ifp->if_link_state == LINK_STATE_UP) { + if (bif->bif_ifp->if_link_state == LINK_STATE_UP) { new_link = LINK_STATE_UP; break; } @@ -3510,8 +3522,4 @@ bridge_linkstate(struct ifnet *ifp) new_link = LINK_STATE_UP; } if_link_state_change(sc->sc_ifp, new_link); - BRIDGE_UNLOCK(sc); - - bstp_linkstate(&bif->bif_stp); } - diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index cd92059ac..2a7ffb832 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -100,6 +100,7 @@ struct carp_softc { #ifdef INET6 struct in6_ifaddr *sc_ia6; /* primary iface address v6 */ struct ip6_moptions sc_im6o; + struct in6_ifaddr *sc_llia; #endif /* INET6 */ TAILQ_ENTRY(carp_softc) sc_list; @@ -1239,6 +1240,16 @@ carp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr) cif = ifp->if_carp; CARP_LOCK(cif); TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + if (IN6_ARE_ADDR_EQUAL(taddr, + &vh->sc_llia->ia_addr.sin6_addr) && + (SC2IFP(vh)->if_flags & IFF_UP) && + (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && + vh->sc_state == MASTER) { + ifa = &vh->sc_llia->ia_addr; + ifa_ref(ifa); + CARP_UNLOCK(cif); + return (ifa); + } IF_ADDR_RLOCK(SC2IFP(vh)); TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, ifa_list) { if (IN6_ARE_ADDR_EQUAL(taddr, @@ -1270,6 +1281,28 @@ carp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr) cif = ifp->if_carp; CARP_LOCK(cif); TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { + if (IN6_ARE_ADDR_EQUAL(taddr, + sc->sc_llia->ia_addr.sin6_addr) && + (SC2IFP(sc)->if_flags & IFF_UP) && + (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING) && + sc->sc_state == MASTER) { + struct ifnet *ifp = SC2IFP(sc); + mtag = m_tag_get(PACKET_TAG_CARP, + sizeof(struct ifnet *), M_NOWAIT); + if (mtag == NULL) { + /* better a bit than nothing */ + IF_ADDR_RUNLOCK(SC2IFP(sc)); + CARP_UNLOCK(cif); + return (IF_LLADDR(sc->sc_ifp)); + } + bcopy(&ifp, (caddr_t)(mtag + 1), + sizeof(struct ifnet *)); + m_tag_prepend(m, mtag); + + IF_ADDR_RUNLOCK(SC2IFP(sc)); + CARP_UNLOCK(cif); + return (IF_LLADDR(sc->sc_ifp)); + } IF_ADDR_RLOCK(SC2IFP(sc)); TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { if (IN6_ARE_ADDR_EQUAL(taddr, @@ -1749,6 +1782,33 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) goto cleanup; im6o->im6o_membership[1] = in6m; im6o->im6o_num_memberships++; + + /* Add link local */ + bzero(&ifra, sizeof(ifra)); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_addr.s6_addr32[0] = htonl(0xfe800000); + ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0; + if ((error = in6_get_hw_ifid(sc->sc_ifp, NULL, &ifra.ifra_addr.sin6_addr)) != 0) + goto cleanup; + if ((error = in6_setscope(&ifra.ifra_addr.sin6_addr, ifp, NULL))) + goto cleanup; + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_addr = in6mask64; + /* link-local addresses should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + + if ((error = in6_update_ifa(ifp, &ifra, NULL, + IN6_IFAUPDATE_DADDELAY)) != 0) + goto cleanup; + sc->sc_llia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); + if (sc->sc_llia == NULL) { + error = ESRCH; + goto cleanup; + } + ifa_free(&sc->sc_llia->ia_ifa); } if (!ifp->if_carp) { -- 2.45.0