Turns out that turning arpinput() mpsafe is more complicated than expected because of the way carp(4) has been hooked^Whacked in it.
Depending on the load-balancing mode arpinput() will need to use one Ethernet address or another one. After some discussions here at u2k15 we figured out that ARP load-balancing might no longer be a required feature as its use cases are limited and you'd probably better run relayd(8) nowadays. Getting rid of the ARP load-balancing mode in carp(4) reduce the madness to two cases: either we use the Ethernet address of the parent interface or the one of the carp interface. Does anybody will mourn this removal? Manage will follow if I get oks. Index: netinet/if_ether.c =================================================================== RCS file: /cvs/src/sys/netinet/if_ether.c,v retrieving revision 1.178 diff -u -p -r1.178 if_ether.c --- netinet/if_ether.c 27 Oct 2015 10:54:52 -0000 1.178 +++ netinet/if_ether.c 27 Oct 2015 12:03:11 -0000 @@ -515,7 +515,7 @@ in_arpinput(struct mbuf *m) struct mbuf *mh; u_int8_t *enaddr = NULL; #if NCARP > 0 - u_int8_t *ether_shost = NULL; + uint8_t *ethshost = NULL; #endif char addr[INET_ADDRSTRLEN]; int op, changed = 0; @@ -554,18 +554,11 @@ in_arpinput(struct mbuf *m) if (itaddr.s_addr != ifatoia(ifa)->ia_addr.sin_addr.s_addr) continue; + if (op == ARPOP_REPLY) + break; #if NCARP > 0 - if (ifp->if_type == IFT_CARP && - ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == - (IFF_UP|IFF_RUNNING))) { - if (op == ARPOP_REPLY) - break; - if (carp_iamatch(ifp, ea->arp_sha, - &enaddr, ðer_shost)) - break; - else - goto out; - } + if (ifp->if_type == IFT_CARP && !carp_iamatch(ifp, ðshost)) + goto out; #endif break; } @@ -731,8 +724,8 @@ out: eh = (struct ether_header *)sa.sa_data; memcpy(eh->ether_dhost, ea->arp_tha, sizeof(eh->ether_dhost)); #if NCARP > 0 - if (ether_shost) - enaddr = ether_shost; + if (ethshost) + enaddr = ethshost; #endif memcpy(eh->ether_shost, enaddr, sizeof(eh->ether_shost)); Index: netinet/ip_carp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.c,v retrieving revision 1.278 diff -u -p -r1.278 ip_carp.c --- netinet/ip_carp.c 25 Oct 2015 11:58:11 -0000 1.278 +++ netinet/ip_carp.c 27 Oct 2015 12:11:50 -0000 @@ -236,7 +236,6 @@ int carp_set_ifp(struct carp_softc *, st void carp_set_enaddr(struct carp_softc *); void carp_set_vhe_enaddr(struct carp_vhost_entry *); void carp_addr_updated(void *); -u_int32_t carp_hash(struct carp_softc *, u_char *); int carp_set_addr(struct carp_softc *, struct sockaddr_in *); int carp_join_multicast(struct carp_softc *); #ifdef INET6 @@ -1297,48 +1296,13 @@ carp_send_na(struct carp_softc *sc) } #endif /* INET6 */ -/* - * Originated from bridge_hash() in if_bridge.c - */ -#define mix(a, b, c) do { \ - a -= b; a -= c; a ^= (c >> 13); \ - b -= c; b -= a; b ^= (a << 8); \ - c -= a; c -= b; c ^= (b >> 13); \ - a -= b; a -= c; a ^= (c >> 12); \ - b -= c; b -= a; b ^= (a << 16); \ - c -= a; c -= b; c ^= (b >> 5); \ - a -= b; a -= c; a ^= (c >> 3); \ - b -= c; b -= a; b ^= (a << 10); \ - c -= a; c -= b; c ^= (b >> 15); \ -} while (0) - -u_int32_t -carp_hash(struct carp_softc *sc, u_char *src) -{ - u_int32_t a = 0x9e3779b9, b = sc->sc_hashkey[0], c = sc->sc_hashkey[1]; - - c += sc->sc_key[3] << 24; - c += sc->sc_key[2] << 16; - c += sc->sc_key[1] << 8; - c += sc->sc_key[0]; - b += src[5] << 8; - b += src[4]; - a += src[3] << 24; - a += src[2] << 16; - a += src[1] << 8; - a += src[0]; - - mix(a, b, c); - return (c); -} - void carp_update_lsmask(struct carp_softc *sc) { struct carp_vhost_entry *vhe; int count; - if (!sc->sc_balancing) + if (sc->sc_balancing == CARP_BAL_NONE) return; sc->sc_lsmask = 0; @@ -1355,48 +1319,20 @@ carp_update_lsmask(struct carp_softc *sc } int -carp_iamatch(struct ifnet *ifp, u_char *src, u_int8_t **sha, - u_int8_t **ether_shost) +carp_iamatch(struct ifnet *ifp, uint8_t **ether_shost) { struct carp_softc *sc = ifp->if_softc; struct carp_vhost_entry *vhe = SRPL_FIRST_LOCKED(&sc->carp_vhosts); KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */ - if (sc->sc_balancing == CARP_BAL_ARP) { - int lshash; - /* - * We use the source MAC address to decide which virtual host - * should handle the request. If we're master of that virtual - * host, then we respond, otherwise, just drop the arp packet - * on the floor. - */ - - if (sc->sc_lscount == 0) /* just to be safe */ - return (0); - lshash = carp_hash(sc, src) % sc->sc_lscount; - if ((1 << lshash) & sc->sc_lsmask) { - int i = 0; - SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, - vhost_entries) { - if (i++ == lshash) - break; - } - if (vhe == NULL) - return (0); - *sha = vhe->vhe_enaddr; - return (1); - } - } else if (sc->sc_balancing == CARP_BAL_IPSTEALTH || - sc->sc_balancing == CARP_BAL_IP) { - if (vhe->state == MASTER) { - *ether_shost = ((struct arpcom *)sc->sc_carpdev)-> - ac_enaddr; - return (1); + if (vhe->state == MASTER) { + if (sc->sc_balancing == CARP_BAL_IPSTEALTH || + sc->sc_balancing == CARP_BAL_IP) { + struct arpcom *ac = (struct arpcom *)sc->sc_carpdev; + *ether_shost = ac->ac_enaddr; } - } else { - if (vhe->state == MASTER) - return (1); + return (1); } return (0); @@ -1404,44 +1340,15 @@ carp_iamatch(struct ifnet *ifp, u_char * #ifdef INET6 int -carp_iamatch6(struct ifnet *ifp, u_char *src, struct sockaddr_dl **sdl) +carp_iamatch6(struct ifnet *ifp) { struct carp_softc *sc = ifp->if_softc; struct carp_vhost_entry *vhe = SRPL_FIRST_LOCKED(&sc->carp_vhosts); KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */ - if (sc->sc_balancing == CARP_BAL_ARP) { - int lshash; - /* - * We use the source MAC address to decide which virtual host - * should handle the request. If we're master of that virtual - * host, then we respond, otherwise, just drop the ndp packet - * on the floor. - */ - - /* can happen if optional src lladdr is not provided */ - if (src == NULL) - return (0); - if (sc->sc_lscount == 0) /* just to be safe */ - return (0); - lshash = carp_hash(sc, src) % sc->sc_lscount; - if ((1 << lshash) & sc->sc_lsmask) { - int i = 0; - SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, - vhost_entries) { - if (i++ == lshash) - break; - } - if (vhe == NULL) - return (0); - *sdl = &vhe->vhe_sdl; - return (1); - } - } else { - if (vhe->state == MASTER) - return (1); - } + if (vhe->state == MASTER) + return (1); return (0); } @@ -1460,20 +1367,10 @@ carp_ourether(void *v, u_int8_t *ena) if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) continue; - if (vh->sc_balancing == CARP_BAL_ARP) { - SRPL_FOREACH_LOCKED(vhe, &vh->carp_vhosts, - vhost_entries) - if (vhe->state == MASTER && - !memcmp(ena, vhe->vhe_enaddr, - ETHER_ADDR_LEN)) - return (&vh->sc_if); - } else { - vhe = SRPL_FIRST_LOCKED(&vh->carp_vhosts); - if ((vhe->state == MASTER || - vh->sc_balancing >= CARP_BAL_IP) && - !memcmp(ena, vh->sc_ac.ac_enaddr, ETHER_ADDR_LEN)) - return (&vh->sc_if); - } + vhe = SRPL_FIRST_LOCKED(&vh->carp_vhosts); + if ((vhe->state == MASTER || vh->sc_balancing >= CARP_BAL_IP) && + !memcmp(ena, vh->sc_ac.ac_enaddr, ETHER_ADDR_LEN)) + return (&vh->sc_if); } return (NULL); } @@ -1485,22 +1382,10 @@ carp_vhe_match(struct carp_softc *sc, ui struct srpl_iter i; int match = 0; - if (sc->sc_balancing == CARP_BAL_ARP) { - SRPL_FOREACH(vhe, &sc->carp_vhosts, &i, vhost_entries) { - if (vhe->state == MASTER && - !memcmp(ena, vhe->vhe_enaddr, ETHER_ADDR_LEN)) { - match = 1; - break; - } - } - SRPL_LEAVE(&i, vhe); - } else { - vhe = SRPL_ENTER(&sc->carp_vhosts, &i); /* head */ - match = (vhe->state == MASTER || - sc->sc_balancing >= CARP_BAL_IP) && - !memcmp(ena, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); - SRPL_LEAVE(&i, vhe); - } + vhe = SRPL_ENTER(&sc->carp_vhosts, &i); /* head */ + match = (vhe->state == MASTER || sc->sc_balancing >= CARP_BAL_IP) && + !memcmp(ena, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); + SRPL_LEAVE(&i, vhe); return (match); } @@ -1583,7 +1468,7 @@ carp_lsdrop(struct mbuf *m, sa_family_t KASSERT(ifp != NULL); sc = ifp->if_softc; - if (sc->sc_balancing < CARP_BAL_IP) + if (sc->sc_balancing == CARP_BAL_NONE) goto done; /* * Never drop carp advertisements. @@ -2430,7 +2315,7 @@ carp_output(struct ifnet *ifp, struct mb vhe = sc->cur_vhe ? sc->cur_vhe : SRPL_FIRST_LOCKED(&sc->carp_vhosts); if ((sc->sc_carpdev == NULL) || - (!sc->sc_balancing && vhe->state != MASTER)) { + (sc->sc_balancing == CARP_BAL_NONE && vhe->state != MASTER)) { m_freem(m); return (ENETUNREACH); } Index: netinet/ip_carp.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_carp.h,v retrieving revision 1.35 diff -u -p -r1.35 ip_carp.h --- netinet/ip_carp.h 22 Oct 2015 13:30:29 -0000 1.35 +++ netinet/ip_carp.h 27 Oct 2015 12:01:17 -0000 @@ -132,13 +132,12 @@ struct carpreq { u_int8_t carpr_vhids[CARP_MAXNODES]; u_int8_t carpr_advskews[CARP_MAXNODES]; u_int8_t carpr_states[CARP_MAXNODES]; -#define CARP_BAL_MODES "none", "arp", "ip", "ip-stealth", "ip-unicast" +#define CARP_BAL_MODES "none", "ip", "ip-stealth", "ip-unicast" #define CARP_BAL_NONE 0 -#define CARP_BAL_ARP 1 -#define CARP_BAL_IP 2 -#define CARP_BAL_IPSTEALTH 3 -#define CARP_BAL_IPUNICAST 4 -#define CARP_BAL_MAXID 4 +#define CARP_BAL_IP 1 +#define CARP_BAL_IPSTEALTH 2 +#define CARP_BAL_IPUNICAST 3 +#define CARP_BAL_MAXID 3 u_int8_t carpr_balancing; int carpr_advbase; unsigned char carpr_key[CARP_KEY_LEN]; @@ -168,9 +167,8 @@ void carp_proto_input (struct mbuf *, void carp_carpdev_state(void *); void carp_group_demote_adj(struct ifnet *, int, char *); int carp6_proto_input(struct mbuf **, int *, int); -int carp_iamatch(struct ifnet *, u_char *, u_int8_t **, - u_int8_t **); -int carp_iamatch6(struct ifnet *, u_char *, struct sockaddr_dl **); +int carp_iamatch(struct ifnet *, uint8_t **); +int carp_iamatch6(struct ifnet *); struct ifnet *carp_ourether(void *, u_int8_t *); int carp_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); Index: netinet6/in6.c =================================================================== RCS file: /cvs/src/sys/netinet6/in6.c,v retrieving revision 1.176 diff -u -p -r1.176 in6.c --- netinet6/in6.c 25 Oct 2015 11:58:11 -0000 1.176 +++ netinet6/in6.c 27 Oct 2015 11:50:19 -0000 @@ -1648,9 +1648,6 @@ in6_ifawithscope(struct ifnet *oifp, str struct ifaddr *ifa; struct ifnet *ifp; struct in6_ifaddr *ia6_best = NULL; -#if NCARP > 0 - struct sockaddr_dl *proxydl = NULL; -#endif if (oifp == NULL) { printf("in6_ifawithscope: output interface is not specified\n"); @@ -1670,8 +1667,7 @@ in6_ifawithscope(struct ifnet *oifp, str * Never use a carp address of an interface which is not * the master. */ - if (ifp->if_type == IFT_CARP && - !carp_iamatch6(ifp, NULL, &proxydl)) + if (ifp->if_type == IFT_CARP && !carp_iamatch6(ifp)) continue; #endif Index: netinet6/nd6_nbr.c =================================================================== RCS file: /cvs/src/sys/netinet6/nd6_nbr.c,v retrieving revision 1.97 diff -u -p -r1.97 nd6_nbr.c --- netinet6/nd6_nbr.c 22 Oct 2015 15:37:47 -0000 1.97 +++ netinet6/nd6_nbr.c 27 Oct 2015 11:50:18 -0000 @@ -215,8 +215,7 @@ nd6_ns_input(struct mbuf *m, int off, in /* (1) and (3) check. */ ifa = &in6ifa_ifpwithaddr(ifp, &taddr6)->ia_ifa; #if NCARP > 0 - if (ifp->if_type == IFT_CARP && ifa && - !carp_iamatch6(ifp, lladdr, &proxydl)) + if (ifp->if_type == IFT_CARP && ifa && !carp_iamatch6(ifp)) ifa = NULL; #endif @@ -670,14 +669,11 @@ nd6_na_input(struct mbuf *m, int off, in if (ifa) { #if NCARP > 0 - struct sockaddr_dl *proxydl = NULL; - /* * Ignore NAs silently for carp addresses if we're not * the CARP master. */ - if (ifp->if_type == IFT_CARP && - !carp_iamatch6(ifp, lladdr, &proxydl)) + if (ifp->if_type == IFT_CARP && !carp_iamatch6(ifp)) goto freeit; #endif log(LOG_ERR, @@ -923,9 +919,6 @@ nd6_na_output(struct ifnet *ifp, struct int icmp6len, maxlen, error; caddr_t mac; struct route_in6 ro; -#if NCARP > 0 - struct sockaddr_dl *proxydl = NULL; -#endif mac = NULL; bzero(&ro, sizeof(ro)); @@ -1055,7 +1048,7 @@ nd6_na_output(struct ifnet *ifp, struct #if NCARP > 0 /* Do not send NAs for carp addresses if we're not the CARP master. */ - if (ifp->if_type == IFT_CARP && !carp_iamatch6(ifp, mac, &proxydl)) + if (ifp->if_type == IFT_CARP && !carp_iamatch6(ifp)) goto bad; #endif