-/* $OpenBSD: ip_carp.c,v 1.257 2015/05/21 09:17:53 mpi Exp $ */
+/* $OpenBSD: ip_carp.c,v 1.258 2015/06/02 09:38:24 mpi Exp $ */
/*
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
#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;
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);
void
carpdetach(struct carp_softc *sc)
{
+ struct ifnet *ifp;
struct carp_if *cif;
int s;
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);
}
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;
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
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);
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);
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 == NULL) ||
+ (!sc->sc_balancing && vhe->state != MASTER)) {
m_freem(m);
return (ENETUNREACH);
}
+
+ return (sc->sc_carpdev->if_output(ifp, m, sa, rt));
}
void