ip_carp.c revision 172467
1142215Sglebius/* 2142215Sglebius * Copyright (c) 2002 Michael Shalayeff. All rights reserved. 3142215Sglebius * Copyright (c) 2003 Ryan McBride. All rights reserved. 4142215Sglebius * 5142215Sglebius * Redistribution and use in source and binary forms, with or without 6142215Sglebius * modification, are permitted provided that the following conditions 7142215Sglebius * are met: 8142215Sglebius * 1. Redistributions of source code must retain the above copyright 9142215Sglebius * notice, this list of conditions and the following disclaimer. 10142215Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11142215Sglebius * notice, this list of conditions and the following disclaimer in the 12142215Sglebius * documentation and/or other materials provided with the distribution. 13142215Sglebius * 14142215Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15142215Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16142215Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17142215Sglebius * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 18142215Sglebius * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19142215Sglebius * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20142215Sglebius * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21142215Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22142215Sglebius * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23142215Sglebius * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24142215Sglebius * THE POSSIBILITY OF SUCH DAMAGE. 25142215Sglebius */ 26142215Sglebius 27172467Ssilby#include <sys/cdefs.h> 28172467Ssilby__FBSDID("$FreeBSD: head/sys/netinet/ip_carp.c 172467 2007-10-07 20:44:24Z silby $"); 29172467Ssilby 30142215Sglebius#include "opt_carp.h" 31142215Sglebius#include "opt_bpf.h" 32142215Sglebius#include "opt_inet.h" 33142215Sglebius#include "opt_inet6.h" 34142215Sglebius 35142215Sglebius#include <sys/types.h> 36142215Sglebius#include <sys/param.h> 37142215Sglebius#include <sys/systm.h> 38142215Sglebius#include <sys/conf.h> 39142215Sglebius#include <sys/kernel.h> 40142215Sglebius#include <sys/limits.h> 41142215Sglebius#include <sys/malloc.h> 42142215Sglebius#include <sys/mbuf.h> 43142215Sglebius#include <sys/module.h> 44142215Sglebius#include <sys/time.h> 45164033Srwatson#include <sys/priv.h> 46142215Sglebius#include <sys/proc.h> 47142215Sglebius#include <sys/sysctl.h> 48142215Sglebius#include <sys/syslog.h> 49142215Sglebius#include <sys/signalvar.h> 50142215Sglebius#include <sys/filio.h> 51142215Sglebius#include <sys/sockio.h> 52142215Sglebius 53142215Sglebius#include <sys/socket.h> 54142215Sglebius#include <sys/vnode.h> 55142215Sglebius 56142215Sglebius#include <machine/stdarg.h> 57142215Sglebius 58142215Sglebius#include <net/bpf.h> 59142215Sglebius#include <net/ethernet.h> 60142215Sglebius#include <net/fddi.h> 61142215Sglebius#include <net/iso88025.h> 62142215Sglebius#include <net/if.h> 63142215Sglebius#include <net/if_clone.h> 64152410Sru#include <net/if_dl.h> 65142215Sglebius#include <net/if_types.h> 66142215Sglebius#include <net/route.h> 67142215Sglebius 68142215Sglebius#ifdef INET 69142215Sglebius#include <netinet/in.h> 70142215Sglebius#include <netinet/in_var.h> 71142215Sglebius#include <netinet/in_systm.h> 72142215Sglebius#include <netinet/ip.h> 73142215Sglebius#include <netinet/ip_var.h> 74142215Sglebius#include <netinet/if_ether.h> 75142215Sglebius#include <machine/in_cksum.h> 76142215Sglebius#endif 77142215Sglebius 78142215Sglebius#ifdef INET6 79142215Sglebius#include <netinet/icmp6.h> 80142215Sglebius#include <netinet/ip6.h> 81142215Sglebius#include <netinet6/ip6_var.h> 82148387Sume#include <netinet6/scope6_var.h> 83142215Sglebius#include <netinet6/nd6.h> 84142215Sglebius#endif 85142215Sglebius 86142215Sglebius#include <crypto/sha1.h> 87142215Sglebius#include <netinet/ip_carp.h> 88142215Sglebius 89142215Sglebius#define CARP_IFNAME "carp" 90142215Sglebiusstatic MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces"); 91142215SglebiusSYSCTL_DECL(_net_inet_carp); 92142215Sglebius 93142215Sglebiusstruct carp_softc { 94147256Sbrooks struct ifnet *sc_ifp; /* Interface clue */ 95142901Sglebius struct ifnet *sc_carpdev; /* Pointer to parent interface */ 96142215Sglebius struct in_ifaddr *sc_ia; /* primary iface address */ 97142215Sglebius struct ip_moptions sc_imo; 98142215Sglebius#ifdef INET6 99142215Sglebius struct in6_ifaddr *sc_ia6; /* primary iface address v6 */ 100142215Sglebius struct ip6_moptions sc_im6o; 101142215Sglebius#endif /* INET6 */ 102142215Sglebius TAILQ_ENTRY(carp_softc) sc_list; 103142215Sglebius 104142215Sglebius enum { INIT = 0, BACKUP, MASTER } sc_state; 105142215Sglebius 106142215Sglebius int sc_flags_backup; 107142215Sglebius int sc_suppress; 108142215Sglebius 109142215Sglebius int sc_sendad_errors; 110142215Sglebius#define CARP_SENDAD_MAX_ERRORS 3 111142215Sglebius int sc_sendad_success; 112142215Sglebius#define CARP_SENDAD_MIN_SUCCESS 3 113142215Sglebius 114142215Sglebius int sc_vhid; 115142215Sglebius int sc_advskew; 116142215Sglebius int sc_naddrs; 117142215Sglebius int sc_naddrs6; 118142215Sglebius int sc_advbase; /* seconds */ 119142215Sglebius int sc_init_counter; 120142215Sglebius u_int64_t sc_counter; 121142215Sglebius 122142215Sglebius /* authentication */ 123142215Sglebius#define CARP_HMAC_PAD 64 124142215Sglebius unsigned char sc_key[CARP_KEY_LEN]; 125142215Sglebius unsigned char sc_pad[CARP_HMAC_PAD]; 126142215Sglebius SHA1_CTX sc_sha1; 127142215Sglebius 128142215Sglebius struct callout sc_ad_tmo; /* advertisement timeout */ 129142215Sglebius struct callout sc_md_tmo; /* master down timeout */ 130142215Sglebius struct callout sc_md6_tmo; /* master down timeout */ 131142215Sglebius 132142215Sglebius LIST_ENTRY(carp_softc) sc_next; /* Interface clue */ 133142215Sglebius}; 134147256Sbrooks#define SC2IFP(sc) ((sc)->sc_ifp) 135142215Sglebius 136142215Sglebiusint carp_suppress_preempt = 0; 137142215Sglebiusint carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */ 138142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW, 139142215Sglebius &carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets"); 140142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW, 141142215Sglebius &carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode"); 142142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW, 143142215Sglebius &carp_opts[CARPCTL_LOG], 0, "log bad carp packets"); 144142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW, 145142215Sglebius &carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses"); 146146226SglebiusSYSCTL_INT(_net_inet_carp, OID_AUTO, suppress_preempt, CTLFLAG_RD, 147146226Sglebius &carp_suppress_preempt, 0, "Preemption is suppressed"); 148142215Sglebius 149142215Sglebiusstruct carpstats carpstats; 150142215SglebiusSYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW, 151142215Sglebius &carpstats, carpstats, 152142215Sglebius "CARP statistics (struct carpstats, netinet/ip_carp.h)"); 153142215Sglebius 154142215Sglebiusstruct carp_if { 155142215Sglebius TAILQ_HEAD(, carp_softc) vhif_vrs; 156142215Sglebius int vhif_nvrs; 157142215Sglebius 158142215Sglebius struct ifnet *vhif_ifp; 159142215Sglebius struct mtx vhif_mtx; 160142215Sglebius}; 161142914Sglebius 162142914Sglebius/* Get carp_if from softc. Valid after carp_set_addr{,6}. */ 163142914Sglebius#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp) 164142914Sglebius 165142215Sglebius/* lock per carp_if queue */ 166142914Sglebius#define CARP_LOCK_INIT(cif) mtx_init(&(cif)->vhif_mtx, "carp_if", \ 167142215Sglebius NULL, MTX_DEF) 168142914Sglebius#define CARP_LOCK_DESTROY(cif) mtx_destroy(&(cif)->vhif_mtx) 169142215Sglebius#define CARP_LOCK_ASSERT(cif) mtx_assert(&(cif)->vhif_mtx, MA_OWNED) 170142215Sglebius#define CARP_LOCK(cif) mtx_lock(&(cif)->vhif_mtx) 171142215Sglebius#define CARP_UNLOCK(cif) mtx_unlock(&(cif)->vhif_mtx) 172142215Sglebius 173142914Sglebius#define CARP_SCLOCK(sc) mtx_lock(&SC2CIF(sc)->vhif_mtx) 174142914Sglebius#define CARP_SCUNLOCK(sc) mtx_unlock(&SC2CIF(sc)->vhif_mtx) 175142914Sglebius#define CARP_SCLOCK_ASSERT(sc) mtx_assert(&SC2CIF(sc)->vhif_mtx, MA_OWNED) 176142914Sglebius 177142451Sglebius#define CARP_LOG(...) do { \ 178142446Sglebius if (carp_opts[CARPCTL_LOG] > 0) \ 179142446Sglebius log(LOG_INFO, __VA_ARGS__); \ 180142451Sglebius} while (0) 181142215Sglebius 182142451Sglebius#define CARP_DEBUG(...) do { \ 183142446Sglebius if (carp_opts[CARPCTL_LOG] > 1) \ 184142446Sglebius log(LOG_DEBUG, __VA_ARGS__); \ 185142451Sglebius} while (0) 186142446Sglebius 187142559Sglebiusstatic void carp_hmac_prepare(struct carp_softc *); 188142559Sglebiusstatic void carp_hmac_generate(struct carp_softc *, u_int32_t *, 189142559Sglebius unsigned char *); 190142559Sglebiusstatic int carp_hmac_verify(struct carp_softc *, u_int32_t *, 191142559Sglebius unsigned char *); 192142559Sglebiusstatic void carp_setroute(struct carp_softc *, int); 193142559Sglebiusstatic void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); 194160195Ssamstatic int carp_clone_create(struct if_clone *, int, caddr_t); 195142559Sglebiusstatic void carp_clone_destroy(struct ifnet *); 196166228Sglebiusstatic void carpdetach(struct carp_softc *, int); 197142559Sglebiusstatic int carp_prepare_ad(struct mbuf *, struct carp_softc *, 198142559Sglebius struct carp_header *); 199142559Sglebiusstatic void carp_send_ad_all(void); 200142559Sglebiusstatic void carp_send_ad(void *); 201142914Sglebiusstatic void carp_send_ad_locked(struct carp_softc *); 202142559Sglebiusstatic void carp_send_arp(struct carp_softc *); 203142559Sglebiusstatic void carp_master_down(void *); 204142914Sglebiusstatic void carp_master_down_locked(struct carp_softc *); 205142559Sglebiusstatic int carp_ioctl(struct ifnet *, u_long, caddr_t); 206142559Sglebiusstatic int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *, 207142559Sglebius struct rtentry *); 208142559Sglebiusstatic void carp_start(struct ifnet *); 209142559Sglebiusstatic void carp_setrun(struct carp_softc *, sa_family_t); 210142559Sglebiusstatic void carp_set_state(struct carp_softc *, int); 211142559Sglebiusstatic int carp_addrcount(struct carp_if *, struct in_ifaddr *, int); 212142215Sglebiusenum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING }; 213142215Sglebius 214156947Sglebiusstatic void carp_multicast_cleanup(struct carp_softc *); 215142559Sglebiusstatic int carp_set_addr(struct carp_softc *, struct sockaddr_in *); 216142559Sglebiusstatic int carp_del_addr(struct carp_softc *, struct sockaddr_in *); 217142914Sglebiusstatic void carp_carpdev_state_locked(struct carp_if *); 218144329Sglebiusstatic void carp_sc_state_locked(struct carp_softc *); 219142215Sglebius#ifdef INET6 220142559Sglebiusstatic void carp_send_na(struct carp_softc *); 221142559Sglebiusstatic int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); 222142559Sglebiusstatic int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *); 223166423Sglebiusstatic void carp_multicast6_cleanup(struct carp_softc *); 224142215Sglebius#endif 225142215Sglebius 226142215Sglebiusstatic LIST_HEAD(, carp_softc) carpif_list; 227142911Sglebiusstatic struct mtx carp_mtx; 228142215SglebiusIFC_SIMPLE_DECLARE(carp, 0); 229142215Sglebius 230156947Sglebiusstatic eventhandler_tag if_detach_event_tag; 231156947Sglebius 232142215Sglebiusstatic __inline u_int16_t 233142215Sglebiuscarp_cksum(struct mbuf *m, int len) 234142215Sglebius{ 235142215Sglebius return (in_cksum(m, len)); 236142215Sglebius} 237142215Sglebius 238142559Sglebiusstatic void 239142215Sglebiuscarp_hmac_prepare(struct carp_softc *sc) 240142215Sglebius{ 241142215Sglebius u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; 242142215Sglebius u_int8_t vhid = sc->sc_vhid & 0xff; 243142215Sglebius struct ifaddr *ifa; 244142215Sglebius int i; 245142215Sglebius#ifdef INET6 246142215Sglebius struct in6_addr in6; 247142215Sglebius#endif 248142215Sglebius 249142914Sglebius if (sc->sc_carpdev) 250142914Sglebius CARP_SCLOCK(sc); 251142914Sglebius 252142914Sglebius /* XXX: possible race here */ 253142914Sglebius 254142215Sglebius /* compute ipad from key */ 255142215Sglebius bzero(sc->sc_pad, sizeof(sc->sc_pad)); 256142215Sglebius bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); 257142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 258142215Sglebius sc->sc_pad[i] ^= 0x36; 259142215Sglebius 260142215Sglebius /* precompute first part of inner hash */ 261142215Sglebius SHA1Init(&sc->sc_sha1); 262142215Sglebius SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); 263142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version)); 264142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); 265142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); 266142215Sglebius#ifdef INET 267147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 268142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET) 269142215Sglebius SHA1Update(&sc->sc_sha1, 270142215Sglebius (void *)&ifatoia(ifa)->ia_addr.sin_addr.s_addr, 271142215Sglebius sizeof(struct in_addr)); 272142215Sglebius } 273142215Sglebius#endif /* INET */ 274142215Sglebius#ifdef INET6 275147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 276142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET6) { 277142215Sglebius in6 = ifatoia6(ifa)->ia_addr.sin6_addr; 278148385Sume in6_clearscope(&in6); 279142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(in6)); 280142215Sglebius } 281142215Sglebius } 282142215Sglebius#endif /* INET6 */ 283142215Sglebius 284142215Sglebius /* convert ipad to opad */ 285142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 286142215Sglebius sc->sc_pad[i] ^= 0x36 ^ 0x5c; 287142914Sglebius 288142914Sglebius if (sc->sc_carpdev) 289142914Sglebius CARP_SCUNLOCK(sc); 290142215Sglebius} 291142215Sglebius 292142559Sglebiusstatic void 293142215Sglebiuscarp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], 294142215Sglebius unsigned char md[20]) 295142215Sglebius{ 296142215Sglebius SHA1_CTX sha1ctx; 297142215Sglebius 298142215Sglebius /* fetch first half of inner hash */ 299142215Sglebius bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); 300142215Sglebius 301142215Sglebius SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter)); 302142215Sglebius SHA1Final(md, &sha1ctx); 303142215Sglebius 304142215Sglebius /* outer hash */ 305142215Sglebius SHA1Init(&sha1ctx); 306142215Sglebius SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); 307142215Sglebius SHA1Update(&sha1ctx, md, 20); 308142215Sglebius SHA1Final(md, &sha1ctx); 309142215Sglebius} 310142215Sglebius 311142559Sglebiusstatic int 312142215Sglebiuscarp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], 313142215Sglebius unsigned char md[20]) 314142215Sglebius{ 315142215Sglebius unsigned char md2[20]; 316142215Sglebius 317142914Sglebius CARP_SCLOCK_ASSERT(sc); 318142914Sglebius 319142215Sglebius carp_hmac_generate(sc, counter, md2); 320142215Sglebius 321142215Sglebius return (bcmp(md, md2, sizeof(md2))); 322142215Sglebius} 323142215Sglebius 324142559Sglebiusstatic void 325142215Sglebiuscarp_setroute(struct carp_softc *sc, int cmd) 326142215Sglebius{ 327142215Sglebius struct ifaddr *ifa; 328142215Sglebius int s; 329142215Sglebius 330142914Sglebius if (sc->sc_carpdev) 331142914Sglebius CARP_SCLOCK_ASSERT(sc); 332142914Sglebius 333142215Sglebius s = splnet(); 334147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 335142914Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 336142914Sglebius sc->sc_carpdev != NULL) { 337142215Sglebius int count = carp_addrcount( 338142564Sglebius (struct carp_if *)sc->sc_carpdev->if_carp, 339142215Sglebius ifatoia(ifa), CARP_COUNT_MASTER); 340142215Sglebius 341142215Sglebius if ((cmd == RTM_ADD && count == 1) || 342142215Sglebius (cmd == RTM_DELETE && count == 0)) 343142215Sglebius rtinit(ifa, cmd, RTF_UP | RTF_HOST); 344142215Sglebius } 345142215Sglebius#ifdef INET6 346142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET6) { 347142215Sglebius if (cmd == RTM_ADD) 348142215Sglebius in6_ifaddloop(ifa); 349142215Sglebius else 350142215Sglebius in6_ifremloop(ifa); 351142215Sglebius } 352142215Sglebius#endif /* INET6 */ 353142215Sglebius } 354142215Sglebius splx(s); 355142215Sglebius} 356142215Sglebius 357142559Sglebiusstatic int 358160195Ssamcarp_clone_create(struct if_clone *ifc, int unit, caddr_t params) 359142215Sglebius{ 360142215Sglebius 361142215Sglebius struct carp_softc *sc; 362142215Sglebius struct ifnet *ifp; 363142215Sglebius 364142215Sglebius MALLOC(sc, struct carp_softc *, sizeof(*sc), M_CARP, M_WAITOK|M_ZERO); 365147256Sbrooks ifp = SC2IFP(sc) = if_alloc(IFT_ETHER); 366147256Sbrooks if (ifp == NULL) { 367147256Sbrooks FREE(sc, M_CARP); 368147256Sbrooks return (ENOSPC); 369147256Sbrooks } 370142215Sglebius 371142215Sglebius sc->sc_flags_backup = 0; 372142215Sglebius sc->sc_suppress = 0; 373142215Sglebius sc->sc_advbase = CARP_DFLTINTV; 374142215Sglebius sc->sc_vhid = -1; /* required setting */ 375142215Sglebius sc->sc_advskew = 0; 376142215Sglebius sc->sc_init_counter = 1; 377142215Sglebius sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */ 378142215Sglebius#ifdef INET6 379142215Sglebius sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; 380142215Sglebius#endif 381160164Smlaier sc->sc_imo.imo_membership = (struct in_multi **)malloc( 382160164Smlaier (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP, 383160164Smlaier M_WAITOK); 384170613Sbms sc->sc_imo.imo_mfilters = NULL; 385160164Smlaier sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; 386162627Sbms sc->sc_imo.imo_multicast_vif = -1; 387142215Sglebius 388171637Srwatson callout_init(&sc->sc_ad_tmo, CALLOUT_MPSAFE); 389171637Srwatson callout_init(&sc->sc_md_tmo, CALLOUT_MPSAFE); 390171637Srwatson callout_init(&sc->sc_md6_tmo, CALLOUT_MPSAFE); 391142215Sglebius 392142215Sglebius ifp->if_softc = sc; 393142215Sglebius if_initname(ifp, CARP_IFNAME, unit); 394142215Sglebius ifp->if_mtu = ETHERMTU; 395151688Syar ifp->if_flags = IFF_LOOPBACK; 396142215Sglebius ifp->if_ioctl = carp_ioctl; 397142215Sglebius ifp->if_output = carp_looutput; 398142215Sglebius ifp->if_start = carp_start; 399142215Sglebius ifp->if_type = IFT_CARP; 400142215Sglebius ifp->if_snd.ifq_maxlen = ifqmaxlen; 401142215Sglebius ifp->if_hdrlen = 0; 402142215Sglebius if_attach(ifp); 403147256Sbrooks bpfattach(SC2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 404142911Sglebius mtx_lock(&carp_mtx); 405142215Sglebius LIST_INSERT_HEAD(&carpif_list, sc, sc_next); 406142911Sglebius mtx_unlock(&carp_mtx); 407142215Sglebius return (0); 408142215Sglebius} 409142215Sglebius 410142559Sglebiusstatic void 411142215Sglebiuscarp_clone_destroy(struct ifnet *ifp) 412142215Sglebius{ 413142215Sglebius struct carp_softc *sc = ifp->if_softc; 414156947Sglebius 415156947Sglebius if (sc->sc_carpdev) 416156947Sglebius CARP_SCLOCK(sc); 417166228Sglebius carpdetach(sc, 1); /* Returns unlocked. */ 418156947Sglebius 419156947Sglebius mtx_lock(&carp_mtx); 420156947Sglebius LIST_REMOVE(sc, sc_next); 421156947Sglebius mtx_unlock(&carp_mtx); 422156947Sglebius bpfdetach(ifp); 423156947Sglebius if_detach(ifp); 424156947Sglebius if_free_type(ifp, IFT_ETHER); 425160164Smlaier free(sc->sc_imo.imo_membership, M_CARP); 426156947Sglebius free(sc, M_CARP); 427156947Sglebius} 428156947Sglebius 429166423Sglebius/* 430166423Sglebius * This function can be called on CARP interface destroy path, 431166423Sglebius * and in case of the removal of the underlying interface as 432166423Sglebius * well. We differentiate these two cases. In the latter case 433166423Sglebius * we do not cleanup our multicast memberships, since they 434166423Sglebius * are already freed. Also, in the latter case we do not 435166423Sglebius * release the lock on return, because the function will be 436166423Sglebius * called once more, for another CARP instance on the same 437166423Sglebius * interface. 438166423Sglebius */ 439156947Sglebiusstatic void 440166228Sglebiuscarpdetach(struct carp_softc *sc, int unlock) 441156947Sglebius{ 442142215Sglebius struct carp_if *cif; 443146226Sglebius 444156947Sglebius callout_stop(&sc->sc_ad_tmo); 445156947Sglebius callout_stop(&sc->sc_md_tmo); 446156947Sglebius callout_stop(&sc->sc_md6_tmo); 447156947Sglebius 448146226Sglebius if (sc->sc_suppress) 449146226Sglebius carp_suppress_preempt--; 450146226Sglebius sc->sc_suppress = 0; 451146226Sglebius 452156947Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) 453156947Sglebius carp_suppress_preempt--; 454156947Sglebius sc->sc_sendad_errors = 0; 455142215Sglebius 456156947Sglebius carp_set_state(sc, INIT); 457156947Sglebius SC2IFP(sc)->if_flags &= ~IFF_UP; 458156947Sglebius carp_setrun(sc, 0); 459166423Sglebius if (unlock) 460166423Sglebius carp_multicast_cleanup(sc); 461166423Sglebius#ifdef INET6 462166423Sglebius carp_multicast6_cleanup(sc); 463166423Sglebius#endif 464142215Sglebius 465156947Sglebius if (sc->sc_carpdev != NULL) { 466156947Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 467156947Sglebius CARP_LOCK_ASSERT(cif); 468142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 469142215Sglebius if (!--cif->vhif_nvrs) { 470156947Sglebius ifpromisc(sc->sc_carpdev, 0); 471142564Sglebius sc->sc_carpdev->if_carp = NULL; 472142215Sglebius CARP_LOCK_DESTROY(cif); 473156947Sglebius FREE(cif, M_IFADDR); 474166228Sglebius } else if (unlock) 475166228Sglebius CARP_UNLOCK(cif); 476166228Sglebius sc->sc_carpdev = NULL; 477142215Sglebius } 478156947Sglebius} 479142215Sglebius 480156947Sglebius/* Detach an interface from the carp. */ 481156947Sglebiusstatic void 482156947Sglebiuscarp_ifdetach(void *arg __unused, struct ifnet *ifp) 483156947Sglebius{ 484156947Sglebius struct carp_if *cif = (struct carp_if *)ifp->if_carp; 485156947Sglebius struct carp_softc *sc, *nextsc; 486166226Sglebius 487156947Sglebius if (cif == NULL) 488156947Sglebius return; 489156947Sglebius 490156947Sglebius /* 491156947Sglebius * XXX: At the end of for() cycle the lock will be destroyed. 492156947Sglebius */ 493156947Sglebius CARP_LOCK(cif); 494156947Sglebius for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) { 495156947Sglebius nextsc = TAILQ_NEXT(sc, sc_list); 496166228Sglebius carpdetach(sc, 0); 497156947Sglebius } 498142215Sglebius} 499142215Sglebius 500142215Sglebius/* 501142215Sglebius * process input packet. 502142215Sglebius * we have rearranged checks order compared to the rfc, 503142215Sglebius * but it seems more efficient this way or not possible otherwise. 504142215Sglebius */ 505142215Sglebiusvoid 506142215Sglebiuscarp_input(struct mbuf *m, int hlen) 507142215Sglebius{ 508142215Sglebius struct ip *ip = mtod(m, struct ip *); 509142215Sglebius struct carp_header *ch; 510142215Sglebius int iplen, len; 511142215Sglebius 512142215Sglebius carpstats.carps_ipackets++; 513142215Sglebius 514142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 515142215Sglebius m_freem(m); 516142215Sglebius return; 517142215Sglebius } 518142215Sglebius 519142215Sglebius /* check if received on a valid carp interface */ 520142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 521142215Sglebius carpstats.carps_badif++; 522142452Sglebius CARP_LOG("carp_input: packet received on non-carp " 523142452Sglebius "interface: %s\n", 524142446Sglebius m->m_pkthdr.rcvif->if_xname); 525142215Sglebius m_freem(m); 526142215Sglebius return; 527142215Sglebius } 528142215Sglebius 529142215Sglebius /* verify that the IP TTL is 255. */ 530142215Sglebius if (ip->ip_ttl != CARP_DFLTTL) { 531142215Sglebius carpstats.carps_badttl++; 532142452Sglebius CARP_LOG("carp_input: received ttl %d != 255i on %s\n", 533142446Sglebius ip->ip_ttl, 534142446Sglebius m->m_pkthdr.rcvif->if_xname); 535142215Sglebius m_freem(m); 536142215Sglebius return; 537142215Sglebius } 538142215Sglebius 539142215Sglebius iplen = ip->ip_hl << 2; 540142215Sglebius 541142215Sglebius if (m->m_pkthdr.len < iplen + sizeof(*ch)) { 542142215Sglebius carpstats.carps_badlen++; 543142446Sglebius CARP_LOG("carp_input: received len %zd < " 544142452Sglebius "sizeof(struct carp_header)\n", 545142446Sglebius m->m_len - sizeof(struct ip)); 546142215Sglebius m_freem(m); 547142215Sglebius return; 548142215Sglebius } 549142215Sglebius 550142215Sglebius if (iplen + sizeof(*ch) < m->m_len) { 551142215Sglebius if ((m = m_pullup(m, iplen + sizeof(*ch))) == NULL) { 552142215Sglebius carpstats.carps_hdrops++; 553142452Sglebius CARP_LOG("carp_input: pullup failed\n"); 554142215Sglebius return; 555142215Sglebius } 556142215Sglebius ip = mtod(m, struct ip *); 557142215Sglebius } 558142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 559142215Sglebius 560142215Sglebius /* 561142215Sglebius * verify that the received packet length is 562142215Sglebius * equal to the CARP header 563142215Sglebius */ 564142215Sglebius len = iplen + sizeof(*ch); 565142215Sglebius if (len > m->m_pkthdr.len) { 566142215Sglebius carpstats.carps_badlen++; 567142452Sglebius CARP_LOG("carp_input: packet too short %d on %s\n", 568142446Sglebius m->m_pkthdr.len, 569142446Sglebius m->m_pkthdr.rcvif->if_xname); 570142215Sglebius m_freem(m); 571142215Sglebius return; 572142215Sglebius } 573142215Sglebius 574142215Sglebius if ((m = m_pullup(m, len)) == NULL) { 575142215Sglebius carpstats.carps_hdrops++; 576142215Sglebius return; 577142215Sglebius } 578142215Sglebius ip = mtod(m, struct ip *); 579142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 580142215Sglebius 581142215Sglebius /* verify the CARP checksum */ 582142215Sglebius m->m_data += iplen; 583142215Sglebius if (carp_cksum(m, len - iplen)) { 584142215Sglebius carpstats.carps_badsum++; 585142452Sglebius CARP_LOG("carp_input: checksum failed on %s\n", 586142446Sglebius m->m_pkthdr.rcvif->if_xname); 587142215Sglebius m_freem(m); 588142215Sglebius return; 589142215Sglebius } 590142215Sglebius m->m_data -= iplen; 591142215Sglebius 592142446Sglebius carp_input_c(m, ch, AF_INET); 593142215Sglebius} 594142215Sglebius 595142215Sglebius#ifdef INET6 596142215Sglebiusint 597142215Sglebiuscarp6_input(struct mbuf **mp, int *offp, int proto) 598142215Sglebius{ 599142215Sglebius struct mbuf *m = *mp; 600142215Sglebius struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 601142215Sglebius struct carp_header *ch; 602142215Sglebius u_int len; 603142215Sglebius 604142215Sglebius carpstats.carps_ipackets6++; 605142215Sglebius 606142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 607142215Sglebius m_freem(m); 608142215Sglebius return (IPPROTO_DONE); 609142215Sglebius } 610142215Sglebius 611142215Sglebius /* check if received on a valid carp interface */ 612142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 613142215Sglebius carpstats.carps_badif++; 614142446Sglebius CARP_LOG("carp6_input: packet received on non-carp " 615142452Sglebius "interface: %s\n", 616142446Sglebius m->m_pkthdr.rcvif->if_xname); 617142215Sglebius m_freem(m); 618142215Sglebius return (IPPROTO_DONE); 619142215Sglebius } 620142215Sglebius 621142215Sglebius /* verify that the IP TTL is 255 */ 622142215Sglebius if (ip6->ip6_hlim != CARP_DFLTTL) { 623142215Sglebius carpstats.carps_badttl++; 624142452Sglebius CARP_LOG("carp6_input: received ttl %d != 255 on %s\n", 625142446Sglebius ip6->ip6_hlim, 626142446Sglebius m->m_pkthdr.rcvif->if_xname); 627142215Sglebius m_freem(m); 628142215Sglebius return (IPPROTO_DONE); 629142215Sglebius } 630142215Sglebius 631142215Sglebius /* verify that we have a complete carp packet */ 632142215Sglebius len = m->m_len; 633142215Sglebius IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch)); 634142215Sglebius if (ch == NULL) { 635142215Sglebius carpstats.carps_badlen++; 636143804Sglebius CARP_LOG("carp6_input: packet size %u too small\n", len); 637142215Sglebius return (IPPROTO_DONE); 638142215Sglebius } 639142215Sglebius 640142215Sglebius 641142215Sglebius /* verify the CARP checksum */ 642142215Sglebius m->m_data += *offp; 643142215Sglebius if (carp_cksum(m, sizeof(*ch))) { 644142215Sglebius carpstats.carps_badsum++; 645142452Sglebius CARP_LOG("carp6_input: checksum failed, on %s\n", 646142446Sglebius m->m_pkthdr.rcvif->if_xname); 647142215Sglebius m_freem(m); 648142215Sglebius return (IPPROTO_DONE); 649142215Sglebius } 650142215Sglebius m->m_data -= *offp; 651142215Sglebius 652142446Sglebius carp_input_c(m, ch, AF_INET6); 653142215Sglebius return (IPPROTO_DONE); 654142215Sglebius} 655142215Sglebius#endif /* INET6 */ 656142215Sglebius 657142559Sglebiusstatic void 658142446Sglebiuscarp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) 659142215Sglebius{ 660142215Sglebius struct ifnet *ifp = m->m_pkthdr.rcvif; 661142446Sglebius struct carp_softc *sc; 662142215Sglebius u_int64_t tmp_counter; 663142215Sglebius struct timeval sc_tv, ch_tv; 664142215Sglebius 665142215Sglebius /* verify that the VHID is valid on the receiving interface */ 666142215Sglebius CARP_LOCK(ifp->if_carp); 667142215Sglebius TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list) 668142215Sglebius if (sc->sc_vhid == ch->carp_vhid) 669142215Sglebius break; 670142914Sglebius 671148887Srwatson if (!sc || !((SC2IFP(sc)->if_flags & IFF_UP) && 672148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 673142215Sglebius carpstats.carps_badvhid++; 674142914Sglebius CARP_UNLOCK(ifp->if_carp); 675142215Sglebius m_freem(m); 676142215Sglebius return; 677142215Sglebius } 678142215Sglebius 679147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 680147256Sbrooks SC2IFP(sc)->if_ipackets++; 681147256Sbrooks SC2IFP(sc)->if_ibytes += m->m_pkthdr.len; 682142215Sglebius 683159180Scsjp if (bpf_peers_present(SC2IFP(sc)->if_bpf)) { 684142215Sglebius struct ip *ip = mtod(m, struct ip *); 685142784Sglebius uint32_t af1 = af; 686142215Sglebius 687142215Sglebius /* BPF wants net byte order */ 688142784Sglebius ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2)); 689142784Sglebius ip->ip_off = htons(ip->ip_off); 690147256Sbrooks bpf_mtap2(SC2IFP(sc)->if_bpf, &af1, sizeof(af1), m); 691142215Sglebius } 692142215Sglebius 693142215Sglebius /* verify the CARP version. */ 694142215Sglebius if (ch->carp_version != CARP_VERSION) { 695142215Sglebius carpstats.carps_badver++; 696147256Sbrooks SC2IFP(sc)->if_ierrors++; 697142914Sglebius CARP_UNLOCK(ifp->if_carp); 698142452Sglebius CARP_LOG("%s; invalid version %d\n", 699147256Sbrooks SC2IFP(sc)->if_xname, 700142446Sglebius ch->carp_version); 701142215Sglebius m_freem(m); 702142215Sglebius return; 703142215Sglebius } 704142215Sglebius 705142215Sglebius /* verify the hash */ 706142215Sglebius if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { 707142215Sglebius carpstats.carps_badauth++; 708147256Sbrooks SC2IFP(sc)->if_ierrors++; 709142914Sglebius CARP_UNLOCK(ifp->if_carp); 710147256Sbrooks CARP_LOG("%s: incorrect hash\n", SC2IFP(sc)->if_xname); 711142215Sglebius m_freem(m); 712142215Sglebius return; 713142215Sglebius } 714142215Sglebius 715142215Sglebius tmp_counter = ntohl(ch->carp_counter[0]); 716142215Sglebius tmp_counter = tmp_counter<<32; 717142215Sglebius tmp_counter += ntohl(ch->carp_counter[1]); 718142215Sglebius 719142215Sglebius /* XXX Replay protection goes here */ 720142215Sglebius 721142215Sglebius sc->sc_init_counter = 0; 722142215Sglebius sc->sc_counter = tmp_counter; 723142215Sglebius 724142215Sglebius sc_tv.tv_sec = sc->sc_advbase; 725142215Sglebius if (carp_suppress_preempt && sc->sc_advskew < 240) 726142215Sglebius sc_tv.tv_usec = 240 * 1000000 / 256; 727142215Sglebius else 728142215Sglebius sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; 729142215Sglebius ch_tv.tv_sec = ch->carp_advbase; 730142215Sglebius ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; 731142215Sglebius 732142215Sglebius switch (sc->sc_state) { 733142215Sglebius case INIT: 734142215Sglebius break; 735142215Sglebius case MASTER: 736142215Sglebius /* 737142215Sglebius * If we receive an advertisement from a master who's going to 738142215Sglebius * be more frequent than us, go into BACKUP state. 739142215Sglebius */ 740142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, >) || 741142215Sglebius timevalcmp(&sc_tv, &ch_tv, ==)) { 742142215Sglebius callout_stop(&sc->sc_ad_tmo); 743142446Sglebius CARP_DEBUG("%s: MASTER -> BACKUP " 744142452Sglebius "(more frequent advertisement received)\n", 745147256Sbrooks SC2IFP(sc)->if_xname); 746142215Sglebius carp_set_state(sc, BACKUP); 747142215Sglebius carp_setrun(sc, 0); 748142215Sglebius carp_setroute(sc, RTM_DELETE); 749142215Sglebius } 750142215Sglebius break; 751142215Sglebius case BACKUP: 752142215Sglebius /* 753142215Sglebius * If we're pre-empting masters who advertise slower than us, 754142215Sglebius * and this one claims to be slower, treat him as down. 755142215Sglebius */ 756142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && 757142215Sglebius timevalcmp(&sc_tv, &ch_tv, <)) { 758142446Sglebius CARP_DEBUG("%s: BACKUP -> MASTER " 759142452Sglebius "(preempting a slower master)\n", 760147256Sbrooks SC2IFP(sc)->if_xname); 761142914Sglebius carp_master_down_locked(sc); 762142215Sglebius break; 763142215Sglebius } 764142215Sglebius 765142215Sglebius /* 766142215Sglebius * If the master is going to advertise at such a low frequency 767142215Sglebius * that he's guaranteed to time out, we'd might as well just 768142215Sglebius * treat him as timed out now. 769142215Sglebius */ 770142215Sglebius sc_tv.tv_sec = sc->sc_advbase * 3; 771142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, <)) { 772142446Sglebius CARP_DEBUG("%s: BACKUP -> MASTER " 773142452Sglebius "(master timed out)\n", 774147256Sbrooks SC2IFP(sc)->if_xname); 775142914Sglebius carp_master_down_locked(sc); 776142215Sglebius break; 777142215Sglebius } 778142215Sglebius 779142215Sglebius /* 780142215Sglebius * Otherwise, we reset the counter and wait for the next 781142215Sglebius * advertisement. 782142215Sglebius */ 783142215Sglebius carp_setrun(sc, af); 784142215Sglebius break; 785142215Sglebius } 786142215Sglebius 787142914Sglebius CARP_UNLOCK(ifp->if_carp); 788142914Sglebius 789142215Sglebius m_freem(m); 790142215Sglebius return; 791142215Sglebius} 792142215Sglebius 793142559Sglebiusstatic int 794142215Sglebiuscarp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) 795142215Sglebius{ 796142215Sglebius struct m_tag *mtag; 797147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 798142215Sglebius 799142215Sglebius if (sc->sc_init_counter) { 800142215Sglebius /* this could also be seconds since unix epoch */ 801142215Sglebius sc->sc_counter = arc4random(); 802142215Sglebius sc->sc_counter = sc->sc_counter << 32; 803142215Sglebius sc->sc_counter += arc4random(); 804142215Sglebius } else 805142215Sglebius sc->sc_counter++; 806142215Sglebius 807142215Sglebius ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff); 808142215Sglebius ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff); 809142215Sglebius 810142215Sglebius carp_hmac_generate(sc, ch->carp_counter, ch->carp_md); 811142215Sglebius 812142215Sglebius /* Tag packet for carp_output */ 813142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT); 814142215Sglebius if (mtag == NULL) { 815142215Sglebius m_freem(m); 816147256Sbrooks SC2IFP(sc)->if_oerrors++; 817142215Sglebius return (ENOMEM); 818142215Sglebius } 819142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 820142215Sglebius m_tag_prepend(m, mtag); 821142215Sglebius 822142215Sglebius return (0); 823142215Sglebius} 824142215Sglebius 825142559Sglebiusstatic void 826142215Sglebiuscarp_send_ad_all(void) 827142215Sglebius{ 828142911Sglebius struct carp_softc *sc; 829142215Sglebius 830142911Sglebius mtx_lock(&carp_mtx); 831142911Sglebius LIST_FOREACH(sc, &carpif_list, sc_next) { 832142911Sglebius if (sc->sc_carpdev == NULL) 833142215Sglebius continue; 834142911Sglebius CARP_SCLOCK(sc); 835148887Srwatson if ((SC2IFP(sc)->if_flags & IFF_UP) && 836148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING) && 837142911Sglebius sc->sc_state == MASTER) 838142914Sglebius carp_send_ad_locked(sc); 839142911Sglebius CARP_SCUNLOCK(sc); 840142215Sglebius } 841142911Sglebius mtx_unlock(&carp_mtx); 842142215Sglebius} 843142215Sglebius 844142559Sglebiusstatic void 845142215Sglebiuscarp_send_ad(void *v) 846142215Sglebius{ 847142914Sglebius struct carp_softc *sc = v; 848142914Sglebius 849142914Sglebius CARP_SCLOCK(sc); 850142914Sglebius carp_send_ad_locked(sc); 851142914Sglebius CARP_SCUNLOCK(sc); 852142914Sglebius} 853142914Sglebius 854142914Sglebiusstatic void 855142914Sglebiuscarp_send_ad_locked(struct carp_softc *sc) 856142914Sglebius{ 857142215Sglebius struct carp_header ch; 858142215Sglebius struct timeval tv; 859142215Sglebius struct carp_header *ch_ptr; 860142215Sglebius struct mbuf *m; 861142215Sglebius int len, advbase, advskew; 862142215Sglebius 863142914Sglebius CARP_SCLOCK_ASSERT(sc); 864142914Sglebius 865142215Sglebius /* bow out if we've lost our UPness or RUNNINGuiness */ 866148887Srwatson if (!((SC2IFP(sc)->if_flags & IFF_UP) && 867148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 868142215Sglebius advbase = 255; 869142215Sglebius advskew = 255; 870142215Sglebius } else { 871142215Sglebius advbase = sc->sc_advbase; 872142215Sglebius if (!carp_suppress_preempt || sc->sc_advskew > 240) 873142215Sglebius advskew = sc->sc_advskew; 874142215Sglebius else 875142215Sglebius advskew = 240; 876142215Sglebius tv.tv_sec = advbase; 877142215Sglebius tv.tv_usec = advskew * 1000000 / 256; 878142215Sglebius } 879142215Sglebius 880142215Sglebius ch.carp_version = CARP_VERSION; 881142215Sglebius ch.carp_type = CARP_ADVERTISEMENT; 882142215Sglebius ch.carp_vhid = sc->sc_vhid; 883142215Sglebius ch.carp_advbase = advbase; 884142215Sglebius ch.carp_advskew = advskew; 885142215Sglebius ch.carp_authlen = 7; /* XXX DEFINE */ 886142215Sglebius ch.carp_pad1 = 0; /* must be zero */ 887142215Sglebius ch.carp_cksum = 0; 888142215Sglebius 889142215Sglebius#ifdef INET 890142215Sglebius if (sc->sc_ia) { 891142215Sglebius struct ip *ip; 892142215Sglebius 893142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 894142215Sglebius if (m == NULL) { 895147256Sbrooks SC2IFP(sc)->if_oerrors++; 896142215Sglebius carpstats.carps_onomem++; 897142215Sglebius /* XXX maybe less ? */ 898142215Sglebius if (advbase != 255 || advskew != 255) 899142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 900142215Sglebius carp_send_ad, sc); 901142215Sglebius return; 902142215Sglebius } 903142215Sglebius len = sizeof(*ip) + sizeof(ch); 904142215Sglebius m->m_pkthdr.len = len; 905142215Sglebius m->m_pkthdr.rcvif = NULL; 906142215Sglebius m->m_len = len; 907142215Sglebius MH_ALIGN(m, m->m_len); 908142215Sglebius m->m_flags |= M_MCAST; 909142215Sglebius ip = mtod(m, struct ip *); 910142215Sglebius ip->ip_v = IPVERSION; 911142215Sglebius ip->ip_hl = sizeof(*ip) >> 2; 912142215Sglebius ip->ip_tos = IPTOS_LOWDELAY; 913142215Sglebius ip->ip_len = len; 914142215Sglebius ip->ip_id = ip_newid(); 915142215Sglebius ip->ip_off = IP_DF; 916142215Sglebius ip->ip_ttl = CARP_DFLTTL; 917142215Sglebius ip->ip_p = IPPROTO_CARP; 918142215Sglebius ip->ip_sum = 0; 919142215Sglebius ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr; 920142215Sglebius ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP); 921142215Sglebius 922142215Sglebius ch_ptr = (struct carp_header *)(&ip[1]); 923142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 924142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 925142215Sglebius return; 926142215Sglebius 927142215Sglebius m->m_data += sizeof(*ip); 928142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip)); 929142215Sglebius m->m_data -= sizeof(*ip); 930142215Sglebius 931147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 932147256Sbrooks SC2IFP(sc)->if_opackets++; 933147256Sbrooks SC2IFP(sc)->if_obytes += len; 934142215Sglebius carpstats.carps_opackets++; 935142215Sglebius 936142215Sglebius if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) { 937147256Sbrooks SC2IFP(sc)->if_oerrors++; 938142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 939142215Sglebius sc->sc_sendad_errors++; 940142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 941142215Sglebius carp_suppress_preempt++; 942142914Sglebius if (carp_suppress_preempt == 1) { 943142914Sglebius CARP_SCUNLOCK(sc); 944142215Sglebius carp_send_ad_all(); 945142914Sglebius CARP_SCLOCK(sc); 946142914Sglebius } 947142215Sglebius } 948142215Sglebius sc->sc_sendad_success = 0; 949142215Sglebius } else { 950142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 951142215Sglebius if (++sc->sc_sendad_success >= 952142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 953142215Sglebius carp_suppress_preempt--; 954142215Sglebius sc->sc_sendad_errors = 0; 955142215Sglebius } 956142215Sglebius } else 957142215Sglebius sc->sc_sendad_errors = 0; 958142215Sglebius } 959142215Sglebius } 960142215Sglebius#endif /* INET */ 961142215Sglebius#ifdef INET6 962142215Sglebius if (sc->sc_ia6) { 963142215Sglebius struct ip6_hdr *ip6; 964142215Sglebius 965142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 966142215Sglebius if (m == NULL) { 967147256Sbrooks SC2IFP(sc)->if_oerrors++; 968142215Sglebius carpstats.carps_onomem++; 969142215Sglebius /* XXX maybe less ? */ 970142215Sglebius if (advbase != 255 || advskew != 255) 971142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 972142215Sglebius carp_send_ad, sc); 973142215Sglebius return; 974142215Sglebius } 975142215Sglebius len = sizeof(*ip6) + sizeof(ch); 976142215Sglebius m->m_pkthdr.len = len; 977142215Sglebius m->m_pkthdr.rcvif = NULL; 978142215Sglebius m->m_len = len; 979142215Sglebius MH_ALIGN(m, m->m_len); 980142215Sglebius m->m_flags |= M_MCAST; 981142215Sglebius ip6 = mtod(m, struct ip6_hdr *); 982142215Sglebius bzero(ip6, sizeof(*ip6)); 983142215Sglebius ip6->ip6_vfc |= IPV6_VERSION; 984142215Sglebius ip6->ip6_hlim = CARP_DFLTTL; 985142215Sglebius ip6->ip6_nxt = IPPROTO_CARP; 986142215Sglebius bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src, 987142215Sglebius sizeof(struct in6_addr)); 988142215Sglebius /* set the multicast destination */ 989142215Sglebius 990163069Sbz ip6->ip6_dst.s6_addr16[0] = htons(0xff02); 991142215Sglebius ip6->ip6_dst.s6_addr8[15] = 0x12; 992163069Sbz if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) { 993163069Sbz SC2IFP(sc)->if_oerrors++; 994163069Sbz m_freem(m); 995163069Sbz CARP_LOG("%s: in6_setscope failed\n", __func__); 996163069Sbz return; 997163069Sbz } 998142215Sglebius 999142215Sglebius ch_ptr = (struct carp_header *)(&ip6[1]); 1000142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 1001142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 1002142215Sglebius return; 1003142215Sglebius 1004142215Sglebius m->m_data += sizeof(*ip6); 1005142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6)); 1006142215Sglebius m->m_data -= sizeof(*ip6); 1007142215Sglebius 1008147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 1009147256Sbrooks SC2IFP(sc)->if_opackets++; 1010147256Sbrooks SC2IFP(sc)->if_obytes += len; 1011142215Sglebius carpstats.carps_opackets6++; 1012142215Sglebius 1013142215Sglebius if (ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL)) { 1014147256Sbrooks SC2IFP(sc)->if_oerrors++; 1015142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 1016142215Sglebius sc->sc_sendad_errors++; 1017142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 1018142215Sglebius carp_suppress_preempt++; 1019142914Sglebius if (carp_suppress_preempt == 1) { 1020142914Sglebius CARP_SCUNLOCK(sc); 1021142215Sglebius carp_send_ad_all(); 1022142914Sglebius CARP_SCLOCK(sc); 1023142914Sglebius } 1024142215Sglebius } 1025142215Sglebius sc->sc_sendad_success = 0; 1026142215Sglebius } else { 1027142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 1028142215Sglebius if (++sc->sc_sendad_success >= 1029142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 1030142215Sglebius carp_suppress_preempt--; 1031142215Sglebius sc->sc_sendad_errors = 0; 1032142215Sglebius } 1033142215Sglebius } else 1034142215Sglebius sc->sc_sendad_errors = 0; 1035142215Sglebius } 1036142215Sglebius } 1037142215Sglebius#endif /* INET6 */ 1038142215Sglebius 1039142215Sglebius if (advbase != 255 || advskew != 255) 1040142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1041142215Sglebius carp_send_ad, sc); 1042142215Sglebius 1043142215Sglebius} 1044142215Sglebius 1045142215Sglebius/* 1046142215Sglebius * Broadcast a gratuitous ARP request containing 1047142215Sglebius * the virtual router MAC address for each IP address 1048142215Sglebius * associated with the virtual router. 1049142215Sglebius */ 1050142559Sglebiusstatic void 1051142215Sglebiuscarp_send_arp(struct carp_softc *sc) 1052142215Sglebius{ 1053142215Sglebius struct ifaddr *ifa; 1054142215Sglebius 1055147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1056142215Sglebius 1057142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET) 1058142215Sglebius continue; 1059142215Sglebius 1060152315Sru/* arprequest(sc->sc_carpdev, &in, &in, IF_LLADDR(sc->sc_ifp)); */ 1061152315Sru arp_ifinit2(sc->sc_carpdev, ifa, IF_LLADDR(sc->sc_ifp)); 1062142215Sglebius 1063142215Sglebius DELAY(1000); /* XXX */ 1064142215Sglebius } 1065142215Sglebius} 1066142215Sglebius 1067142215Sglebius#ifdef INET6 1068142559Sglebiusstatic void 1069142215Sglebiuscarp_send_na(struct carp_softc *sc) 1070142215Sglebius{ 1071142215Sglebius struct ifaddr *ifa; 1072142215Sglebius struct in6_addr *in6; 1073142215Sglebius static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; 1074142215Sglebius 1075147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1076142215Sglebius 1077142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET6) 1078142215Sglebius continue; 1079142215Sglebius 1080142215Sglebius in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; 1081142564Sglebius nd6_na_output(sc->sc_carpdev, &mcast, in6, 1082142215Sglebius ND_NA_FLAG_OVERRIDE, 1, NULL); 1083142215Sglebius DELAY(1000); /* XXX */ 1084142215Sglebius } 1085142215Sglebius} 1086142215Sglebius#endif /* INET6 */ 1087142215Sglebius 1088142559Sglebiusstatic int 1089142215Sglebiuscarp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) 1090142215Sglebius{ 1091142215Sglebius struct carp_softc *vh; 1092142215Sglebius struct ifaddr *ifa; 1093142215Sglebius int count = 0; 1094142215Sglebius 1095142914Sglebius CARP_LOCK_ASSERT(cif); 1096142914Sglebius 1097142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1098142215Sglebius if ((type == CARP_COUNT_RUNNING && 1099148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1100148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) || 1101142215Sglebius (type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) { 1102147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1103142215Sglebius ifa_list) { 1104142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 1105142215Sglebius ia->ia_addr.sin_addr.s_addr == 1106142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) 1107142215Sglebius count++; 1108142215Sglebius } 1109142215Sglebius } 1110142215Sglebius } 1111142215Sglebius return (count); 1112142215Sglebius} 1113142215Sglebius 1114142215Sglebiusint 1115142215Sglebiuscarp_iamatch(void *v, struct in_ifaddr *ia, 1116142215Sglebius struct in_addr *isaddr, u_int8_t **enaddr) 1117142215Sglebius{ 1118142215Sglebius struct carp_if *cif = v; 1119142215Sglebius struct carp_softc *vh; 1120142215Sglebius int index, count = 0; 1121142215Sglebius struct ifaddr *ifa; 1122142215Sglebius 1123142215Sglebius CARP_LOCK(cif); 1124142215Sglebius 1125142215Sglebius if (carp_opts[CARPCTL_ARPBALANCE]) { 1126142215Sglebius /* 1127142215Sglebius * XXX proof of concept implementation. 1128142215Sglebius * We use the source ip to decide which virtual host should 1129142215Sglebius * handle the request. If we're master of that virtual host, 1130142215Sglebius * then we respond, otherwise, just drop the arp packet on 1131142215Sglebius * the floor. 1132142215Sglebius */ 1133142215Sglebius count = carp_addrcount(cif, ia, CARP_COUNT_RUNNING); 1134142215Sglebius if (count == 0) { 1135142215Sglebius /* should never reach this */ 1136142215Sglebius CARP_UNLOCK(cif); 1137142215Sglebius return (0); 1138142215Sglebius } 1139142215Sglebius 1140142215Sglebius /* this should be a hash, like pf_hash() */ 1141147718Sglebius index = ntohl(isaddr->s_addr) % count; 1142142215Sglebius count = 0; 1143142215Sglebius 1144142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1145148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1146148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) { 1147147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1148142215Sglebius ifa_list) { 1149142215Sglebius if (ifa->ifa_addr->sa_family == 1150142215Sglebius AF_INET && 1151142215Sglebius ia->ia_addr.sin_addr.s_addr == 1152142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) { 1153142215Sglebius if (count == index) { 1154142215Sglebius if (vh->sc_state == 1155142215Sglebius MASTER) { 1156152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1157142215Sglebius CARP_UNLOCK(cif); 1158142215Sglebius return (1); 1159142215Sglebius } else { 1160142215Sglebius CARP_UNLOCK(cif); 1161142215Sglebius return (0); 1162142215Sglebius } 1163142215Sglebius } 1164142215Sglebius count++; 1165142215Sglebius } 1166142215Sglebius } 1167142215Sglebius } 1168142215Sglebius } 1169142215Sglebius } else { 1170142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1171148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1172148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1173152550Sglebius ia->ia_ifp == SC2IFP(vh) && 1174152550Sglebius vh->sc_state == MASTER) { 1175152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1176142215Sglebius CARP_UNLOCK(cif); 1177142215Sglebius return (1); 1178142215Sglebius } 1179142215Sglebius } 1180142215Sglebius } 1181142215Sglebius CARP_UNLOCK(cif); 1182142215Sglebius return (0); 1183142215Sglebius} 1184142215Sglebius 1185142215Sglebius#ifdef INET6 1186142641Smlaierstruct ifaddr * 1187142215Sglebiuscarp_iamatch6(void *v, struct in6_addr *taddr) 1188142215Sglebius{ 1189142215Sglebius struct carp_if *cif = v; 1190142215Sglebius struct carp_softc *vh; 1191142215Sglebius struct ifaddr *ifa; 1192142215Sglebius 1193142215Sglebius CARP_LOCK(cif); 1194142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1195147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, ifa_list) { 1196142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1197142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1198148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1199152550Sglebius (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1200152550Sglebius vh->sc_state == MASTER) { 1201142215Sglebius CARP_UNLOCK(cif); 1202142215Sglebius return (ifa); 1203142215Sglebius } 1204142215Sglebius } 1205142215Sglebius } 1206142215Sglebius CARP_UNLOCK(cif); 1207142215Sglebius 1208142215Sglebius return (NULL); 1209142215Sglebius} 1210142215Sglebius 1211142641Smlaiervoid * 1212142215Sglebiuscarp_macmatch6(void *v, struct mbuf *m, const struct in6_addr *taddr) 1213142215Sglebius{ 1214142215Sglebius struct m_tag *mtag; 1215142215Sglebius struct carp_if *cif = v; 1216142215Sglebius struct carp_softc *sc; 1217142215Sglebius struct ifaddr *ifa; 1218142215Sglebius 1219142215Sglebius CARP_LOCK(cif); 1220142215Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { 1221147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1222142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1223142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1224148887Srwatson (SC2IFP(sc)->if_flags & IFF_UP) && 1225148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING)) { 1226147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 1227142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, 1228142215Sglebius sizeof(struct ifnet *), M_NOWAIT); 1229142215Sglebius if (mtag == NULL) { 1230142215Sglebius /* better a bit than nothing */ 1231142215Sglebius CARP_UNLOCK(cif); 1232152315Sru return (IF_LLADDR(sc->sc_ifp)); 1233142215Sglebius } 1234142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), 1235142215Sglebius sizeof(struct ifnet *)); 1236142215Sglebius m_tag_prepend(m, mtag); 1237142215Sglebius 1238142215Sglebius CARP_UNLOCK(cif); 1239152315Sru return (IF_LLADDR(sc->sc_ifp)); 1240142215Sglebius } 1241142215Sglebius } 1242142215Sglebius } 1243142215Sglebius CARP_UNLOCK(cif); 1244142215Sglebius 1245142215Sglebius return (NULL); 1246142215Sglebius} 1247142215Sglebius#endif 1248142215Sglebius 1249142215Sglebiusstruct ifnet * 1250142215Sglebiuscarp_forus(void *v, void *dhost) 1251142215Sglebius{ 1252142215Sglebius struct carp_if *cif = v; 1253142215Sglebius struct carp_softc *vh; 1254142215Sglebius u_int8_t *ena = dhost; 1255142215Sglebius 1256142215Sglebius if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) 1257142215Sglebius return (NULL); 1258142215Sglebius 1259142215Sglebius CARP_LOCK(cif); 1260142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) 1261148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1262148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1263148887Srwatson vh->sc_state == MASTER && 1264152315Sru !bcmp(dhost, IF_LLADDR(vh->sc_ifp), ETHER_ADDR_LEN)) { 1265142215Sglebius CARP_UNLOCK(cif); 1266147256Sbrooks return (SC2IFP(vh)); 1267142215Sglebius } 1268142215Sglebius 1269142215Sglebius CARP_UNLOCK(cif); 1270142215Sglebius return (NULL); 1271142215Sglebius} 1272142215Sglebius 1273142559Sglebiusstatic void 1274142215Sglebiuscarp_master_down(void *v) 1275142215Sglebius{ 1276142215Sglebius struct carp_softc *sc = v; 1277142215Sglebius 1278142914Sglebius CARP_SCLOCK(sc); 1279142914Sglebius carp_master_down_locked(sc); 1280142914Sglebius CARP_SCUNLOCK(sc); 1281142914Sglebius} 1282142914Sglebius 1283142914Sglebiusstatic void 1284142914Sglebiuscarp_master_down_locked(struct carp_softc *sc) 1285142914Sglebius{ 1286142914Sglebius if (sc->sc_carpdev) 1287142914Sglebius CARP_SCLOCK_ASSERT(sc); 1288142914Sglebius 1289142215Sglebius switch (sc->sc_state) { 1290142215Sglebius case INIT: 1291142215Sglebius printf("%s: master_down event in INIT state\n", 1292147256Sbrooks SC2IFP(sc)->if_xname); 1293142215Sglebius break; 1294142215Sglebius case MASTER: 1295142215Sglebius break; 1296142215Sglebius case BACKUP: 1297142215Sglebius carp_set_state(sc, MASTER); 1298142914Sglebius carp_send_ad_locked(sc); 1299142215Sglebius carp_send_arp(sc); 1300142215Sglebius#ifdef INET6 1301142215Sglebius carp_send_na(sc); 1302142215Sglebius#endif /* INET6 */ 1303142215Sglebius carp_setrun(sc, 0); 1304142215Sglebius carp_setroute(sc, RTM_ADD); 1305142215Sglebius break; 1306142215Sglebius } 1307142215Sglebius} 1308142215Sglebius 1309142215Sglebius/* 1310142215Sglebius * When in backup state, af indicates whether to reset the master down timer 1311142215Sglebius * for v4 or v6. If it's set to zero, reset the ones which are already pending. 1312142215Sglebius */ 1313142559Sglebiusstatic void 1314142215Sglebiuscarp_setrun(struct carp_softc *sc, sa_family_t af) 1315142215Sglebius{ 1316142215Sglebius struct timeval tv; 1317142215Sglebius 1318156947Sglebius if (sc->sc_carpdev == NULL) { 1319156947Sglebius SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1320156947Sglebius carp_set_state(sc, INIT); 1321156947Sglebius return; 1322156947Sglebius } else 1323142914Sglebius CARP_SCLOCK_ASSERT(sc); 1324142914Sglebius 1325147256Sbrooks if (SC2IFP(sc)->if_flags & IFF_UP && 1326142215Sglebius sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6)) 1327148887Srwatson SC2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; 1328142215Sglebius else { 1329148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1330142215Sglebius carp_setroute(sc, RTM_DELETE); 1331142215Sglebius return; 1332142215Sglebius } 1333142215Sglebius 1334142215Sglebius switch (sc->sc_state) { 1335142215Sglebius case INIT: 1336142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) { 1337142914Sglebius carp_send_ad_locked(sc); 1338142215Sglebius carp_send_arp(sc); 1339142215Sglebius#ifdef INET6 1340142215Sglebius carp_send_na(sc); 1341142215Sglebius#endif /* INET6 */ 1342142452Sglebius CARP_DEBUG("%s: INIT -> MASTER (preempting)\n", 1343147256Sbrooks SC2IFP(sc)->if_xname); 1344142215Sglebius carp_set_state(sc, MASTER); 1345142215Sglebius carp_setroute(sc, RTM_ADD); 1346142215Sglebius } else { 1347147256Sbrooks CARP_DEBUG("%s: INIT -> BACKUP\n", SC2IFP(sc)->if_xname); 1348142215Sglebius carp_set_state(sc, BACKUP); 1349142215Sglebius carp_setroute(sc, RTM_DELETE); 1350142215Sglebius carp_setrun(sc, 0); 1351142215Sglebius } 1352142215Sglebius break; 1353142215Sglebius case BACKUP: 1354142215Sglebius callout_stop(&sc->sc_ad_tmo); 1355142215Sglebius tv.tv_sec = 3 * sc->sc_advbase; 1356142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1357142215Sglebius switch (af) { 1358142215Sglebius#ifdef INET 1359142215Sglebius case AF_INET: 1360142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1361142215Sglebius carp_master_down, sc); 1362142215Sglebius break; 1363142215Sglebius#endif /* INET */ 1364142215Sglebius#ifdef INET6 1365142215Sglebius case AF_INET6: 1366142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1367142215Sglebius carp_master_down, sc); 1368142215Sglebius break; 1369142215Sglebius#endif /* INET6 */ 1370142215Sglebius default: 1371142215Sglebius if (sc->sc_naddrs) 1372142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1373142215Sglebius carp_master_down, sc); 1374142215Sglebius if (sc->sc_naddrs6) 1375142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1376142215Sglebius carp_master_down, sc); 1377142215Sglebius break; 1378142215Sglebius } 1379142215Sglebius break; 1380142215Sglebius case MASTER: 1381142215Sglebius tv.tv_sec = sc->sc_advbase; 1382142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1383142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1384142215Sglebius carp_send_ad, sc); 1385142215Sglebius break; 1386142215Sglebius } 1387142215Sglebius} 1388142215Sglebius 1389166423Sglebiusstatic void 1390156947Sglebiuscarp_multicast_cleanup(struct carp_softc *sc) 1391156947Sglebius{ 1392156947Sglebius struct ip_moptions *imo = &sc->sc_imo; 1393156947Sglebius u_int16_t n = imo->imo_num_memberships; 1394166226Sglebius 1395156947Sglebius /* Clean up our own multicast memberships */ 1396156947Sglebius while (n-- > 0) { 1397156947Sglebius if (imo->imo_membership[n] != NULL) { 1398156947Sglebius in_delmulti(imo->imo_membership[n]); 1399156947Sglebius imo->imo_membership[n] = NULL; 1400156947Sglebius } 1401156947Sglebius } 1402170613Sbms KASSERT(imo->imo_mfilters == NULL, 1403170613Sbms ("%s: imo_mfilters != NULL", __func__)); 1404156947Sglebius imo->imo_num_memberships = 0; 1405156947Sglebius imo->imo_multicast_ifp = NULL; 1406166423Sglebius} 1407156947Sglebius 1408156947Sglebius#ifdef INET6 1409166423Sglebiusstatic void 1410166423Sglebiuscarp_multicast6_cleanup(struct carp_softc *sc) 1411166423Sglebius{ 1412166423Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1413166423Sglebius 1414156947Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1415156947Sglebius struct in6_multi_mship *imm = 1416156947Sglebius LIST_FIRST(&im6o->im6o_memberships); 1417166226Sglebius 1418156947Sglebius LIST_REMOVE(imm, i6mm_chain); 1419156947Sglebius in6_leavegroup(imm); 1420156947Sglebius } 1421156947Sglebius im6o->im6o_multicast_ifp = NULL; 1422166423Sglebius} 1423156947Sglebius#endif 1424156947Sglebius 1425142559Sglebiusstatic int 1426142215Sglebiuscarp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1427142215Sglebius{ 1428142215Sglebius struct ifnet *ifp; 1429142215Sglebius struct carp_if *cif; 1430142215Sglebius struct in_ifaddr *ia, *ia_if; 1431142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1432142215Sglebius struct in_addr addr; 1433142215Sglebius u_long iaddr = htonl(sin->sin_addr.s_addr); 1434142215Sglebius int own, error; 1435142215Sglebius 1436142215Sglebius if (sin->sin_addr.s_addr == 0) { 1437147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1438142215Sglebius carp_set_state(sc, INIT); 1439142215Sglebius if (sc->sc_naddrs) 1440147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1441142215Sglebius carp_setrun(sc, 0); 1442142215Sglebius return (0); 1443142215Sglebius } 1444142215Sglebius 1445142215Sglebius /* we have to do it by hands to check we won't match on us */ 1446142215Sglebius ia_if = NULL; own = 0; 1447142215Sglebius TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 1448142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1449147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1450142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1451142215Sglebius (iaddr & ia->ia_subnetmask) == ia->ia_subnet) { 1452142215Sglebius if (!ia_if) 1453142215Sglebius ia_if = ia; 1454142215Sglebius if (sin->sin_addr.s_addr == 1455142215Sglebius ia->ia_addr.sin_addr.s_addr) 1456142215Sglebius own++; 1457142215Sglebius } 1458142215Sglebius } 1459142215Sglebius 1460142215Sglebius if (!ia_if) 1461142215Sglebius return (EADDRNOTAVAIL); 1462142215Sglebius 1463142215Sglebius ia = ia_if; 1464142215Sglebius ifp = ia->ia_ifp; 1465142215Sglebius 1466142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1467142215Sglebius (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) 1468142215Sglebius return (EADDRNOTAVAIL); 1469142215Sglebius 1470142215Sglebius if (imo->imo_num_memberships == 0) { 1471142215Sglebius addr.s_addr = htonl(INADDR_CARP_GROUP); 1472142215Sglebius if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == NULL) 1473142215Sglebius return (ENOBUFS); 1474142215Sglebius imo->imo_num_memberships++; 1475142215Sglebius imo->imo_multicast_ifp = ifp; 1476142215Sglebius imo->imo_multicast_ttl = CARP_DFLTTL; 1477142215Sglebius imo->imo_multicast_loop = 0; 1478142215Sglebius } 1479142215Sglebius 1480142215Sglebius if (!ifp->if_carp) { 1481142215Sglebius 1482142215Sglebius MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP, 1483142215Sglebius M_WAITOK|M_ZERO); 1484142215Sglebius if (!cif) { 1485142215Sglebius error = ENOBUFS; 1486142215Sglebius goto cleanup; 1487142215Sglebius } 1488142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1489142215Sglebius FREE(cif, M_CARP); 1490142215Sglebius goto cleanup; 1491142215Sglebius } 1492142215Sglebius 1493142215Sglebius CARP_LOCK_INIT(cif); 1494142215Sglebius CARP_LOCK(cif); 1495142215Sglebius cif->vhif_ifp = ifp; 1496142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1497142215Sglebius ifp->if_carp = cif; 1498142215Sglebius 1499142215Sglebius } else { 1500142215Sglebius struct carp_softc *vr; 1501142215Sglebius 1502142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1503142215Sglebius CARP_LOCK(cif); 1504142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1505142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1506142215Sglebius CARP_UNLOCK(cif); 1507142215Sglebius error = EINVAL; 1508142215Sglebius goto cleanup; 1509142215Sglebius } 1510142215Sglebius } 1511142215Sglebius sc->sc_ia = ia; 1512142564Sglebius sc->sc_carpdev = ifp; 1513142215Sglebius 1514142215Sglebius { /* XXX prevent endless loop if already in queue */ 1515142215Sglebius struct carp_softc *vr, *after = NULL; 1516142215Sglebius int myself = 0; 1517142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1518142215Sglebius 1519142215Sglebius /* XXX: cif should not change, right? So we still hold the lock */ 1520142215Sglebius CARP_LOCK_ASSERT(cif); 1521142215Sglebius 1522142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1523142215Sglebius if (vr == sc) 1524142215Sglebius myself = 1; 1525142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1526142215Sglebius after = vr; 1527142215Sglebius } 1528142215Sglebius 1529142215Sglebius if (!myself) { 1530142215Sglebius /* We're trying to keep things in order */ 1531142215Sglebius if (after == NULL) { 1532142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1533142215Sglebius } else { 1534142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1535142215Sglebius } 1536142215Sglebius cif->vhif_nvrs++; 1537142215Sglebius } 1538142215Sglebius } 1539142215Sglebius 1540142215Sglebius sc->sc_naddrs++; 1541147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1542142215Sglebius if (own) 1543142215Sglebius sc->sc_advskew = 0; 1544144329Sglebius carp_sc_state_locked(sc); 1545142215Sglebius carp_setrun(sc, 0); 1546142215Sglebius 1547142914Sglebius CARP_UNLOCK(cif); 1548142914Sglebius 1549142215Sglebius return (0); 1550142215Sglebius 1551142215Sglebiuscleanup: 1552142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1553142215Sglebius return (error); 1554142215Sglebius} 1555142215Sglebius 1556142559Sglebiusstatic int 1557142215Sglebiuscarp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1558142215Sglebius{ 1559142215Sglebius int error = 0; 1560142215Sglebius 1561142215Sglebius if (!--sc->sc_naddrs) { 1562142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1563142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1564142215Sglebius 1565142914Sglebius CARP_LOCK(cif); 1566142215Sglebius callout_stop(&sc->sc_ad_tmo); 1567148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1568148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1569142215Sglebius sc->sc_vhid = -1; 1570142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1571142215Sglebius imo->imo_multicast_ifp = NULL; 1572142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1573142215Sglebius if (!--cif->vhif_nvrs) { 1574142564Sglebius sc->sc_carpdev->if_carp = NULL; 1575142215Sglebius CARP_LOCK_DESTROY(cif); 1576142215Sglebius FREE(cif, M_IFADDR); 1577142215Sglebius } else { 1578142215Sglebius CARP_UNLOCK(cif); 1579142215Sglebius } 1580142215Sglebius } 1581142215Sglebius 1582142215Sglebius return (error); 1583142215Sglebius} 1584142215Sglebius 1585142215Sglebius#ifdef INET6 1586142559Sglebiusstatic int 1587142215Sglebiuscarp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1588142215Sglebius{ 1589142215Sglebius struct ifnet *ifp; 1590142215Sglebius struct carp_if *cif; 1591142215Sglebius struct in6_ifaddr *ia, *ia_if; 1592142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1593142215Sglebius struct in6_multi_mship *imm; 1594148385Sume struct in6_addr in6; 1595142215Sglebius int own, error; 1596142215Sglebius 1597142215Sglebius if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 1598147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1599142215Sglebius carp_set_state(sc, INIT); 1600142215Sglebius if (sc->sc_naddrs6) 1601147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1602142215Sglebius carp_setrun(sc, 0); 1603142215Sglebius return (0); 1604142215Sglebius } 1605142215Sglebius 1606142215Sglebius /* we have to do it by hands to check we won't match on us */ 1607142215Sglebius ia_if = NULL; own = 0; 1608142215Sglebius for (ia = in6_ifaddr; ia; ia = ia->ia_next) { 1609142215Sglebius int i; 1610142215Sglebius 1611142215Sglebius for (i = 0; i < 4; i++) { 1612142215Sglebius if ((sin6->sin6_addr.s6_addr32[i] & 1613142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i]) != 1614142215Sglebius (ia->ia_addr.sin6_addr.s6_addr32[i] & 1615142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i])) 1616142215Sglebius break; 1617142215Sglebius } 1618142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1619147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1620142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1621142215Sglebius (i == 4)) { 1622142215Sglebius if (!ia_if) 1623142215Sglebius ia_if = ia; 1624142215Sglebius if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 1625142215Sglebius &ia->ia_addr.sin6_addr)) 1626142215Sglebius own++; 1627142215Sglebius } 1628142215Sglebius } 1629142215Sglebius 1630142215Sglebius if (!ia_if) 1631142215Sglebius return (EADDRNOTAVAIL); 1632142215Sglebius ia = ia_if; 1633142215Sglebius ifp = ia->ia_ifp; 1634142215Sglebius 1635142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1636142215Sglebius (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) 1637142215Sglebius return (EADDRNOTAVAIL); 1638142215Sglebius 1639142215Sglebius if (!sc->sc_naddrs6) { 1640142215Sglebius im6o->im6o_multicast_ifp = ifp; 1641142215Sglebius 1642142215Sglebius /* join CARP multicast address */ 1643148385Sume bzero(&in6, sizeof(in6)); 1644148385Sume in6.s6_addr16[0] = htons(0xff02); 1645148385Sume in6.s6_addr8[15] = 0x12; 1646148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1647142215Sglebius goto cleanup; 1648151556Smlaier if ((imm = in6_joingroup(ifp, &in6, &error, 0)) == NULL) 1649148385Sume goto cleanup; 1650142215Sglebius LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 1651142215Sglebius 1652142215Sglebius /* join solicited multicast address */ 1653148385Sume bzero(&in6, sizeof(in6)); 1654148385Sume in6.s6_addr16[0] = htons(0xff02); 1655148385Sume in6.s6_addr32[1] = 0; 1656148385Sume in6.s6_addr32[2] = htonl(1); 1657148385Sume in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; 1658148385Sume in6.s6_addr8[12] = 0xff; 1659148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1660142215Sglebius goto cleanup; 1661151556Smlaier if ((imm = in6_joingroup(ifp, &in6, &error, 0)) == NULL) 1662148385Sume goto cleanup; 1663142215Sglebius LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 1664142215Sglebius } 1665142215Sglebius 1666142215Sglebius if (!ifp->if_carp) { 1667142215Sglebius MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP, 1668142215Sglebius M_WAITOK|M_ZERO); 1669142215Sglebius if (!cif) { 1670142215Sglebius error = ENOBUFS; 1671142215Sglebius goto cleanup; 1672142215Sglebius } 1673142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1674142215Sglebius FREE(cif, M_CARP); 1675142215Sglebius goto cleanup; 1676142215Sglebius } 1677142215Sglebius 1678142215Sglebius CARP_LOCK_INIT(cif); 1679142215Sglebius CARP_LOCK(cif); 1680142215Sglebius cif->vhif_ifp = ifp; 1681142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1682142215Sglebius ifp->if_carp = cif; 1683142215Sglebius 1684142215Sglebius } else { 1685142215Sglebius struct carp_softc *vr; 1686142215Sglebius 1687142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1688142215Sglebius CARP_LOCK(cif); 1689142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1690142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1691142215Sglebius CARP_UNLOCK(cif); 1692142215Sglebius error = EINVAL; 1693142215Sglebius goto cleanup; 1694142215Sglebius } 1695142215Sglebius } 1696142215Sglebius sc->sc_ia6 = ia; 1697142564Sglebius sc->sc_carpdev = ifp; 1698142215Sglebius 1699142215Sglebius { /* XXX prevent endless loop if already in queue */ 1700142215Sglebius struct carp_softc *vr, *after = NULL; 1701142215Sglebius int myself = 0; 1702142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1703142215Sglebius CARP_LOCK_ASSERT(cif); 1704142215Sglebius 1705142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1706142215Sglebius if (vr == sc) 1707142215Sglebius myself = 1; 1708142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1709142215Sglebius after = vr; 1710142215Sglebius } 1711142215Sglebius 1712142215Sglebius if (!myself) { 1713142215Sglebius /* We're trying to keep things in order */ 1714142215Sglebius if (after == NULL) { 1715142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1716142215Sglebius } else { 1717142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1718142215Sglebius } 1719142215Sglebius cif->vhif_nvrs++; 1720142215Sglebius } 1721142215Sglebius } 1722142215Sglebius 1723142215Sglebius sc->sc_naddrs6++; 1724147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1725142215Sglebius if (own) 1726142215Sglebius sc->sc_advskew = 0; 1727144329Sglebius carp_sc_state_locked(sc); 1728142215Sglebius carp_setrun(sc, 0); 1729142215Sglebius 1730142914Sglebius CARP_UNLOCK(cif); 1731142914Sglebius 1732142215Sglebius return (0); 1733142215Sglebius 1734142215Sglebiuscleanup: 1735142215Sglebius /* clean up multicast memberships */ 1736142215Sglebius if (!sc->sc_naddrs6) { 1737142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1738142215Sglebius imm = LIST_FIRST(&im6o->im6o_memberships); 1739142215Sglebius LIST_REMOVE(imm, i6mm_chain); 1740142215Sglebius in6_leavegroup(imm); 1741142215Sglebius } 1742142215Sglebius } 1743142215Sglebius return (error); 1744142215Sglebius} 1745142215Sglebius 1746142559Sglebiusstatic int 1747142215Sglebiuscarp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1748142215Sglebius{ 1749142215Sglebius int error = 0; 1750142215Sglebius 1751142215Sglebius if (!--sc->sc_naddrs6) { 1752142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1753142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1754142215Sglebius 1755142914Sglebius CARP_LOCK(cif); 1756142215Sglebius callout_stop(&sc->sc_ad_tmo); 1757148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1758148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1759142215Sglebius sc->sc_vhid = -1; 1760142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1761142215Sglebius struct in6_multi_mship *imm = 1762142215Sglebius LIST_FIRST(&im6o->im6o_memberships); 1763142215Sglebius 1764142215Sglebius LIST_REMOVE(imm, i6mm_chain); 1765142215Sglebius in6_leavegroup(imm); 1766142215Sglebius } 1767142215Sglebius im6o->im6o_multicast_ifp = NULL; 1768142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1769142215Sglebius if (!--cif->vhif_nvrs) { 1770142215Sglebius CARP_LOCK_DESTROY(cif); 1771142564Sglebius sc->sc_carpdev->if_carp = NULL; 1772142215Sglebius FREE(cif, M_IFADDR); 1773142215Sglebius } else 1774142215Sglebius CARP_UNLOCK(cif); 1775142215Sglebius } 1776142215Sglebius 1777142215Sglebius return (error); 1778142215Sglebius} 1779142215Sglebius#endif /* INET6 */ 1780142215Sglebius 1781142559Sglebiusstatic int 1782142215Sglebiuscarp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) 1783142215Sglebius{ 1784142215Sglebius struct carp_softc *sc = ifp->if_softc, *vr; 1785142215Sglebius struct carpreq carpr; 1786142215Sglebius struct ifaddr *ifa; 1787142215Sglebius struct ifreq *ifr; 1788142215Sglebius struct ifaliasreq *ifra; 1789142914Sglebius int locked = 0, error = 0; 1790142215Sglebius 1791142215Sglebius ifa = (struct ifaddr *)addr; 1792142215Sglebius ifra = (struct ifaliasreq *)addr; 1793142215Sglebius ifr = (struct ifreq *)addr; 1794142215Sglebius 1795142215Sglebius switch (cmd) { 1796142215Sglebius case SIOCSIFADDR: 1797142215Sglebius switch (ifa->ifa_addr->sa_family) { 1798142215Sglebius#ifdef INET 1799142215Sglebius case AF_INET: 1800147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1801142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1802142215Sglebius sizeof(struct sockaddr)); 1803142215Sglebius error = carp_set_addr(sc, satosin(ifa->ifa_addr)); 1804142215Sglebius break; 1805142215Sglebius#endif /* INET */ 1806142215Sglebius#ifdef INET6 1807142215Sglebius case AF_INET6: 1808147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1809142215Sglebius error = carp_set_addr6(sc, satosin6(ifa->ifa_addr)); 1810142215Sglebius break; 1811142215Sglebius#endif /* INET6 */ 1812142215Sglebius default: 1813142215Sglebius error = EAFNOSUPPORT; 1814142215Sglebius break; 1815142215Sglebius } 1816142215Sglebius break; 1817142215Sglebius 1818142215Sglebius case SIOCAIFADDR: 1819142215Sglebius switch (ifa->ifa_addr->sa_family) { 1820142215Sglebius#ifdef INET 1821142215Sglebius case AF_INET: 1822147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1823142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1824142215Sglebius sizeof(struct sockaddr)); 1825142215Sglebius error = carp_set_addr(sc, satosin(&ifra->ifra_addr)); 1826142215Sglebius break; 1827142215Sglebius#endif /* INET */ 1828142215Sglebius#ifdef INET6 1829142215Sglebius case AF_INET6: 1830147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1831142215Sglebius error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr)); 1832142215Sglebius break; 1833142215Sglebius#endif /* INET6 */ 1834142215Sglebius default: 1835142215Sglebius error = EAFNOSUPPORT; 1836142215Sglebius break; 1837142215Sglebius } 1838142215Sglebius break; 1839142215Sglebius 1840142215Sglebius case SIOCDIFADDR: 1841142215Sglebius switch (ifa->ifa_addr->sa_family) { 1842142215Sglebius#ifdef INET 1843142215Sglebius case AF_INET: 1844142215Sglebius error = carp_del_addr(sc, satosin(&ifra->ifra_addr)); 1845142215Sglebius break; 1846142215Sglebius#endif /* INET */ 1847142215Sglebius#ifdef INET6 1848142215Sglebius case AF_INET6: 1849142215Sglebius error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr)); 1850142215Sglebius break; 1851142215Sglebius#endif /* INET6 */ 1852142215Sglebius default: 1853142215Sglebius error = EAFNOSUPPORT; 1854142215Sglebius break; 1855142215Sglebius } 1856142215Sglebius break; 1857142215Sglebius 1858142215Sglebius case SIOCSIFFLAGS: 1859142914Sglebius if (sc->sc_carpdev) { 1860142914Sglebius locked = 1; 1861142914Sglebius CARP_SCLOCK(sc); 1862142914Sglebius } 1863142215Sglebius if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { 1864142215Sglebius callout_stop(&sc->sc_ad_tmo); 1865142215Sglebius callout_stop(&sc->sc_md_tmo); 1866142215Sglebius callout_stop(&sc->sc_md6_tmo); 1867142215Sglebius if (sc->sc_state == MASTER) 1868142914Sglebius carp_send_ad_locked(sc); 1869142215Sglebius carp_set_state(sc, INIT); 1870142215Sglebius carp_setrun(sc, 0); 1871142215Sglebius } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { 1872147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1873142215Sglebius carp_setrun(sc, 0); 1874142215Sglebius } 1875142215Sglebius break; 1876142215Sglebius 1877142215Sglebius case SIOCSVH: 1878164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 1879164033Srwatson if (error) 1880142215Sglebius break; 1881142215Sglebius if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) 1882142215Sglebius break; 1883142215Sglebius error = 1; 1884142914Sglebius if (sc->sc_carpdev) { 1885142914Sglebius locked = 1; 1886142914Sglebius CARP_SCLOCK(sc); 1887142914Sglebius } 1888142215Sglebius if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { 1889142215Sglebius switch (carpr.carpr_state) { 1890142215Sglebius case BACKUP: 1891142215Sglebius callout_stop(&sc->sc_ad_tmo); 1892142215Sglebius carp_set_state(sc, BACKUP); 1893142215Sglebius carp_setrun(sc, 0); 1894142215Sglebius carp_setroute(sc, RTM_DELETE); 1895142215Sglebius break; 1896142215Sglebius case MASTER: 1897142914Sglebius carp_master_down_locked(sc); 1898142215Sglebius break; 1899142215Sglebius default: 1900142215Sglebius break; 1901142215Sglebius } 1902142215Sglebius } 1903142215Sglebius if (carpr.carpr_vhid > 0) { 1904142215Sglebius if (carpr.carpr_vhid > 255) { 1905142215Sglebius error = EINVAL; 1906142215Sglebius break; 1907142215Sglebius } 1908142564Sglebius if (sc->sc_carpdev) { 1909142215Sglebius struct carp_if *cif; 1910142564Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1911142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1912142215Sglebius if (vr != sc && 1913170373Sglebius vr->sc_vhid == carpr.carpr_vhid) { 1914170373Sglebius error = EEXIST; 1915170373Sglebius break; 1916170373Sglebius } 1917170373Sglebius if (error == EEXIST) 1918170373Sglebius break; 1919142215Sglebius } 1920142215Sglebius sc->sc_vhid = carpr.carpr_vhid; 1921152315Sru IF_LLADDR(sc->sc_ifp)[0] = 0; 1922152315Sru IF_LLADDR(sc->sc_ifp)[1] = 0; 1923152315Sru IF_LLADDR(sc->sc_ifp)[2] = 0x5e; 1924152315Sru IF_LLADDR(sc->sc_ifp)[3] = 0; 1925152315Sru IF_LLADDR(sc->sc_ifp)[4] = 1; 1926152315Sru IF_LLADDR(sc->sc_ifp)[5] = sc->sc_vhid; 1927142215Sglebius error--; 1928142215Sglebius } 1929142215Sglebius if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { 1930142215Sglebius if (carpr.carpr_advskew >= 255) { 1931142215Sglebius error = EINVAL; 1932142215Sglebius break; 1933142215Sglebius } 1934142215Sglebius if (carpr.carpr_advbase > 255) { 1935142215Sglebius error = EINVAL; 1936142215Sglebius break; 1937142215Sglebius } 1938142215Sglebius sc->sc_advbase = carpr.carpr_advbase; 1939142215Sglebius sc->sc_advskew = carpr.carpr_advskew; 1940142215Sglebius error--; 1941142215Sglebius } 1942142215Sglebius bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); 1943142215Sglebius if (error > 0) 1944142215Sglebius error = EINVAL; 1945142215Sglebius else { 1946142215Sglebius error = 0; 1947142215Sglebius carp_setrun(sc, 0); 1948142215Sglebius } 1949142215Sglebius break; 1950142215Sglebius 1951142215Sglebius case SIOCGVH: 1952142914Sglebius /* XXX: lockless read */ 1953142215Sglebius bzero(&carpr, sizeof(carpr)); 1954142215Sglebius carpr.carpr_state = sc->sc_state; 1955142215Sglebius carpr.carpr_vhid = sc->sc_vhid; 1956142215Sglebius carpr.carpr_advbase = sc->sc_advbase; 1957142215Sglebius carpr.carpr_advskew = sc->sc_advskew; 1958164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 1959164033Srwatson if (error == 0) 1960142215Sglebius bcopy(sc->sc_key, carpr.carpr_key, 1961142215Sglebius sizeof(carpr.carpr_key)); 1962142215Sglebius error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); 1963142215Sglebius break; 1964142215Sglebius 1965142215Sglebius default: 1966142215Sglebius error = EINVAL; 1967142215Sglebius } 1968142215Sglebius 1969142914Sglebius if (locked) 1970142914Sglebius CARP_SCUNLOCK(sc); 1971142914Sglebius 1972142215Sglebius carp_hmac_prepare(sc); 1973142914Sglebius 1974142215Sglebius return (error); 1975142215Sglebius} 1976142215Sglebius 1977142215Sglebius/* 1978142215Sglebius * XXX: this is looutput. We should eventually use it from there. 1979142215Sglebius */ 1980142215Sglebiusstatic int 1981142215Sglebiuscarp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 1982142215Sglebius struct rtentry *rt) 1983142215Sglebius{ 1984147611Sdwmalone u_int32_t af; 1985147611Sdwmalone 1986142215Sglebius M_ASSERTPKTHDR(m); /* check if we have the packet header */ 1987142215Sglebius 1988142215Sglebius if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 1989142215Sglebius m_freem(m); 1990142215Sglebius return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 1991142215Sglebius rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 1992142215Sglebius } 1993142215Sglebius 1994142215Sglebius ifp->if_opackets++; 1995142215Sglebius ifp->if_obytes += m->m_pkthdr.len; 1996147611Sdwmalone 1997147611Sdwmalone /* BPF writes need to be handled specially. */ 1998147611Sdwmalone if (dst->sa_family == AF_UNSPEC) { 1999147611Sdwmalone bcopy(dst->sa_data, &af, sizeof(af)); 2000147611Sdwmalone dst->sa_family = af; 2001147611Sdwmalone } 2002147611Sdwmalone 2003142215Sglebius#if 1 /* XXX */ 2004142215Sglebius switch (dst->sa_family) { 2005142215Sglebius case AF_INET: 2006142215Sglebius case AF_INET6: 2007142215Sglebius case AF_IPX: 2008142215Sglebius case AF_APPLETALK: 2009142215Sglebius break; 2010142215Sglebius default: 2011142215Sglebius printf("carp_looutput: af=%d unexpected\n", dst->sa_family); 2012142215Sglebius m_freem(m); 2013142215Sglebius return (EAFNOSUPPORT); 2014142215Sglebius } 2015142215Sglebius#endif 2016142215Sglebius return(if_simloop(ifp, m, dst->sa_family, 0)); 2017142215Sglebius} 2018142215Sglebius 2019142215Sglebius/* 2020142215Sglebius * Start output on carp interface. This function should never be called. 2021142215Sglebius */ 2022142559Sglebiusstatic void 2023142215Sglebiuscarp_start(struct ifnet *ifp) 2024142215Sglebius{ 2025142215Sglebius#ifdef DEBUG 2026142215Sglebius printf("%s: start called\n", ifp->if_xname); 2027142215Sglebius#endif 2028142215Sglebius} 2029142215Sglebius 2030142215Sglebiusint 2031142215Sglebiuscarp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, 2032142215Sglebius struct rtentry *rt) 2033142215Sglebius{ 2034142215Sglebius struct m_tag *mtag; 2035142215Sglebius struct carp_softc *sc; 2036142215Sglebius struct ifnet *carp_ifp; 2037142215Sglebius 2038142215Sglebius if (!sa) 2039142215Sglebius return (0); 2040142215Sglebius 2041142215Sglebius switch (sa->sa_family) { 2042142215Sglebius#ifdef INET 2043142215Sglebius case AF_INET: 2044142215Sglebius break; 2045142215Sglebius#endif /* INET */ 2046142215Sglebius#ifdef INET6 2047142215Sglebius case AF_INET6: 2048142215Sglebius break; 2049142215Sglebius#endif /* INET6 */ 2050142215Sglebius default: 2051142215Sglebius return (0); 2052142215Sglebius } 2053142215Sglebius 2054142215Sglebius mtag = m_tag_find(m, PACKET_TAG_CARP, NULL); 2055142215Sglebius if (mtag == NULL) 2056142215Sglebius return (0); 2057142215Sglebius 2058142215Sglebius bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *)); 2059142215Sglebius sc = carp_ifp->if_softc; 2060142215Sglebius 2061142215Sglebius /* Set the source MAC address to Virtual Router MAC Address */ 2062142215Sglebius switch (ifp->if_type) { 2063142798Syar case IFT_ETHER: 2064142798Syar case IFT_L2VLAN: { 2065142215Sglebius struct ether_header *eh; 2066142215Sglebius 2067142215Sglebius eh = mtod(m, struct ether_header *); 2068142215Sglebius eh->ether_shost[0] = 0; 2069142215Sglebius eh->ether_shost[1] = 0; 2070142215Sglebius eh->ether_shost[2] = 0x5e; 2071142215Sglebius eh->ether_shost[3] = 0; 2072142215Sglebius eh->ether_shost[4] = 1; 2073142215Sglebius eh->ether_shost[5] = sc->sc_vhid; 2074142215Sglebius } 2075142215Sglebius break; 2076142215Sglebius case IFT_FDDI: { 2077142215Sglebius struct fddi_header *fh; 2078142215Sglebius 2079142215Sglebius fh = mtod(m, struct fddi_header *); 2080142215Sglebius fh->fddi_shost[0] = 0; 2081142215Sglebius fh->fddi_shost[1] = 0; 2082142215Sglebius fh->fddi_shost[2] = 0x5e; 2083142215Sglebius fh->fddi_shost[3] = 0; 2084142215Sglebius fh->fddi_shost[4] = 1; 2085142215Sglebius fh->fddi_shost[5] = sc->sc_vhid; 2086142215Sglebius } 2087142215Sglebius break; 2088142215Sglebius case IFT_ISO88025: { 2089142215Sglebius struct iso88025_header *th; 2090142215Sglebius th = mtod(m, struct iso88025_header *); 2091142215Sglebius th->iso88025_shost[0] = 3; 2092142215Sglebius th->iso88025_shost[1] = 0; 2093142215Sglebius th->iso88025_shost[2] = 0x40 >> (sc->sc_vhid - 1); 2094142215Sglebius th->iso88025_shost[3] = 0x40000 >> (sc->sc_vhid - 1); 2095142215Sglebius th->iso88025_shost[4] = 0; 2096142215Sglebius th->iso88025_shost[5] = 0; 2097142215Sglebius } 2098142215Sglebius break; 2099142215Sglebius default: 2100142215Sglebius printf("%s: carp is not supported for this interface type\n", 2101142215Sglebius ifp->if_xname); 2102142215Sglebius return (EOPNOTSUPP); 2103142215Sglebius } 2104142215Sglebius 2105142215Sglebius return (0); 2106142215Sglebius} 2107142215Sglebius 2108142559Sglebiusstatic void 2109142215Sglebiuscarp_set_state(struct carp_softc *sc, int state) 2110142215Sglebius{ 2111142914Sglebius 2112142914Sglebius if (sc->sc_carpdev) 2113142914Sglebius CARP_SCLOCK_ASSERT(sc); 2114142914Sglebius 2115142215Sglebius if (sc->sc_state == state) 2116142215Sglebius return; 2117142215Sglebius 2118142215Sglebius sc->sc_state = state; 2119142215Sglebius switch (state) { 2120142215Sglebius case BACKUP: 2121147256Sbrooks SC2IFP(sc)->if_link_state = LINK_STATE_DOWN; 2122142215Sglebius break; 2123142215Sglebius case MASTER: 2124147256Sbrooks SC2IFP(sc)->if_link_state = LINK_STATE_UP; 2125142215Sglebius break; 2126142215Sglebius default: 2127147256Sbrooks SC2IFP(sc)->if_link_state = LINK_STATE_UNKNOWN; 2128142215Sglebius break; 2129142215Sglebius } 2130147256Sbrooks rt_ifmsg(SC2IFP(sc)); 2131142215Sglebius} 2132142215Sglebius 2133142215Sglebiusvoid 2134142215Sglebiuscarp_carpdev_state(void *v) 2135142215Sglebius{ 2136142215Sglebius struct carp_if *cif = v; 2137142914Sglebius 2138142914Sglebius CARP_LOCK(cif); 2139142914Sglebius carp_carpdev_state_locked(cif); 2140142914Sglebius CARP_UNLOCK(cif); 2141142914Sglebius} 2142142914Sglebius 2143142914Sglebiusstatic void 2144142914Sglebiuscarp_carpdev_state_locked(struct carp_if *cif) 2145142914Sglebius{ 2146142215Sglebius struct carp_softc *sc; 2147142215Sglebius 2148144329Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) 2149144329Sglebius carp_sc_state_locked(sc); 2150144329Sglebius} 2151144329Sglebius 2152144329Sglebiusstatic void 2153144329Sglebiuscarp_sc_state_locked(struct carp_softc *sc) 2154144329Sglebius{ 2155144329Sglebius CARP_SCLOCK_ASSERT(sc); 2156144329Sglebius 2157144329Sglebius if (sc->sc_carpdev->if_link_state != LINK_STATE_UP || 2158144329Sglebius !(sc->sc_carpdev->if_flags & IFF_UP)) { 2159147256Sbrooks sc->sc_flags_backup = SC2IFP(sc)->if_flags; 2160148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 2161148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 2162144329Sglebius callout_stop(&sc->sc_ad_tmo); 2163144329Sglebius callout_stop(&sc->sc_md_tmo); 2164144329Sglebius callout_stop(&sc->sc_md6_tmo); 2165144329Sglebius carp_set_state(sc, INIT); 2166144329Sglebius carp_setrun(sc, 0); 2167144329Sglebius if (!sc->sc_suppress) { 2168144329Sglebius carp_suppress_preempt++; 2169144329Sglebius if (carp_suppress_preempt == 1) { 2170144329Sglebius CARP_SCUNLOCK(sc); 2171144329Sglebius carp_send_ad_all(); 2172144329Sglebius CARP_SCLOCK(sc); 2173142215Sglebius } 2174142215Sglebius } 2175144329Sglebius sc->sc_suppress = 1; 2176144329Sglebius } else { 2177147256Sbrooks SC2IFP(sc)->if_flags |= sc->sc_flags_backup; 2178144329Sglebius carp_set_state(sc, INIT); 2179144329Sglebius carp_setrun(sc, 0); 2180144329Sglebius if (sc->sc_suppress) 2181144329Sglebius carp_suppress_preempt--; 2182144329Sglebius sc->sc_suppress = 0; 2183142215Sglebius } 2184144329Sglebius 2185144329Sglebius return; 2186142215Sglebius} 2187142215Sglebius 2188142215Sglebiusstatic int 2189142215Sglebiuscarp_modevent(module_t mod, int type, void *data) 2190142215Sglebius{ 2191142215Sglebius switch (type) { 2192142215Sglebius case MOD_LOAD: 2193156947Sglebius if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 2194156947Sglebius carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 2195156947Sglebius if (if_detach_event_tag == NULL) 2196156947Sglebius return (ENOMEM); 2197142911Sglebius mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); 2198142215Sglebius LIST_INIT(&carpif_list); 2199142215Sglebius if_clone_attach(&carp_cloner); 2200142215Sglebius break; 2201142215Sglebius 2202142215Sglebius case MOD_UNLOAD: 2203156947Sglebius EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag); 2204142215Sglebius if_clone_detach(&carp_cloner); 2205142911Sglebius mtx_destroy(&carp_mtx); 2206142215Sglebius break; 2207142215Sglebius 2208142215Sglebius default: 2209156947Sglebius return (EINVAL); 2210142215Sglebius } 2211142215Sglebius 2212156947Sglebius return (0); 2213142215Sglebius} 2214142215Sglebius 2215142215Sglebiusstatic moduledata_t carp_mod = { 2216142215Sglebius "carp", 2217142215Sglebius carp_modevent, 2218142215Sglebius 0 2219142215Sglebius}; 2220142215Sglebius 2221142215SglebiusDECLARE_MODULE(carp, carp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2222