diff options
Diffstat (limited to 'sys/netinet/ip_carp.c')
-rw-r--r-- | sys/netinet/ip_carp.c | 717 |
1 files changed, 482 insertions, 235 deletions
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index db6ab897d4a..54006c26ce2 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_carp.c,v 1.154 2007/11/16 05:08:39 djm Exp $ */ +/* $OpenBSD: ip_carp.c,v 1.155 2007/11/22 01:21:40 mpf Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff. All rights reserved. @@ -104,6 +104,27 @@ struct carp_mc_entry { enum { HMAC_ORIG=0, HMAC_NOV6LL=1, HMAC_MAX=2 }; +struct carp_vhost_entry { + LIST_ENTRY(carp_vhost_entry) vhost_entries; + struct carp_softc *parent_sc; + int master; + int vhid; + int advskew; + enum { INIT = 0, BACKUP, MASTER } state; + struct timeout ad_tmo; /* advertisement timeout */ + struct timeout md_tmo; /* master down timeout */ + struct timeout md6_tmo; /* master down timeout */ + + u_int64_t vhe_replay_cookie; + + /* authentication */ +#define CARP_HMAC_PAD 64 + unsigned char vhe_pad[CARP_HMAC_PAD]; + SHA1_CTX vhe_sha1[HMAC_MAX]; + + u_int8_t vhe_enaddr[ETHER_ADDR_LEN]; +}; + struct carp_softc { struct arpcom sc_ac; #define sc_if sc_ac.ac_if @@ -116,41 +137,36 @@ struct carp_softc { #endif /* INET6 */ TAILQ_ENTRY(carp_softc) sc_list; - enum { INIT = 0, BACKUP, MASTER } sc_state; - int sc_suppress; int sc_bow_out; int sc_sendad_errors; -#define CARP_SENDAD_MAX_ERRORS 3 +#define CARP_SENDAD_MAX_ERRORS(sc) (3 * (sc)->sc_vhe_count) int sc_sendad_success; -#define CARP_SENDAD_MIN_SUCCESS 3 +#define CARP_SENDAD_MIN_SUCCESS(sc) (3 * (sc)->sc_vhe_count) - char sc_carplladdr[ETHER_ADDR_LEN]; char sc_curlladdr[ETHER_ADDR_LEN]; - int sc_vhid; - int sc_advskew; + + LIST_HEAD(__carp_vhosthead, carp_vhost_entry) carp_vhosts; + int sc_vhe_count; + u_int8_t sc_vhids[CARP_MAXNODES]; + u_int8_t sc_advskews[CARP_MAXNODES]; + int sc_naddrs; int sc_naddrs6; int sc_advbase; /* seconds */ - u_int64_t sc_replay_cookie; /* authentication */ -#define CARP_HMAC_PAD 64 unsigned char sc_key[CARP_KEY_LEN]; - unsigned char sc_pad[CARP_HMAC_PAD]; - SHA1_CTX sc_sha1[HMAC_MAX]; u_int32_t sc_hashkey[2]; u_int32_t sc_lsmask; /* load sharing mask */ int sc_lscount; /* # load sharing interfaces (max 32) */ - struct timeout sc_ad_tmo; /* advertisement timeout */ - struct timeout sc_md_tmo; /* master down timeout */ - struct timeout sc_md6_tmo; /* master down timeout */ int sc_delayed_arp; /* delayed ARP request countdown */ LIST_HEAD(__carp_mchead, carp_mc_entry) carp_mc_listhead; + struct carp_vhost_entry *cur_vhe; /* current active vhe */ }; int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 0, 0 }; /* XXX for now */ @@ -175,32 +191,39 @@ struct carp_if { } void carp_hmac_prepare(struct carp_softc *); -void carp_hmac_prepare_ctx(struct carp_softc *, u_int8_t); -void carp_hmac_generate(struct carp_softc *, u_int32_t *, +void carp_hmac_prepare_ctx(struct carp_vhost_entry *, u_int8_t); +void carp_hmac_generate(struct carp_vhost_entry *, u_int32_t *, unsigned char *, u_int8_t); -int carp_hmac_verify(struct carp_softc *, u_int32_t *, +int carp_hmac_verify(struct carp_vhost_entry *, u_int32_t *, unsigned char *); void carp_setroute(struct carp_softc *, int); void carp_proto_input_c(struct mbuf *, struct carp_header *, sa_family_t); void carpattach(int); void carpdetach(struct carp_softc *); -int carp_prepare_ad(struct mbuf *, struct carp_softc *, +int carp_prepare_ad(struct mbuf *, struct carp_vhost_entry *, struct carp_header *); void carp_send_ad_all(void); +void carp_vhe_send_ad_all(struct carp_softc *); void carp_send_ad(void *); void carp_send_arp(struct carp_softc *); void carp_master_down(void *); int carp_ioctl(struct ifnet *, u_long, caddr_t); +int carp_vhids_ioctl(struct carp_softc *, struct carpreq); +int carp_check_dup_vhids(struct carp_softc *, struct carp_if *, + struct carpreq *); void carp_ifgroup_ioctl(struct ifnet *, u_long, caddr_t); void carp_ifgattr_ioctl(struct ifnet *, u_long, caddr_t); void carp_start(struct ifnet *); -void carp_setrun(struct carp_softc *, sa_family_t); -void carp_set_state(struct carp_softc *, int); +void carp_setrun_all(struct carp_softc *, sa_family_t); +void carp_setrun(struct carp_vhost_entry *, sa_family_t); +void carp_set_state_all(struct carp_softc *, int); +void carp_set_state(struct carp_vhost_entry *, int); int carp_addrcount(struct carp_if *, struct ifaddr *, int); enum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING, CARP_COUNT_LINK0 }; 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_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 *); @@ -217,6 +240,9 @@ int carp_ether_delmulti(struct carp_softc *, struct ifreq *); void carp_ether_purgemulti(struct carp_softc *); int carp_group_demote_count(struct carp_softc *); void carp_update_lsmask(struct carp_softc *); +int carp_new_vhost(struct carp_softc *, int, int); +void carp_destroy_vhosts(struct carp_softc *); +void carp_del_all_timeouts(struct carp_softc *); struct if_clone carp_cloner = IF_CLONE_INITIALIZER("carp", carp_clone_create, carp_clone_destroy); @@ -226,17 +252,23 @@ struct if_clone carp_cloner = void carp_hmac_prepare(struct carp_softc *sc) { + struct carp_vhost_entry *vhe; u_int8_t i; - for (i=0; i < HMAC_MAX; i++) - carp_hmac_prepare_ctx(sc, i); + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { + for (i = 0; i < HMAC_MAX; i++) { + carp_hmac_prepare_ctx(vhe, i); + } + } } void -carp_hmac_prepare_ctx(struct carp_softc *sc, u_int8_t ctx) +carp_hmac_prepare_ctx(struct carp_vhost_entry *vhe, u_int8_t ctx) { + struct carp_softc *sc = vhe->parent_sc; + u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; - u_int8_t vhid = sc->sc_vhid & 0xff; + u_int8_t vhid = vhe->vhid & 0xff; SHA1_CTX sha1ctx; u_int32_t kmd[5]; struct ifaddr *ifa; @@ -247,29 +279,32 @@ carp_hmac_prepare_ctx(struct carp_softc *sc, u_int8_t ctx) #endif /* INET6 */ /* compute ipad from key */ - bzero(sc->sc_pad, sizeof(sc->sc_pad)); - bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); - for (i = 0; i < sizeof(sc->sc_pad); i++) - sc->sc_pad[i] ^= 0x36; + bzero(vhe->vhe_pad, sizeof(vhe->vhe_pad)); + bcopy(sc->sc_key, vhe->vhe_pad, sizeof(sc->sc_key)); + for (i = 0; i < sizeof(vhe->vhe_pad); i++) + vhe->vhe_pad[i] ^= 0x36; /* precompute first part of inner hash */ - SHA1Init(&sc->sc_sha1[ctx]); - SHA1Update(&sc->sc_sha1[ctx], sc->sc_pad, sizeof(sc->sc_pad)); - SHA1Update(&sc->sc_sha1[ctx], (void *)&version, sizeof(version)); - SHA1Update(&sc->sc_sha1[ctx], (void *)&type, sizeof(type)); + SHA1Init(&vhe->vhe_sha1[ctx]); + SHA1Update(&vhe->vhe_sha1[ctx], vhe->vhe_pad, sizeof(vhe->vhe_pad)); + SHA1Update(&vhe->vhe_sha1[ctx], (void *)&version, sizeof(version)); + SHA1Update(&vhe->vhe_sha1[ctx], (void *)&type, sizeof(type)); /* generate a key for the arpbalance hash, before the vhid is hashed */ - bcopy(&sc->sc_sha1[ctx], &sha1ctx, sizeof(sha1ctx)); - SHA1Final((unsigned char *)kmd, &sha1ctx); - sc->sc_hashkey[0] = kmd[0] ^ kmd[1]; - sc->sc_hashkey[1] = kmd[2] ^ kmd[3]; + if (vhe->master) { + bcopy(&vhe->vhe_sha1[ctx], &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 */ - if (bcmp(sc->sc_ac.ac_enaddr, sc->sc_carplladdr, ETHER_ADDR_LEN) != 0) - SHA1Update(&sc->sc_sha1[ctx], sc->sc_ac.ac_enaddr, + if (vhe->master && bcmp(sc->sc_ac.ac_enaddr, vhe->vhe_enaddr, + ETHER_ADDR_LEN) != 0) + SHA1Update(&vhe->vhe_sha1[ctx], sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); - SHA1Update(&sc->sc_sha1[ctx], (void *)&vhid, sizeof(vhid)); + SHA1Update(&vhe->vhe_sha1[ctx], (void *)&vhid, sizeof(vhid)); /* Hash the addresses from smallest to largest, not interface order */ #ifdef INET @@ -288,7 +323,7 @@ carp_hmac_prepare_ctx(struct carp_softc *sc, u_int8_t ctx) } } if (found) - SHA1Update(&sc->sc_sha1[ctx], + SHA1Update(&vhe->vhe_sha1[ctx], (void *)&cur, sizeof(cur)); } while (found); #endif /* INET */ @@ -313,44 +348,44 @@ carp_hmac_prepare_ctx(struct carp_softc *sc, u_int8_t ctx) } } if (found) - SHA1Update(&sc->sc_sha1[ctx], + SHA1Update(&vhe->vhe_sha1[ctx], (void *)&cur6, sizeof(cur6)); } while (found); #endif /* INET6 */ /* convert ipad to opad */ - for (i = 0; i < sizeof(sc->sc_pad); i++) - sc->sc_pad[i] ^= 0x36 ^ 0x5c; + for (i = 0; i < sizeof(vhe->vhe_pad); i++) + vhe->vhe_pad[i] ^= 0x36 ^ 0x5c; } void -carp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], +carp_hmac_generate(struct carp_vhost_entry *vhe, u_int32_t counter[2], unsigned char md[20], u_int8_t ctx) { SHA1_CTX sha1ctx; /* fetch first half of inner hash */ - bcopy(&sc->sc_sha1[ctx], &sha1ctx, sizeof(sha1ctx)); + bcopy(&vhe->vhe_sha1[ctx], &sha1ctx, sizeof(sha1ctx)); - SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_replay_cookie)); + SHA1Update(&sha1ctx, (void *)counter, sizeof(vhe->vhe_replay_cookie)); SHA1Final(md, &sha1ctx); /* outer hash */ SHA1Init(&sha1ctx); - SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); + SHA1Update(&sha1ctx, vhe->vhe_pad, sizeof(vhe->vhe_pad)); SHA1Update(&sha1ctx, md, 20); SHA1Final(md, &sha1ctx); } int -carp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], +carp_hmac_verify(struct carp_vhost_entry *vhe, u_int32_t counter[2], unsigned char md[20]) { unsigned char md2[20]; u_int8_t i; - for (i=0; i < HMAC_MAX; i++) { - carp_hmac_generate(sc, counter, md2, i); + for (i = 0; i < HMAC_MAX; i++) { + carp_hmac_generate(vhe, counter, md2, i); if (!bcmp(md, md2, sizeof(md2))) return (0); } @@ -636,12 +671,17 @@ void carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) { struct carp_softc *sc; + struct carp_vhost_entry *vhe; struct timeval sc_tv, ch_tv; TAILQ_FOREACH(sc, &((struct carp_if *) - m->m_pkthdr.rcvif->if_carpdev->if_carp)->vhif_vrs, sc_list) - if (sc->sc_vhid == ch->carp_vhid) - break; + m->m_pkthdr.rcvif->if_carpdev->if_carp)->vhif_vrs, sc_list) { + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { + if (vhe->vhid == ch->carp_vhid) + goto found; + } + } + found: if (!sc || (sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { @@ -665,7 +705,7 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) } /* verify the hash */ - if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { + if (carp_hmac_verify(vhe, ch->carp_counter, ch->carp_md)) { carpstats.carps_badauth++; sc->sc_if.if_ierrors++; CARP_LOG(sc, ("incorrect hash")); @@ -673,7 +713,7 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) return; } - if (!bcmp(&sc->sc_replay_cookie, ch->carp_counter, + if (!bcmp(&vhe->vhe_replay_cookie, ch->carp_counter, sizeof(ch->carp_counter))) { /* Do not log duplicates from non simplex interfaces */ if (sc->sc_carpdev->if_flags & IFF_SIMPLEX) { @@ -686,14 +726,14 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) } sc_tv.tv_sec = sc->sc_advbase; - if (carp_group_demote_count(sc) && sc->sc_advskew < 240) + if (carp_group_demote_count(sc) && vhe->advskew < 240) sc_tv.tv_usec = 240 * 1000000 / 256; else - sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; + sc_tv.tv_usec = vhe->advskew * 1000000 / 256; ch_tv.tv_sec = ch->carp_advbase; ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; - switch (sc->sc_state) { + switch (vhe->state) { case INIT: break; case MASTER: @@ -705,10 +745,11 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) (timercmp(&sc_tv, &ch_tv, ==) && ch->carp_demote <= (carp_group_demote_count(sc) & 0xff))) { - timeout_del(&sc->sc_ad_tmo); - carp_set_state(sc, BACKUP); - carp_setrun(sc, 0); - carp_setroute(sc, RTM_DELETE); + timeout_del(&vhe->ad_tmo); + carp_set_state(vhe, BACKUP); + carp_setrun(vhe, 0); + if (vhe->master) + carp_setroute(sc, RTM_DELETE); } break; case BACKUP: @@ -717,7 +758,7 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) * and this one claims to be slower, treat him as down. */ if (carp_opts[CARPCTL_PREEMPT] && timercmp(&sc_tv, &ch_tv, <)) { - carp_master_down(sc); + carp_master_down(vhe); break; } @@ -726,7 +767,7 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) * regardless of CARPCTL_PREEMPT. */ if (ch->carp_demote > (carp_group_demote_count(sc) & 0xff)) { - carp_master_down(sc); + carp_master_down(vhe); break; } @@ -737,7 +778,7 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) */ sc_tv.tv_sec = sc->sc_advbase * 3; if (timercmp(&sc_tv, &ch_tv, <)) { - carp_master_down(sc); + carp_master_down(vhe); break; } @@ -745,7 +786,7 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) * Otherwise, we reset the counter and wait for the next * advertisement. */ - carp_setrun(sc, af); + carp_setrun(vhe, af); break; } @@ -795,10 +836,15 @@ carp_clone_create(ifc, unit) return (ENOMEM); bzero(sc, sizeof(*sc)); + LIST_INIT(&sc->carp_vhosts); + sc->sc_vhe_count = 0; + if (carp_new_vhost(sc, 0, 0)) { + free(sc, M_DEVBUF); + return (ENOMEM); + } + sc->sc_suppress = 0; sc->sc_advbase = CARP_DFLTINTV; - sc->sc_vhid = -1; /* required setting */ - sc->sc_advskew = 0; sc->sc_naddrs = sc->sc_naddrs6 = 0; #ifdef INET6 sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; @@ -808,10 +854,6 @@ carp_clone_create(ifc, unit) M_WAITOK|M_ZERO); sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; - timeout_set(&sc->sc_ad_tmo, carp_send_ad, sc); - timeout_set(&sc->sc_md_tmo, carp_master_down, sc); - timeout_set(&sc->sc_md6_tmo, carp_master_down, sc); - LIST_INIT(&sc->carp_mc_listhead); ifp = &sc->sc_if; ifp->if_softc = sc; @@ -838,6 +880,41 @@ carp_clone_create(ifc, unit) } int +carp_new_vhost(struct carp_softc *sc, int vhid, int advskew) +{ + struct carp_vhost_entry *vhe, *vhe0; + + MALLOC(vhe, struct carp_vhost_entry *, sizeof(*vhe), + M_DEVBUF, M_NOWAIT); + if (vhe == NULL) + return (ENOMEM); + bzero(vhe, sizeof(*vhe)); + + vhe->parent_sc = sc; + vhe->vhid = vhid; + vhe->advskew = advskew; + timeout_set(&vhe->ad_tmo, carp_send_ad, vhe); + timeout_set(&vhe->md_tmo, carp_master_down, vhe); + timeout_set(&vhe->md6_tmo, carp_master_down, vhe); + + /* mark the first vhe as master */ + if (LIST_EMPTY(&sc->carp_vhosts)) { + vhe->master = 1; + LIST_INSERT_HEAD(&sc->carp_vhosts, vhe, vhost_entries); + sc->sc_vhe_count = 1; + return (0); + } + + LIST_FOREACH(vhe0, &sc->carp_vhosts, vhost_entries) + if (LIST_NEXT(vhe0, vhost_entries) == NULL) + break; + LIST_INSERT_AFTER(vhe0, vhe, vhost_entries); + sc->sc_vhe_count++; + + return (0); +} + +int carp_clone_destroy(struct ifnet *ifp) { struct carp_softc *sc = ifp->if_softc; @@ -845,6 +922,7 @@ carp_clone_destroy(struct ifnet *ifp) carpdetach(sc); ether_ifdetach(ifp); if_detach(ifp); + carp_destroy_vhosts(ifp->if_softc); free(sc->sc_imo.imo_membership, M_IPMOPTS); free(sc, M_DEVBUF); @@ -852,26 +930,36 @@ carp_clone_destroy(struct ifnet *ifp) } void +carp_del_all_timeouts(struct carp_softc *sc) +{ + struct carp_vhost_entry *vhe; + + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { + timeout_del(&vhe->ad_tmo); + timeout_del(&vhe->md_tmo); + timeout_del(&vhe->md6_tmo); + } +} + +void carpdetach(struct carp_softc *sc) { struct carp_if *cif; int s; - timeout_del(&sc->sc_ad_tmo); - timeout_del(&sc->sc_md_tmo); - timeout_del(&sc->sc_md6_tmo); + carp_del_all_timeouts(sc); if (sc->sc_suppress) carp_group_demote_adj(&sc->sc_if, -1); sc->sc_suppress = 0; - if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) + if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS(sc)) carp_group_demote_adj(&sc->sc_if, -1); sc->sc_sendad_errors = 0; - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); sc->sc_if.if_flags &= ~IFF_UP; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); carp_multicast_cleanup(sc); s = splnet(); @@ -904,22 +992,38 @@ carp_ifdetach(struct ifnet *ifp) } } +void +carp_destroy_vhosts(struct carp_softc *sc) +{ + /* XXX bow out? */ + struct carp_vhost_entry *vhe, *nvhe; + + for (vhe = LIST_FIRST(&sc->carp_vhosts); + vhe != LIST_END(&sc->carp_vhosts); vhe = nvhe) { + nvhe = LIST_NEXT(vhe, vhost_entries); + free(vhe, M_DEVBUF); + } + LIST_INIT(&sc->carp_vhosts); + sc->sc_vhe_count = 0; +} + int -carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) +carp_prepare_ad(struct mbuf *m, struct carp_vhost_entry *vhe, + struct carp_header *ch) { - if (!sc->sc_replay_cookie) { - arc4random_bytes(&sc->sc_replay_cookie, - sizeof(sc->sc_replay_cookie)); + if (!vhe->vhe_replay_cookie) { + arc4random_bytes(&vhe->vhe_replay_cookie, + sizeof(vhe->vhe_replay_cookie)); } - bcopy(&sc->sc_replay_cookie, ch->carp_counter, + bcopy(&vhe->vhe_replay_cookie, ch->carp_counter, sizeof(ch->carp_counter)); /* * For the time being, do not include the IPv6 linklayer addresses * in the HMAC. */ - carp_hmac_generate(sc, ch->carp_counter, ch->carp_md, HMAC_NOV6LL); + carp_hmac_generate(vhe, ch->carp_counter, ch->carp_md, HMAC_NOV6LL); return (0); } @@ -938,20 +1042,33 @@ carp_send_ad_all(void) cif = (struct carp_if *)ifp->if_carp; TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == - (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER) - carp_send_ad(vh); + (IFF_UP|IFF_RUNNING)) { + carp_vhe_send_ad_all(vh); + } } } } +void +carp_vhe_send_ad_all(struct carp_softc *sc) +{ + struct carp_vhost_entry *vhe; + + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { + if (vhe->state == MASTER) + carp_send_ad(vhe); + } +} void carp_send_ad(void *v) { struct carp_header ch; struct timeval tv; - struct carp_softc *sc = v; + struct carp_vhost_entry *vhe = v; + struct carp_softc *sc = vhe->parent_sc; struct carp_header *ch_ptr; + struct mbuf *m; int error, len, advbase, advskew, s; struct ifaddr *ifa; @@ -966,13 +1083,12 @@ carp_send_ad(void *v) /* bow out if we've gone to backup (the carp interface is going down) */ if (sc->sc_bow_out) { - sc->sc_bow_out = 0; advbase = 255; advskew = 255; } else { advbase = sc->sc_advbase; - if (!carp_group_demote_count(sc) || sc->sc_advskew > 240) - advskew = sc->sc_advskew; + if (!carp_group_demote_count(sc) || vhe->advskew > 240) + advskew = vhe->advskew; else advskew = 240; tv.tv_sec = advbase; @@ -981,13 +1097,14 @@ carp_send_ad(void *v) ch.carp_version = CARP_VERSION; ch.carp_type = CARP_ADVERTISEMENT; - ch.carp_vhid = sc->sc_vhid; + ch.carp_vhid = vhe->vhid; ch.carp_demote = carp_group_demote_count(sc) & 0xff; ch.carp_advbase = advbase; ch.carp_advskew = advskew; ch.carp_authlen = 7; /* XXX DEFINE */ ch.carp_cksum = 0; + sc->cur_vhe = vhe; /* we need the vhe later on the output path */ #ifdef INET if (sc->sc_naddrs) { @@ -1029,7 +1146,7 @@ carp_send_ad(void *v) ch_ptr = (void *)ip + sizeof(*ip); bcopy(&ch, ch_ptr, sizeof(ch)); - if (carp_prepare_ad(m, sc, ch_ptr)) + if (carp_prepare_ad(m, vhe, ch_ptr)) goto retry_later; m->m_data += sizeof(*ip); @@ -1051,24 +1168,26 @@ carp_send_ad(void *v) sc->sc_if.if_oerrors++; if (sc->sc_sendad_errors < INT_MAX) sc->sc_sendad_errors++; - if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) + if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS(sc)) carp_group_demote_adj(&sc->sc_if, 1); sc->sc_sendad_success = 0; } else { - if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { + if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS(sc)) { if (++sc->sc_sendad_success >= - CARP_SENDAD_MIN_SUCCESS) { + CARP_SENDAD_MIN_SUCCESS(sc)) { carp_group_demote_adj(&sc->sc_if, -1); sc->sc_sendad_errors = 0; } } else sc->sc_sendad_errors = 0; } - if (sc->sc_delayed_arp > 0) - sc->sc_delayed_arp--; - if (sc->sc_delayed_arp == 0) { - carp_send_arp(sc); - sc->sc_delayed_arp = -1; + if (vhe->master) { + if (sc->sc_delayed_arp > 0) + sc->sc_delayed_arp--; + if (sc->sc_delayed_arp == 0) { + carp_send_arp(sc); + sc->sc_delayed_arp = -1; + } } } #endif /* INET */ @@ -1112,7 +1231,7 @@ carp_send_ad(void *v) ch_ptr = (void *)ip6 + sizeof(*ip6); bcopy(&ch, ch_ptr, sizeof(ch)); - if (carp_prepare_ad(m, sc, ch_ptr)) + if (carp_prepare_ad(m, vhe, ch_ptr)) goto retry_later; m->m_data += sizeof(*ip6); @@ -1133,13 +1252,13 @@ carp_send_ad(void *v) sc->sc_if.if_oerrors++; if (sc->sc_sendad_errors < INT_MAX) sc->sc_sendad_errors++; - if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) + if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS(sc)) carp_group_demote_adj(&sc->sc_if, 1); sc->sc_sendad_success = 0; } else { - if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { + if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS(sc)) { if (++sc->sc_sendad_success >= - CARP_SENDAD_MIN_SUCCESS) { + CARP_SENDAD_MIN_SUCCESS(sc)) { carp_group_demote_adj(&sc->sc_if, -1); sc->sc_sendad_errors = 0; } @@ -1150,9 +1269,10 @@ carp_send_ad(void *v) #endif /* INET6 */ retry_later: + sc->cur_vhe = NULL; splx(s); if (advbase != 255 || advskew != 255) - timeout_add(&sc->sc_ad_tmo, tvtohz(&tv)); + timeout_add(&vhe->ad_tmo, tvtohz(&tv)); } /* @@ -1257,7 +1377,7 @@ carp_addrcount(struct carp_if *cif, struct ifaddr *ifa0, int type) continue; break; case CARP_COUNT_MASTER: - if (vh->sc_state != MASTER) + if (LIST_FIRST(&vh->carp_vhosts)->state != MASTER) continue; break; case CARP_COUNT_LINK0: @@ -1289,6 +1409,7 @@ carp_update_lsmask(struct carp_softc *sc) { struct carp_softc *curvh, *vh, *sc0 = NULL; struct carp_if *cif; + struct carp_vhost_entry *vhe; struct ifaddr *ifa, *ifa0 = NULL; int cur, last, count, found; @@ -1333,6 +1454,7 @@ carp_update_lsmask(struct carp_softc *sc) last = cur; cur = 255; TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + vhe = LIST_FIRST(&vh->carp_vhosts); if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) continue; @@ -1349,14 +1471,14 @@ carp_update_lsmask(struct carp_softc *sc) break; #endif } - if (ifa && vh->sc_vhid > last && vh->sc_vhid < cur) { - cur = vh->sc_vhid; + if (ifa && vhe->vhid > last && vhe->vhid < cur) { + cur = vhe->vhid; curvh = vh; found++; } } if (found) { - if (curvh->sc_state == MASTER && + if (LIST_FIRST(&curvh->carp_vhosts)->state == MASTER && count < sizeof(sc0->sc_lsmask) * 8) sc0->sc_lsmask |= 1 << count; count++; @@ -1375,6 +1497,7 @@ carp_iamatch(struct in_ifaddr *ia, u_char *src, u_int32_t *count, u_int32_t index) { struct carp_softc *sc = ia->ia_ifp->if_softc; + struct carp_vhost_entry *vhe = LIST_FIRST(&sc->carp_vhosts); /* * If the asked address is found on a LINK0 interface @@ -1404,11 +1527,11 @@ carp_iamatch(struct in_ifaddr *ia, u_char *src, return (0); if (carp_hash(sc, src) % *count == index - 1 && - sc->sc_state == MASTER) { + LIST_FIRST(&sc->carp_vhosts)->state == MASTER) { return (1); } } else { - if (sc->sc_state == MASTER) + if (vhe->state == MASTER) return (1); } @@ -1430,7 +1553,7 @@ carp_iamatch6(struct ifnet *ifp, struct ifaddr *ifa) ifa, CARP_COUNT_LINK0)) return (0); - if (sc->sc_state == MASTER) + if (LIST_FIRST(&sc->carp_vhosts)->state == MASTER) return (1); return (0); @@ -1450,16 +1573,33 @@ carp_ourether(void *v, struct ether_header *eh, u_char iftype, int src) ena = (u_int8_t *)&eh->ether_dhost; TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + /* XXX need to check all vhes */ + struct carp_vhost_entry *vhe = LIST_FIRST(&vh->carp_vhosts); if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) continue; - if ((vh->sc_state == MASTER || vh->sc_if.if_flags & IFF_LINK0) + if ((vhe->state == MASTER || vh->sc_if.if_flags & IFF_LINK0) && !bcmp(ena, vh->sc_ac.ac_enaddr, ETHER_ADDR_LEN)) return (&vh->sc_if); } return (NULL); } +void +carp_rewrite_lladdr(struct ifnet *ifp, u_int8_t *s_enaddr) +{ + struct carp_softc *sc = ifp->if_softc; + + if (!(ifp->if_flags & IFF_LINK1) && sc->cur_vhe) { + if (sc->cur_vhe->master) + bcopy((caddr_t)sc->sc_ac.ac_enaddr, + (caddr_t)s_enaddr, ETHER_ADDR_LEN); + else + bcopy((caddr_t)sc->cur_vhe->vhe_enaddr, + (caddr_t)s_enaddr, ETHER_ADDR_LEN); + } +} + int carp_input(struct mbuf *m, u_int8_t *shost, u_int8_t *dhost, u_int16_t etype) { @@ -1539,9 +1679,10 @@ carp_lsdrop(struct mbuf *m, sa_family_t af, u_int32_t *src, u_int32_t *dst) void carp_master_down(void *v) { - struct carp_softc *sc = v; + struct carp_vhost_entry *vhe = v; + struct carp_softc *sc = vhe->parent_sc; - switch (sc->sc_state) { + switch (vhe->state) { case INIT: printf("%s: master_down event in INIT state\n", sc->sc_if.if_xname); @@ -1549,79 +1690,95 @@ carp_master_down(void *v) case MASTER: break; case BACKUP: - carp_set_state(sc, MASTER); - carp_send_ad(sc); - carp_send_arp(sc); - /* Schedule a delayed ARP request to deal w/ some L3 switches */ - sc->sc_delayed_arp = 2; + carp_set_state(vhe, MASTER); + carp_send_ad(vhe); + if (vhe->master) { + carp_send_arp(sc); + /* Schedule a delayed ARP to deal w/ some L3 switches */ + sc->sc_delayed_arp = 2; #ifdef INET6 - carp_send_na(sc); + carp_send_na(sc); #endif /* INET6 */ - carp_setrun(sc, 0); - carp_setroute(sc, RTM_ADD); + } + carp_setrun(vhe, 0); + if (vhe->master) + carp_setroute(sc, RTM_ADD); carpstats.carps_preempt++; break; } } +void +carp_setrun_all(struct carp_softc *sc, sa_family_t af) +{ + struct carp_vhost_entry *vhe; + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { + carp_setrun(vhe, af); + } +} + /* * When in backup state, af indicates whether to reset the master down timer * for v4 or v6. If it's set to zero, reset the ones which are already pending. */ void -carp_setrun(struct carp_softc *sc, sa_family_t af) +carp_setrun(struct carp_vhost_entry *vhe, sa_family_t af) { struct timeval tv; + struct carp_softc *sc = vhe->parent_sc; if (sc->sc_carpdev == NULL) { sc->sc_if.if_flags &= ~IFF_RUNNING; - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); return; } - if (sc->sc_if.if_flags & IFF_UP && sc->sc_vhid > 0 && + if (sc->sc_if.if_flags & IFF_UP && vhe->vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6) && !sc->sc_suppress) { sc->sc_if.if_flags |= IFF_RUNNING; } else { sc->sc_if.if_flags &= ~IFF_RUNNING; - carp_setroute(sc, RTM_DELETE); + if (vhe->master) + carp_setroute(sc, RTM_DELETE); return; } - switch (sc->sc_state) { + switch (vhe->state) { case INIT: - carp_set_state(sc, BACKUP); - carp_setroute(sc, RTM_DELETE); - carp_setrun(sc, 0); + carp_set_state(vhe, BACKUP); + if (vhe->master) + carp_setroute(sc, RTM_DELETE); + carp_setrun(vhe, 0); break; case BACKUP: - timeout_del(&sc->sc_ad_tmo); + timeout_del(&vhe->ad_tmo); tv.tv_sec = 3 * sc->sc_advbase; - tv.tv_usec = sc->sc_advskew * 1000000 / 256; - sc->sc_delayed_arp = -1; + tv.tv_usec = vhe->advskew * 1000000 / 256; + if (vhe->master) + sc->sc_delayed_arp = -1; switch (af) { #ifdef INET case AF_INET: - timeout_add(&sc->sc_md_tmo, tvtohz(&tv)); + timeout_add(&vhe->md_tmo, tvtohz(&tv)); break; #endif /* INET */ #ifdef INET6 case AF_INET6: - timeout_add(&sc->sc_md6_tmo, tvtohz(&tv)); + timeout_add(&vhe->md6_tmo, tvtohz(&tv)); break; #endif /* INET6 */ default: if (sc->sc_naddrs) - timeout_add(&sc->sc_md_tmo, tvtohz(&tv)); + timeout_add(&vhe->md_tmo, tvtohz(&tv)); if (sc->sc_naddrs6) - timeout_add(&sc->sc_md6_tmo, tvtohz(&tv)); + timeout_add(&vhe->md6_tmo, tvtohz(&tv)); break; } break; case MASTER: tv.tv_sec = sc->sc_advbase; - tv.tv_usec = sc->sc_advskew * 1000000 / 256; - timeout_add(&sc->sc_ad_tmo, tvtohz(&tv)); + tv.tv_usec = vhe->advskew * 1000000 / 256; + timeout_add(&vhe->ad_tmo, tvtohz(&tv)); break; } } @@ -1691,9 +1848,8 @@ carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp) TAILQ_INIT(&ncif->vhif_vrs); } else { cif = (struct carp_if *)ifp->if_carp; - TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) - if (vr != sc && vr->sc_vhid == sc->sc_vhid) - return (EINVAL); + if (carp_check_dup_vhids(sc, cif, NULL)) + return (EINVAL); } /* detach from old interface */ @@ -1726,7 +1882,8 @@ carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp) TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { if (vr == sc) myself = 1; - if (vr->sc_vhid < sc->sc_vhid) + if (LIST_FIRST(&vr->carp_vhosts)->vhid < + LIST_FIRST(&sc->carp_vhosts)->vhid) after = vr; } @@ -1756,32 +1913,44 @@ carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp) } void -carp_set_enaddr(struct carp_softc *sc) +carp_set_vhe_enaddr(struct carp_vhost_entry *vhe) { - if (sc->sc_vhid != -1 && sc->sc_carpdev) { - /* XXX detach ipv6 link-local address? */ - if (sc->sc_if.if_flags & IFF_LINK2) - sc->sc_carplladdr[0] = 1; + struct carp_softc *sc = vhe->parent_sc; + + if (vhe->vhid != 0 && sc->sc_carpdev) { + if (vhe->master && sc->sc_if.if_flags & IFF_LINK2) + vhe->vhe_enaddr[0] = 1; else - sc->sc_carplladdr[0] = 0; - sc->sc_carplladdr[1] = 0; - sc->sc_carplladdr[2] = 0x5e; - sc->sc_carplladdr[3] = 0; - sc->sc_carplladdr[4] = 1; - sc->sc_carplladdr[5] = sc->sc_vhid; + vhe->vhe_enaddr[0] = 0; + vhe->vhe_enaddr[1] = 0; + vhe->vhe_enaddr[2] = 0x5e; + vhe->vhe_enaddr[3] = 0; + vhe->vhe_enaddr[4] = 1; + vhe->vhe_enaddr[5] = vhe->vhid; } else - bzero(sc->sc_carplladdr, ETHER_ADDR_LEN); + bzero(vhe->vhe_enaddr, ETHER_ADDR_LEN); +} + +void +carp_set_enaddr(struct carp_softc *sc) +{ + struct carp_vhost_entry *vhe; + + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) + carp_set_vhe_enaddr(vhe); + + vhe = LIST_FIRST(&sc->carp_vhosts); /* * Use the carp lladdr if the running one isn't manually set. * Only compare static parts of the lladdr. */ - if ((bcmp(sc->sc_ac.ac_enaddr + 1, sc->sc_carplladdr + 1, + if ((bcmp(sc->sc_ac.ac_enaddr + 1, vhe->vhe_enaddr + 1, ETHER_ADDR_LEN - 2) == 0) || (!sc->sc_ac.ac_enaddr[0] && !sc->sc_ac.ac_enaddr[1] && !sc->sc_ac.ac_enaddr[2] && !sc->sc_ac.ac_enaddr[3] && !sc->sc_ac.ac_enaddr[4] && !sc->sc_ac.ac_enaddr[5])) - bcopy(sc->sc_carplladdr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); + bcopy(vhe->vhe_enaddr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); /* Make sure the enaddr has changed before further twiddling. */ if (bcmp(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN) != 0) { @@ -1795,8 +1964,8 @@ carp_set_enaddr(struct carp_softc *sc) */ in6_ifattach_linklocal(&sc->sc_if, NULL); #endif - carp_set_state(sc, INIT); - carp_setrun(sc, 0); + carp_set_state_all(sc, INIT); + carp_setrun_all(sc, 0); } } @@ -1840,12 +2009,12 @@ carp_addr_updated(void *v) if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) { sc->sc_if.if_flags &= ~IFF_UP; - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); } else carp_hmac_prepare(sc); } - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); } int @@ -1857,10 +2026,10 @@ carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) if (sin->sin_addr.s_addr == 0) { if (!(sc->sc_if.if_flags & IFF_UP)) - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); if (sc->sc_naddrs) sc->sc_if.if_flags |= IFF_UP; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); return (0); } @@ -1903,7 +2072,7 @@ carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) if (sc->sc_carpdev != NULL) sc->sc_if.if_flags |= IFF_UP; - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); /* * Hook if_addrhooks so that we get a callback after in_ifinit has run, @@ -1946,10 +2115,10 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { if (!(sc->sc_if.if_flags & IFF_UP)) - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); if (sc->sc_naddrs6) sc->sc_if.if_flags |= IFF_UP; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); return (0); } @@ -1998,8 +2167,8 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) sc->sc_naddrs6++; if (sc->sc_carpdev != NULL && sc->sc_naddrs6) sc->sc_if.if_flags |= IFF_UP; - carp_set_state(sc, INIT); - carp_setrun(sc, 0); + carp_set_state_all(sc, INIT); + carp_setrun_all(sc, 0); return (0); } @@ -2055,12 +2224,13 @@ int carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) { struct proc *p = curproc; /* XXX */ - struct carp_softc *sc = ifp->if_softc, *vr; + struct carp_softc *sc = ifp->if_softc; + struct carp_vhost_entry *vhe; struct carpreq carpr; struct ifaddr *ifa = (struct ifaddr *)addr; struct ifreq *ifr = (struct ifreq *)addr; struct ifnet *cdev = NULL; - int error = 0; + int i, error = 0; switch (cmd) { case SIOCSIFADDR: @@ -2086,22 +2256,22 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) break; case SIOCSIFFLAGS: - if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { - timeout_del(&sc->sc_ad_tmo); - timeout_del(&sc->sc_md_tmo); - timeout_del(&sc->sc_md6_tmo); - if (sc->sc_state == MASTER) { - /* we need the interface up to bow out */ - sc->sc_if.if_flags |= IFF_UP; - sc->sc_bow_out = 1; - carp_send_ad(sc); - } + vhe = LIST_FIRST(&sc->carp_vhosts); + if (vhe->state != INIT && !(ifr->ifr_flags & IFF_UP)) { + carp_del_all_timeouts(sc); + + /* we need the interface up to bow out */ + sc->sc_if.if_flags |= IFF_UP; + sc->sc_bow_out = 1; + carp_vhe_send_ad_all(sc); + sc->sc_bow_out = 0; + sc->sc_if.if_flags &= ~IFF_UP; - carp_set_state(sc, INIT); - carp_setrun(sc, 0); - } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { + carp_set_state_all(sc, INIT); + carp_setrun_all(sc, 0); + } else if (vhe->state == INIT && (ifr->ifr_flags & IFF_UP)) { sc->sc_if.if_flags |= IFF_UP; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); } carp_set_enaddr(sc); /* for changes on LINK2 */ if (ifr->ifr_flags & IFF_LINK0) @@ -2109,6 +2279,7 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) break; case SIOCSVH: + vhe = LIST_FIRST(&sc->carp_vhosts); if ((error = suser(p, p->p_acflag)) != 0) break; if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) @@ -2119,60 +2290,47 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) return (EINVAL); if ((error = carp_set_ifp(sc, cdev))) return (error); - if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { + if (vhe->state != INIT && carpr.carpr_state != vhe->state) { switch (carpr.carpr_state) { case BACKUP: - timeout_del(&sc->sc_ad_tmo); - carp_set_state(sc, BACKUP); - carp_setrun(sc, 0); + timeout_del(&vhe->ad_tmo); + carp_set_state_all(sc, BACKUP); + carp_setrun_all(sc, 0); carp_setroute(sc, RTM_DELETE); break; case MASTER: - carp_master_down(sc); + LIST_FOREACH(vhe, &sc->carp_vhosts, + vhost_entries) + carp_master_down(vhe); break; default: break; } } - if (carpr.carpr_vhid > 0 && carpr.carpr_vhid != sc->sc_vhid) { - if (carpr.carpr_vhid > 255) { - error = EINVAL; - break; - } - if (sc->sc_carpdev) { - struct carp_if *cif; - cif = (struct carp_if *)sc->sc_carpdev->if_carp; - TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) - if (vr != sc && - vr->sc_vhid == carpr.carpr_vhid) - return (EINVAL); - } - if (carpr.carpr_vhid != sc->sc_vhid) { - sc->sc_vhid = carpr.carpr_vhid; - carp_set_enaddr(sc); - carp_set_state(sc, INIT); - } - error--; - } - if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { - if (carpr.carpr_advskew >= 255) { - error = EINVAL; - break; - } + if ((error = carp_vhids_ioctl(sc, carpr))) + return (error); + if (carpr.carpr_advbase > 0) { if (carpr.carpr_advbase > 255) { error = EINVAL; break; } sc->sc_advbase = carpr.carpr_advbase; - sc->sc_advskew = carpr.carpr_advskew; error--; } + if (bcmp(sc->sc_advskews, carpr.carpr_advskews, + sizeof(sc->sc_advskews))) { + i = 0; + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) + vhe->advskew = carpr.carpr_advskews[i++]; + bcopy(carpr.carpr_advskews, sc->sc_advskews, + sizeof(sc->sc_advskews)); + } bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); if (error > 0) error = EINVAL; else { error = 0; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); } break; @@ -2181,10 +2339,14 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) if (sc->sc_carpdev != NULL) strlcpy(carpr.carpr_carpdev, sc->sc_carpdev->if_xname, IFNAMSIZ); - carpr.carpr_state = sc->sc_state; - carpr.carpr_vhid = sc->sc_vhid; + i = 0; + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { + carpr.carpr_vhids[i] = vhe->vhid; + carpr.carpr_advskews[i] = vhe->advskew; + carpr.carpr_states[i] = vhe->state; + i++; + } carpr.carpr_advbase = sc->sc_advbase; - carpr.carpr_advskew = sc->sc_advskew; if (suser(p, p->p_acflag) == 0) bcopy(sc->sc_key, carpr.carpr_key, sizeof(carpr.carpr_key)); @@ -2216,6 +2378,76 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) return (error); } +int +carp_check_dup_vhids(struct carp_softc *sc, struct carp_if *cif, + struct carpreq *carpr) +{ + struct carp_softc *vr; + struct carp_vhost_entry *vhe, *vhe0; + int i; + + TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { + if (vr == sc) + continue; + LIST_FOREACH(vhe, &vr->carp_vhosts, vhost_entries) { + if (carpr) { + for (i = 0; carpr->carpr_vhids[i]; i++) { + if (vhe->vhid == carpr->carpr_vhids[i]) + return (EINVAL); + } + } + LIST_FOREACH(vhe0, &sc->carp_vhosts, vhost_entries) { + if (vhe->vhid == vhe0->vhid) + return (EINVAL); + } + } + } + return (0); +} + +int +carp_vhids_ioctl(struct carp_softc *sc, struct carpreq carpr) +{ + int i; + u_int8_t taken_vhids[256]; + + if (carpr.carpr_vhids[0] == 0 || + !bcmp(sc->sc_vhids, carpr.carpr_vhids, sizeof(sc->sc_vhids))) + return (0); + + bzero(taken_vhids, sizeof(taken_vhids)); + for (i = 0; carpr.carpr_vhids[i]; i++) { + if (taken_vhids[carpr.carpr_vhids[i]]) + return (EINVAL); + taken_vhids[carpr.carpr_vhids[i]] = 1; + + if (sc->sc_carpdev) { + struct carp_if *cif; + cif = (struct carp_if *)sc->sc_carpdev->if_carp; + if (carp_check_dup_vhids(sc, cif, &carpr)) + return (EINVAL); + } + if (carpr.carpr_advskews[i] >= 255) + return (EINVAL); + } + + /* destroy all */ + carp_del_all_timeouts(sc); + carp_destroy_vhosts(sc); + bzero(sc->sc_vhids, sizeof(sc->sc_vhids)); + + for (i = 0; carpr.carpr_vhids[i]; i++) { + if (carp_new_vhost(sc, carpr.carpr_vhids[i], + carpr.carpr_advskews[i])) + return (ENOMEM); + sc->sc_vhids[i] = carpr.carpr_vhids[i]; + sc->sc_advskews[i] = carpr.carpr_advskews[i]; + } + carp_set_enaddr(sc); + carp_set_state_all(sc, INIT); + return (0); +} + void carp_ifgroup_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) { @@ -2241,9 +2473,8 @@ carp_ifgattr_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) struct carp_softc *sc = ifp->if_softc; if (ifgr->ifgr_attrib.ifg_carp_demoted > 0 && (sc->sc_if.if_flags & - (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) && - sc->sc_state == MASTER) - carp_send_ad(sc); + (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING)) + carp_vhe_send_ad_all(sc); } /* @@ -2262,8 +2493,11 @@ carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, struct rtentry *rt) { struct carp_softc *sc = ((struct carp_softc *)ifp->if_softc); + struct carp_vhost_entry *vhe; - if (sc->sc_carpdev != NULL && sc->sc_state == MASTER) + vhe = sc->cur_vhe ? sc->cur_vhe : LIST_FIRST(&sc->carp_vhosts); + + if (sc->sc_carpdev != NULL && vhe->state == MASTER) return (sc->sc_carpdev->if_output(ifp, m, sa, rt)); else { m_freem(m); @@ -2272,14 +2506,29 @@ carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, } void -carp_set_state(struct carp_softc *sc, int state) +carp_set_state_all(struct carp_softc *sc, int state) { - if (sc->sc_state == state) + struct carp_vhost_entry *vhe; + + LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) + carp_set_state(vhe, state); +} + +void +carp_set_state(struct carp_vhost_entry *vhe, int state) +{ + struct carp_softc *sc = vhe->parent_sc; + + if (vhe->state == state) return; - sc->sc_state = state; + vhe->state = state; carp_update_lsmask(sc); + /* only the master vhe creates link state messages */ + if (!vhe->master) + return; + switch (state) { case BACKUP: sc->sc_if.if_link_state = LINK_STATE_DOWN; @@ -2348,18 +2597,16 @@ carp_carpdev_state(void *v) if (sc->sc_carpdev->if_link_state == LINK_STATE_DOWN || !(sc->sc_carpdev->if_flags & IFF_UP)) { sc->sc_if.if_flags &= ~IFF_RUNNING; - timeout_del(&sc->sc_ad_tmo); - timeout_del(&sc->sc_md_tmo); - timeout_del(&sc->sc_md6_tmo); - carp_set_state(sc, INIT); + carp_del_all_timeouts(sc); + carp_set_state_all(sc, INIT); sc->sc_suppress = 1; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); if (!suppressed) carp_group_demote_adj(&sc->sc_if, 1); } else { - carp_set_state(sc, INIT); + carp_set_state_all(sc, INIT); sc->sc_suppress = 0; - carp_setrun(sc, 0); + carp_setrun_all(sc, 0); if (suppressed) carp_group_demote_adj(&sc->sc_if, -1); } |