If stealth balancing is used in setups where the carpdev does not share an IP in the same subnet as carp, ARP queries will be replied through the carp interface. arprequest() (which is also used to form replies) will use the MAC address of the carp interface as the ethernet source address. This will let a switch learn the virtual MAC, and thus break stealth balancing.
This diff extends arprequest() with an optional ether source address argument. Instead of extending the #ifdef maze even further, a new function carp_arprequest() is used to handle all the carp cases. OK? Index: netinet/if_ether.c =================================================================== RCS file: /cvs/src/sys/netinet/if_ether.c,v retrieving revision 1.93 diff -p -u -r1.93 if_ether.c --- netinet/if_ether.c 18 Sep 2011 11:17:58 -0000 1.93 +++ netinet/if_ether.c 17 Oct 2011 21:17:11 -0000 @@ -227,7 +227,7 @@ arp_rtrequest(int req, struct rtentry *r arprequest(rt->rt_ifp, &SIN(rt_key(rt))->sin_addr.s_addr, &SIN(rt_key(rt))->sin_addr.s_addr, - (u_char *)LLADDR(SDL(gate))); + (u_char *)LLADDR(SDL(gate)), NULL); /*FALLTHROUGH*/ case RTM_RESOLVE: if (gate->sa_family != AF_LINK || @@ -322,7 +322,8 @@ arp_rtrequest(int req, struct rtentry *r * - arp header source ethernet address */ void -arprequest(struct ifnet *ifp, u_int32_t *sip, u_int32_t *tip, u_int8_t *enaddr) +arprequest(struct ifnet *ifp, u_int32_t *sip, u_int32_t *tip, u_int8_t *enaddr, + u_int8_t *eshost) { struct mbuf *m; struct ether_header *eh; @@ -346,7 +347,7 @@ arprequest(struct ifnet *ifp, u_int32_t ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */ ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */ ea->arp_op = htons(ARPOP_REQUEST); - bcopy((caddr_t)enaddr, (caddr_t)eh->ether_shost, + bcopy(eshost ? eshost : enaddr, (caddr_t)eh->ether_shost, sizeof(eh->ether_shost)); bcopy((caddr_t)enaddr, (caddr_t)ea->arp_sha, sizeof(ea->arp_sha)); bcopy((caddr_t)sip, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa)); @@ -467,15 +468,14 @@ arpresolve(struct arpcom *ac, struct rte if (la->la_asked == 0 || rt->rt_expire != time_second) { rt->rt_expire = time_second; if (la->la_asked++ < arp_maxtries) +#if NCARP > 0 + carp_arprequest(rt, ac, dst); +#else arprequest(&ac->ac_if, &(SIN(rt->rt_ifa->ifa_addr)->sin_addr.s_addr), - &(SIN(dst)->sin_addr.s_addr), -#if NCARP > 0 - (rt->rt_ifp->if_type == IFT_CARP) ? - ((struct arpcom *) rt->rt_ifp->if_softc - )->ac_enaddr : + &(SIN(dst)->sin_addr.s_addr), ac->ac_enaddr, + NULL); #endif - ac->ac_enaddr); else { rt->rt_flags |= RTF_REJECT; rt->rt_expire += arpt_down; @@ -850,7 +850,7 @@ arp_ifinit(struct arpcom *ac, struct ifa arprequest(&ac->ac_if, &(IA_SIN(ifa)->sin_addr.s_addr), &(IA_SIN(ifa)->sin_addr.s_addr), - ac->ac_enaddr); + ac->ac_enaddr, NULL); ifa->ifa_rtrequest = arp_rtrequest; ifa->ifa_flags |= RTF_CLONING; } Index: netinet/if_ether.h =================================================================== RCS file: /cvs/src/sys/netinet/if_ether.h,v retrieving revision 1.47 diff -p -u -r1.47 if_ether.h --- netinet/if_ether.h 8 Feb 2010 13:32:50 -0000 1.47 +++ netinet/if_ether.h 17 Oct 2011 21:00:55 -0000 @@ -278,7 +278,8 @@ do { \ extern struct ifnet *myip_ifp; -void arprequest(struct ifnet *, u_int32_t *, u_int32_t *, u_int8_t *); +void arprequest(struct ifnet *, u_int32_t *, u_int32_t *, u_int8_t *, + u_int8_t *); void revarpinput(struct mbuf *); void in_revarpinput(struct mbuf *); void revarprequest(struct ifnet *); Index: netinet/ip_carp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.c,v retrieving revision 1.191 diff -p -u -r1.191 ip_carp.c --- netinet/ip_carp.c 16 Oct 2011 21:07:19 -0000 1.191 +++ netinet/ip_carp.c 27 Oct 2011 07:34:29 -0000 @@ -1333,7 +1333,7 @@ carp_send_arp(struct carp_softc *sc) continue; in = ifatoia(ifa)->ia_addr.sin_addr.s_addr; - arprequest(sc->sc_carpdev, &in, &in, sc->sc_ac.ac_enaddr); + arprequest(sc->sc_carpdev, &in, &in, sc->sc_ac.ac_enaddr, NULL); DELAY(1000); /* XXX */ } splx(s); @@ -1462,6 +1462,23 @@ carp_iamatch(struct in_ifaddr *ia, u_cha } return (0); +} + +void +carp_arprequest(struct rtentry *rt, struct arpcom *ac, struct sockaddr *dst) +{ + u_int8_t *enaddr = ac->ac_enaddr; + u_int8_t *eshost = NULL; + + if (rt->rt_ifp->if_type == IFT_CARP) { + struct carp_softc *sc = rt->rt_ifp->if_softc; + enaddr = ((struct arpcom *)rt->rt_ifp->if_softc)->ac_enaddr; + if (sc->sc_balancing == CARP_BAL_IPSTEALTH) + eshost = ac->ac_enaddr; + } + arprequest(&ac->ac_if, + &(satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr), + &(satosin(dst)->sin_addr.s_addr), enaddr, eshost); } #ifdef INET6 Index: netinet/ip_carp.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.h,v retrieving revision 1.28 diff -p -u -r1.28 ip_carp.h --- netinet/ip_carp.h 25 Apr 2010 17:38:53 -0000 1.28 +++ netinet/ip_carp.h 17 Oct 2011 20:58:48 -0000 @@ -179,5 +179,7 @@ int carp_sysctl(int *, u_int, void *, int carp_lsdrop(struct mbuf *, sa_family_t, u_int32_t *, u_int32_t *); void carp_rewrite_lladdr(struct ifnet *, u_int8_t *); int carp_our_mcastaddr(struct ifnet *, u_int8_t *); +void carp_arprequest(struct rtentry *, struct arpcom *, + struct sockaddr *); #endif /* _KERNEL */ #endif /* _NETINET_IP_CARP_H_ */