Let's take carp(4) out of ether_input(). This is quite similar to what happened to trunk(4) and vlan(4).
I appreciate tests of any kind, reviews and oks. Index: net/if_ethersubr.c =================================================================== RCS file: /cvs/src/sys/net/if_ethersubr.c,v retrieving revision 1.199 diff -u -p -r1.199 if_ethersubr.c --- net/if_ethersubr.c 19 May 2015 11:09:24 -0000 1.199 +++ net/if_ethersubr.c 22 May 2015 13:39:44 -0000 @@ -502,18 +502,6 @@ ether_input(struct mbuf *m, void *hdr) } #endif -#if NCARP > 0 - if (ifp->if_carp) { - if (ifp->if_type != IFT_CARP && (carp_input(ifp, eh, m) == 0)) - return (1); - /* clear mcast if received on a carp IP balanced address */ - else if (ifp->if_type == IFT_CARP && - m->m_flags & (M_BCAST|M_MCAST) && - carp_our_mcastaddr(ifp, (u_int8_t *)&eh->ether_dhost)) - m->m_flags &= ~(M_BCAST|M_MCAST); - } -#endif /* NCARP > 0 */ - ac = (struct arpcom *)ifp; /* Index: netinet/ip_carp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.c,v retrieving revision 1.257 diff -u -p -r1.257 ip_carp.c --- netinet/ip_carp.c 21 May 2015 09:17:53 -0000 1.257 +++ netinet/ip_carp.c 22 May 2015 13:39:44 -0000 @@ -120,6 +120,7 @@ struct carp_softc { #define sc_carpdev sc_ac.ac_if.if_carpdev void *ah_cookie; void *lh_cookie; + struct ifih *sc_ifih; struct ip_moptions sc_imo; #ifdef INET6 struct ip6_moptions sc_im6o; @@ -193,6 +194,7 @@ void carp_hmac_generate(struct carp_vhos unsigned char *, u_int8_t); int carp_hmac_verify(struct carp_vhost_entry *, u_int32_t *, unsigned char *); +int carp_input(struct mbuf *); void carp_proto_input_c(struct mbuf *, struct carp_header *, int, sa_family_t); void carpattach(int); @@ -824,6 +826,7 @@ carp_del_all_timeouts(struct carp_softc void carpdetach(struct carp_softc *sc) { + struct ifnet *ifp; struct carp_if *cif; int s; @@ -839,20 +842,29 @@ carpdetach(struct carp_softc *sc) carp_setrun_all(sc, 0); carp_multicast_cleanup(sc); - s = splnet(); if (sc->ah_cookie != NULL) hook_disestablish(sc->sc_if.if_addrhooks, sc->ah_cookie); - if (sc->sc_carpdev != NULL) { - if (sc->lh_cookie != NULL) - hook_disestablish(sc->sc_carpdev->if_linkstatehooks, - sc->lh_cookie); - cif = (struct carp_if *)sc->sc_carpdev->if_carp; - TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); - if (!--cif->vhif_nvrs) { - ifpromisc(sc->sc_carpdev, 0); - sc->sc_carpdev->if_carp = NULL; - free(cif, M_IFADDR, sizeof(*cif)); - } + + ifp = sc->sc_carpdev; + if (ifp == NULL) + return; + + s = splnet(); + /* Restore previous input handler. */ + if (--sc->sc_ifih->ifih_refcnt == 0) { + SLIST_REMOVE(&ifp->if_inputs, sc->sc_ifih, ifih, ifih_next); + free(sc->sc_ifih, M_DEVBUF, sizeof(*sc->sc_ifih)); + } + + if (sc->lh_cookie != NULL) + hook_disestablish(ifp->if_linkstatehooks, + sc->lh_cookie); + cif = (struct carp_if *)ifp->if_carp; + TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); + if (!--cif->vhif_nvrs) { + ifpromisc(ifp, 0); + ifp->if_carp = NULL; + free(cif, M_IFADDR, sizeof(*cif)); } sc->sc_carpdev = NULL; splx(s); @@ -1403,27 +1415,21 @@ carp_get_srclladdr(struct ifnet *ifp, u_ } int -carp_our_mcastaddr(struct ifnet *ifp, u_int8_t *d_enaddr) -{ - struct carp_softc *sc = ifp->if_softc; - - if (sc->sc_balancing != CARP_BAL_IP) - return (0); - - return (!memcmp(sc->sc_ac.ac_enaddr, d_enaddr, ETHER_ADDR_LEN)); -} - - -int -carp_input(struct ifnet *ifp0, struct ether_header *eh0, struct mbuf *m) +carp_input(struct mbuf *m) { + struct carp_softc *sc; struct ether_header *eh; - struct carp_if *cif = (struct carp_if *)ifp0->if_carp; - struct ifnet *ifp; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct carp_if *cif; + struct ifnet *ifp0, *ifp; - ifp = carp_ourether(cif, eh0->ether_dhost); - if (ifp == NULL && (m->m_flags & (M_BCAST|M_MCAST)) == 0) - return (1); + ifp0 = m->m_pkthdr.rcvif; + eh = mtod(m, struct ether_header *); + cif = (struct carp_if *)ifp0->if_carp; + + ifp = carp_ourether(cif, eh->ether_dhost); + if (ifp == NULL && !ETHER_IS_MULTICAST(eh->ether_dhost)) + return (0); if (ifp == NULL) { struct carp_softc *vh; @@ -1439,41 +1445,33 @@ carp_input(struct ifnet *ifp0, struct et m0 = m_copym2(m, 0, M_COPYALL, M_DONTWAIT); if (m0 == NULL) continue; - M_PREPEND(m0, sizeof(*eh), M_DONTWAIT); - if (m0 == NULL) - continue; - eh = mtod(m0, struct ether_header *); - memmove(eh, eh0, sizeof(*eh)); - m0->m_pkthdr.rcvif = &vh->sc_if; -#if NBPFILTER > 0 - if (vh->sc_if.if_bpf) - bpf_mtap_ether(vh->sc_if.if_bpf, m0, - BPF_DIRECTION_IN); -#endif + ml_init(&ml); + ml_enqueue(&ml, m0); + + if_input(&vh->sc_if, &ml); vh->sc_if.if_ipackets++; - ether_input_mbuf(&vh->sc_if, m0); } - return (1); + return (0); } - M_PREPEND(m, sizeof(*eh), M_DONTWAIT); - if (m == NULL) - return (0); - eh = mtod(m, struct ether_header *); - memmove(eh, eh0, sizeof(*eh)); + ifp0->if_ibytes += m->m_pkthdr.len; - m->m_pkthdr.rcvif = ifp; + /* + * Clear mcast if received on a carp IP balanced address. + */ + sc = ifp->if_softc; + if (sc->sc_balancing == CARP_BAL_IP && + ETHER_IS_MULTICAST(eh->ether_dhost)) + *(eh->ether_dhost) &= ~0x01; -#if NBPFILTER > 0 - if (ifp->if_bpf) - bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_IN); -#endif - ifp->if_ipackets++; - ether_input_mbuf(ifp, m); - return (0); + ml_enqueue(&ml, m); + + if_input(ifp, &ml); + ifp->if_ipackets++; + return (1); } int @@ -1688,6 +1686,18 @@ carp_set_ifp(struct carp_softc *sc, stru return (EINVAL); } + /* Can we share an ifih between multiple carp(4) instances? */ + sc->sc_ifih = SLIST_FIRST(&ifp->if_inputs); + if (sc->sc_ifih->ifih_input != carp_input) { + sc->sc_ifih = malloc(sizeof(*sc->sc_ifih), M_DEVBUF, M_NOWAIT); + if (sc->sc_ifih == NULL) { + free(ncif, M_IFADDR, sizeof(*ncif)); + return (ENOMEM); + } + sc->sc_ifih->ifih_input = carp_input; + sc->sc_ifih->ifih_refcnt = 0; + } + /* detach from old interface */ if (sc->sc_carpdev != NULL) carpdetach(sc); @@ -1720,9 +1730,15 @@ carp_set_ifp(struct carp_softc *sc, stru if (sc->sc_naddrs || sc->sc_naddrs6) sc->sc_if.if_flags |= IFF_UP; carp_set_enaddr(sc); - s = splnet(); + sc->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1, carp_carpdev_state, ifp); + + s = splnet(); + /* Change input handler of the physical interface. */ + if (++sc->sc_ifih->ifih_refcnt == 1) + SLIST_INSERT_HEAD(&ifp->if_inputs, sc->sc_ifih, ifih_next); + carp_carpdev_state(ifp); splx(s); @@ -2261,13 +2277,12 @@ carp_output(struct ifnet *ifp, struct mb vhe = sc->cur_vhe ? sc->cur_vhe : LIST_FIRST(&sc->carp_vhosts); - if (sc->sc_carpdev != NULL && - (sc->sc_balancing || vhe->state == MASTER)) - return (sc->sc_carpdev->if_output(ifp, m, sa, rt)); - else { + if (!sc->sc_carpdev || (!sc->sc_balancing && vhe->state != MASTER)) { m_freem(m); return (ENETUNREACH); } + + return (sc->sc_carpdev->if_output(ifp, m, sa, rt)); } void Index: netinet/ip_carp.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.h,v retrieving revision 1.32 diff -u -p -r1.32 ip_carp.h --- netinet/ip_carp.h 19 Dec 2014 05:36:28 -0000 1.32 +++ netinet/ip_carp.h 22 May 2015 13:39:44 -0000 @@ -172,13 +172,10 @@ int carp_iamatch(struct in_ifaddr *, u u_int8_t **); int carp_iamatch6(struct ifnet *, u_char *, struct sockaddr_dl **); struct ifnet *carp_ourether(void *, u_int8_t *); -int carp_input(struct ifnet *, struct ether_header *, - struct mbuf *); int carp_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); int carp_sysctl(int *, u_int, void *, size_t *, void *, size_t); int carp_lsdrop(struct mbuf *, sa_family_t, u_int32_t *, u_int32_t *); u_char *carp_get_srclladdr(struct ifnet *, u_char *); -int carp_our_mcastaddr(struct ifnet *, u_int8_t *); #endif /* _KERNEL */ #endif /* _NETINET_IP_CARP_H_ */