diff options
author | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2005-11-04 08:11:55 +0000 |
---|---|---|
committer | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2005-11-04 08:11:55 +0000 |
commit | 7f3928b797733aab5c22644458a23ba97b57442e (patch) | |
tree | 4d97c0c76ceb100ed5185292474a3fba96d7dd8b /sys | |
parent | 7e28db5e49a7a6bc415d4c3f964ca65101529142 (diff) |
Add carp_hash() - hash the ethernet address of the ARP request and use
the result to determine which carp interface should answer rather than
simply using the ip address.
Fixes breakage debugged by Matt Bradford <m.bradford@isrc.qut.edu.au>
'just commit' deraadt@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netinet/ip_carp.c | 55 |
1 files changed, 51 insertions, 4 deletions
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 6ff7742497d..a446bb67a43 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_carp.c,v 1.112 2005/10/31 01:40:54 pascoe Exp $ */ +/* $OpenBSD: ip_carp.c,v 1.113 2005/11/04 08:11:54 mcbride Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff. All rights reserved. @@ -136,6 +136,7 @@ struct carp_softc { unsigned char sc_key[CARP_KEY_LEN]; unsigned char sc_pad[CARP_HMAC_PAD]; SHA1_CTX sc_sha1; + u_int32_t sc_hashkey[2]; struct timeout sc_ad_tmo; /* advertisement timeout */ struct timeout sc_md_tmo; /* master down timeout */ @@ -192,6 +193,7 @@ void carp_multicast_cleanup(struct carp_softc *); int carp_set_ifp(struct carp_softc *, struct ifnet *); void carp_set_enaddr(struct carp_softc *); 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 @@ -219,6 +221,8 @@ carp_hmac_prepare(struct carp_softc *sc) { u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; u_int8_t vhid = sc->sc_vhid & 0xff; + SHA1_CTX sha1ctx; + u_int32_t kmd[5]; struct ifaddr *ifa; int i; #ifdef INET6 @@ -236,6 +240,14 @@ carp_hmac_prepare(struct carp_softc *sc) SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version)); SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); + + /* generate a key for the arpbalance hash, before the vhid is hashed */ + bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); + SHA1Final((unsigned char *)kmd, &sha1ctx); + sc->sc_hashkey[0] = kmd[0] ^ kmd[1]; + sc->sc_hashkey[1] = kmd[2] ^ kmd[3]; + + /* the rest of the precomputation */ SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); #ifdef INET TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { @@ -1079,6 +1091,42 @@ carp_send_na(struct carp_softc *sc) } #endif /* INET6 */ +/* + * Based on 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); +} + int carp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) { @@ -1126,9 +1174,8 @@ carp_iamatch(struct in_ifaddr *ia, u_char *src, if (*count == 0) return (0); - /* this should be a hash, like pf_hash() */ - if (ia->ia_addr.sin_addr.s_addr % *count == index - 1 && - sc->sc_state == MASTER) { + if (carp_hash(sc, src) % *count == index - 1 && + sc->sc_state == MASTER) { return (1); } } else { |