ip_carp.c revision 166226
1142215Sglebius/* $FreeBSD: head/sys/netinet/ip_carp.c 166226 2007-01-25 17:58:16Z glebius $ */ 2142215Sglebius 3142215Sglebius/* 4142215Sglebius * Copyright (c) 2002 Michael Shalayeff. All rights reserved. 5142215Sglebius * Copyright (c) 2003 Ryan McBride. All rights reserved. 6142215Sglebius * 7142215Sglebius * Redistribution and use in source and binary forms, with or without 8142215Sglebius * modification, are permitted provided that the following conditions 9142215Sglebius * are met: 10142215Sglebius * 1. Redistributions of source code must retain the above copyright 11142215Sglebius * notice, this list of conditions and the following disclaimer. 12142215Sglebius * 2. Redistributions in binary form must reproduce the above copyright 13142215Sglebius * notice, this list of conditions and the following disclaimer in the 14142215Sglebius * documentation and/or other materials provided with the distribution. 15142215Sglebius * 16142215Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17142215Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18142215Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19142215Sglebius * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 20142215Sglebius * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21142215Sglebius * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22142215Sglebius * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23142215Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24142215Sglebius * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25142215Sglebius * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26142215Sglebius * THE POSSIBILITY OF SUCH DAMAGE. 27142215Sglebius */ 28142215Sglebius 29142215Sglebius#include "opt_carp.h" 30142215Sglebius#include "opt_bpf.h" 31142215Sglebius#include "opt_inet.h" 32142215Sglebius#include "opt_inet6.h" 33142215Sglebius 34142215Sglebius#include <sys/types.h> 35142215Sglebius#include <sys/param.h> 36142215Sglebius#include <sys/systm.h> 37142215Sglebius#include <sys/conf.h> 38142215Sglebius#include <sys/kernel.h> 39142215Sglebius#include <sys/limits.h> 40142215Sglebius#include <sys/malloc.h> 41142215Sglebius#include <sys/mbuf.h> 42142215Sglebius#include <sys/module.h> 43142215Sglebius#include <sys/time.h> 44164033Srwatson#include <sys/priv.h> 45142215Sglebius#include <sys/proc.h> 46142215Sglebius#include <sys/sysctl.h> 47142215Sglebius#include <sys/syslog.h> 48142215Sglebius#include <sys/signalvar.h> 49142215Sglebius#include <sys/filio.h> 50142215Sglebius#include <sys/sockio.h> 51142215Sglebius 52142215Sglebius#include <sys/socket.h> 53142215Sglebius#include <sys/vnode.h> 54142215Sglebius 55142215Sglebius#include <machine/stdarg.h> 56142215Sglebius 57142215Sglebius#include <net/bpf.h> 58142215Sglebius#include <net/ethernet.h> 59142215Sglebius#include <net/fddi.h> 60142215Sglebius#include <net/iso88025.h> 61142215Sglebius#include <net/if.h> 62142215Sglebius#include <net/if_clone.h> 63152410Sru#include <net/if_dl.h> 64142215Sglebius#include <net/if_types.h> 65142215Sglebius#include <net/route.h> 66142215Sglebius 67142215Sglebius#ifdef INET 68142215Sglebius#include <netinet/in.h> 69142215Sglebius#include <netinet/in_var.h> 70142215Sglebius#include <netinet/in_systm.h> 71142215Sglebius#include <netinet/ip.h> 72142215Sglebius#include <netinet/ip_var.h> 73142215Sglebius#include <netinet/if_ether.h> 74142215Sglebius#include <machine/in_cksum.h> 75142215Sglebius#endif 76142215Sglebius 77142215Sglebius#ifdef INET6 78142215Sglebius#include <netinet/icmp6.h> 79142215Sglebius#include <netinet/ip6.h> 80142215Sglebius#include <netinet6/ip6_var.h> 81148387Sume#include <netinet6/scope6_var.h> 82142215Sglebius#include <netinet6/nd6.h> 83142215Sglebius#endif 84142215Sglebius 85142215Sglebius#include <crypto/sha1.h> 86142215Sglebius#include <netinet/ip_carp.h> 87142215Sglebius 88142215Sglebius#define CARP_IFNAME "carp" 89142215Sglebiusstatic MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces"); 90142215SglebiusSYSCTL_DECL(_net_inet_carp); 91142215Sglebius 92142215Sglebiusstruct carp_softc { 93147256Sbrooks struct ifnet *sc_ifp; /* Interface clue */ 94142901Sglebius struct ifnet *sc_carpdev; /* Pointer to parent interface */ 95142215Sglebius struct in_ifaddr *sc_ia; /* primary iface address */ 96142215Sglebius struct ip_moptions sc_imo; 97142215Sglebius#ifdef INET6 98142215Sglebius struct in6_ifaddr *sc_ia6; /* primary iface address v6 */ 99142215Sglebius struct ip6_moptions sc_im6o; 100142215Sglebius#endif /* INET6 */ 101142215Sglebius TAILQ_ENTRY(carp_softc) sc_list; 102142215Sglebius 103142215Sglebius enum { INIT = 0, BACKUP, MASTER } sc_state; 104142215Sglebius 105142215Sglebius int sc_flags_backup; 106142215Sglebius int sc_suppress; 107142215Sglebius 108142215Sglebius int sc_sendad_errors; 109142215Sglebius#define CARP_SENDAD_MAX_ERRORS 3 110142215Sglebius int sc_sendad_success; 111142215Sglebius#define CARP_SENDAD_MIN_SUCCESS 3 112142215Sglebius 113142215Sglebius int sc_vhid; 114142215Sglebius int sc_advskew; 115142215Sglebius int sc_naddrs; 116142215Sglebius int sc_naddrs6; 117142215Sglebius int sc_advbase; /* seconds */ 118142215Sglebius int sc_init_counter; 119142215Sglebius u_int64_t sc_counter; 120142215Sglebius 121142215Sglebius /* authentication */ 122142215Sglebius#define CARP_HMAC_PAD 64 123142215Sglebius unsigned char sc_key[CARP_KEY_LEN]; 124142215Sglebius unsigned char sc_pad[CARP_HMAC_PAD]; 125142215Sglebius SHA1_CTX sc_sha1; 126142215Sglebius 127142215Sglebius struct callout sc_ad_tmo; /* advertisement timeout */ 128142215Sglebius struct callout sc_md_tmo; /* master down timeout */ 129142215Sglebius struct callout sc_md6_tmo; /* master down timeout */ 130142215Sglebius 131142215Sglebius LIST_ENTRY(carp_softc) sc_next; /* Interface clue */ 132142215Sglebius}; 133147256Sbrooks#define SC2IFP(sc) ((sc)->sc_ifp) 134142215Sglebius 135142215Sglebiusint carp_suppress_preempt = 0; 136142215Sglebiusint carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */ 137142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW, 138142215Sglebius &carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets"); 139142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW, 140142215Sglebius &carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode"); 141142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW, 142142215Sglebius &carp_opts[CARPCTL_LOG], 0, "log bad carp packets"); 143142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW, 144142215Sglebius &carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses"); 145146226SglebiusSYSCTL_INT(_net_inet_carp, OID_AUTO, suppress_preempt, CTLFLAG_RD, 146146226Sglebius &carp_suppress_preempt, 0, "Preemption is suppressed"); 147142215Sglebius 148142215Sglebiusstruct carpstats carpstats; 149142215SglebiusSYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW, 150142215Sglebius &carpstats, carpstats, 151142215Sglebius "CARP statistics (struct carpstats, netinet/ip_carp.h)"); 152142215Sglebius 153142215Sglebiusstruct carp_if { 154142215Sglebius TAILQ_HEAD(, carp_softc) vhif_vrs; 155142215Sglebius int vhif_nvrs; 156142215Sglebius 157142215Sglebius struct ifnet *vhif_ifp; 158142215Sglebius struct mtx vhif_mtx; 159142215Sglebius}; 160142914Sglebius 161142914Sglebius/* Get carp_if from softc. Valid after carp_set_addr{,6}. */ 162142914Sglebius#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp) 163142914Sglebius 164142215Sglebius/* lock per carp_if queue */ 165142914Sglebius#define CARP_LOCK_INIT(cif) mtx_init(&(cif)->vhif_mtx, "carp_if", \ 166142215Sglebius NULL, MTX_DEF) 167142914Sglebius#define CARP_LOCK_DESTROY(cif) mtx_destroy(&(cif)->vhif_mtx) 168142215Sglebius#define CARP_LOCK_ASSERT(cif) mtx_assert(&(cif)->vhif_mtx, MA_OWNED) 169142215Sglebius#define CARP_LOCK(cif) mtx_lock(&(cif)->vhif_mtx) 170142215Sglebius#define CARP_UNLOCK(cif) mtx_unlock(&(cif)->vhif_mtx) 171142215Sglebius 172142914Sglebius#define CARP_SCLOCK(sc) mtx_lock(&SC2CIF(sc)->vhif_mtx) 173142914Sglebius#define CARP_SCUNLOCK(sc) mtx_unlock(&SC2CIF(sc)->vhif_mtx) 174142914Sglebius#define CARP_SCLOCK_ASSERT(sc) mtx_assert(&SC2CIF(sc)->vhif_mtx, MA_OWNED) 175142914Sglebius 176142451Sglebius#define CARP_LOG(...) do { \ 177142446Sglebius if (carp_opts[CARPCTL_LOG] > 0) \ 178142446Sglebius log(LOG_INFO, __VA_ARGS__); \ 179142451Sglebius} while (0) 180142215Sglebius 181142451Sglebius#define CARP_DEBUG(...) do { \ 182142446Sglebius if (carp_opts[CARPCTL_LOG] > 1) \ 183142446Sglebius log(LOG_DEBUG, __VA_ARGS__); \ 184142451Sglebius} while (0) 185142446Sglebius 186142559Sglebiusstatic void carp_hmac_prepare(struct carp_softc *); 187142559Sglebiusstatic void carp_hmac_generate(struct carp_softc *, u_int32_t *, 188142559Sglebius unsigned char *); 189142559Sglebiusstatic int carp_hmac_verify(struct carp_softc *, u_int32_t *, 190142559Sglebius unsigned char *); 191142559Sglebiusstatic void carp_setroute(struct carp_softc *, int); 192142559Sglebiusstatic void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); 193160195Ssamstatic int carp_clone_create(struct if_clone *, int, caddr_t); 194142559Sglebiusstatic void carp_clone_destroy(struct ifnet *); 195142559Sglebiusstatic void carpdetach(struct carp_softc *); 196142559Sglebiusstatic int carp_prepare_ad(struct mbuf *, struct carp_softc *, 197142559Sglebius struct carp_header *); 198142559Sglebiusstatic void carp_send_ad_all(void); 199142559Sglebiusstatic void carp_send_ad(void *); 200142914Sglebiusstatic void carp_send_ad_locked(struct carp_softc *); 201142559Sglebiusstatic void carp_send_arp(struct carp_softc *); 202142559Sglebiusstatic void carp_master_down(void *); 203142914Sglebiusstatic void carp_master_down_locked(struct carp_softc *); 204142559Sglebiusstatic int carp_ioctl(struct ifnet *, u_long, caddr_t); 205142559Sglebiusstatic int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *, 206142559Sglebius struct rtentry *); 207142559Sglebiusstatic void carp_start(struct ifnet *); 208142559Sglebiusstatic void carp_setrun(struct carp_softc *, sa_family_t); 209142559Sglebiusstatic void carp_set_state(struct carp_softc *, int); 210142559Sglebiusstatic int carp_addrcount(struct carp_if *, struct in_ifaddr *, int); 211142215Sglebiusenum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING }; 212142215Sglebius 213156947Sglebiusstatic void carp_multicast_cleanup(struct carp_softc *); 214142559Sglebiusstatic int carp_set_addr(struct carp_softc *, struct sockaddr_in *); 215142559Sglebiusstatic int carp_del_addr(struct carp_softc *, struct sockaddr_in *); 216142914Sglebiusstatic void carp_carpdev_state_locked(struct carp_if *); 217144329Sglebiusstatic void carp_sc_state_locked(struct carp_softc *); 218142215Sglebius#ifdef INET6 219142559Sglebiusstatic void carp_send_na(struct carp_softc *); 220142559Sglebiusstatic int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); 221142559Sglebiusstatic int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *); 222142215Sglebius#endif 223142215Sglebius 224142215Sglebiusstatic LIST_HEAD(, carp_softc) carpif_list; 225142911Sglebiusstatic struct mtx carp_mtx; 226142215SglebiusIFC_SIMPLE_DECLARE(carp, 0); 227142215Sglebius 228156947Sglebiusstatic eventhandler_tag if_detach_event_tag; 229156947Sglebius 230142215Sglebiusstatic __inline u_int16_t 231142215Sglebiuscarp_cksum(struct mbuf *m, int len) 232142215Sglebius{ 233142215Sglebius return (in_cksum(m, len)); 234142215Sglebius} 235142215Sglebius 236142559Sglebiusstatic void 237142215Sglebiuscarp_hmac_prepare(struct carp_softc *sc) 238142215Sglebius{ 239142215Sglebius u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; 240142215Sglebius u_int8_t vhid = sc->sc_vhid & 0xff; 241142215Sglebius struct ifaddr *ifa; 242142215Sglebius int i; 243142215Sglebius#ifdef INET6 244142215Sglebius struct in6_addr in6; 245142215Sglebius#endif 246142215Sglebius 247142914Sglebius if (sc->sc_carpdev) 248142914Sglebius CARP_SCLOCK(sc); 249142914Sglebius 250142914Sglebius /* XXX: possible race here */ 251142914Sglebius 252142215Sglebius /* compute ipad from key */ 253142215Sglebius bzero(sc->sc_pad, sizeof(sc->sc_pad)); 254142215Sglebius bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); 255142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 256142215Sglebius sc->sc_pad[i] ^= 0x36; 257142215Sglebius 258142215Sglebius /* precompute first part of inner hash */ 259142215Sglebius SHA1Init(&sc->sc_sha1); 260142215Sglebius SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); 261142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version)); 262142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); 263142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); 264142215Sglebius#ifdef INET 265147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 266142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET) 267142215Sglebius SHA1Update(&sc->sc_sha1, 268142215Sglebius (void *)&ifatoia(ifa)->ia_addr.sin_addr.s_addr, 269142215Sglebius sizeof(struct in_addr)); 270142215Sglebius } 271142215Sglebius#endif /* INET */ 272142215Sglebius#ifdef INET6 273147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 274142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET6) { 275142215Sglebius in6 = ifatoia6(ifa)->ia_addr.sin6_addr; 276148385Sume in6_clearscope(&in6); 277142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(in6)); 278142215Sglebius } 279142215Sglebius } 280142215Sglebius#endif /* INET6 */ 281142215Sglebius 282142215Sglebius /* convert ipad to opad */ 283142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 284142215Sglebius sc->sc_pad[i] ^= 0x36 ^ 0x5c; 285142914Sglebius 286142914Sglebius if (sc->sc_carpdev) 287142914Sglebius CARP_SCUNLOCK(sc); 288142215Sglebius} 289142215Sglebius 290142559Sglebiusstatic void 291142215Sglebiuscarp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], 292142215Sglebius unsigned char md[20]) 293142215Sglebius{ 294142215Sglebius SHA1_CTX sha1ctx; 295142215Sglebius 296142215Sglebius /* fetch first half of inner hash */ 297142215Sglebius bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); 298142215Sglebius 299142215Sglebius SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter)); 300142215Sglebius SHA1Final(md, &sha1ctx); 301142215Sglebius 302142215Sglebius /* outer hash */ 303142215Sglebius SHA1Init(&sha1ctx); 304142215Sglebius SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); 305142215Sglebius SHA1Update(&sha1ctx, md, 20); 306142215Sglebius SHA1Final(md, &sha1ctx); 307142215Sglebius} 308142215Sglebius 309142559Sglebiusstatic int 310142215Sglebiuscarp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], 311142215Sglebius unsigned char md[20]) 312142215Sglebius{ 313142215Sglebius unsigned char md2[20]; 314142215Sglebius 315142914Sglebius CARP_SCLOCK_ASSERT(sc); 316142914Sglebius 317142215Sglebius carp_hmac_generate(sc, counter, md2); 318142215Sglebius 319142215Sglebius return (bcmp(md, md2, sizeof(md2))); 320142215Sglebius} 321142215Sglebius 322142559Sglebiusstatic void 323142215Sglebiuscarp_setroute(struct carp_softc *sc, int cmd) 324142215Sglebius{ 325142215Sglebius struct ifaddr *ifa; 326142215Sglebius int s; 327142215Sglebius 328142914Sglebius if (sc->sc_carpdev) 329142914Sglebius CARP_SCLOCK_ASSERT(sc); 330142914Sglebius 331142215Sglebius s = splnet(); 332147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 333142914Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 334142914Sglebius sc->sc_carpdev != NULL) { 335142215Sglebius int count = carp_addrcount( 336142564Sglebius (struct carp_if *)sc->sc_carpdev->if_carp, 337142215Sglebius ifatoia(ifa), CARP_COUNT_MASTER); 338142215Sglebius 339142215Sglebius if ((cmd == RTM_ADD && count == 1) || 340142215Sglebius (cmd == RTM_DELETE && count == 0)) 341142215Sglebius rtinit(ifa, cmd, RTF_UP | RTF_HOST); 342142215Sglebius } 343142215Sglebius#ifdef INET6 344142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET6) { 345142215Sglebius if (cmd == RTM_ADD) 346142215Sglebius in6_ifaddloop(ifa); 347142215Sglebius else 348142215Sglebius in6_ifremloop(ifa); 349142215Sglebius } 350142215Sglebius#endif /* INET6 */ 351142215Sglebius } 352142215Sglebius splx(s); 353142215Sglebius} 354142215Sglebius 355142559Sglebiusstatic int 356160195Ssamcarp_clone_create(struct if_clone *ifc, int unit, caddr_t params) 357142215Sglebius{ 358142215Sglebius 359142215Sglebius struct carp_softc *sc; 360142215Sglebius struct ifnet *ifp; 361142215Sglebius 362142215Sglebius MALLOC(sc, struct carp_softc *, sizeof(*sc), M_CARP, M_WAITOK|M_ZERO); 363147256Sbrooks ifp = SC2IFP(sc) = if_alloc(IFT_ETHER); 364147256Sbrooks if (ifp == NULL) { 365147256Sbrooks FREE(sc, M_CARP); 366147256Sbrooks return (ENOSPC); 367147256Sbrooks } 368142215Sglebius 369142215Sglebius sc->sc_flags_backup = 0; 370142215Sglebius sc->sc_suppress = 0; 371142215Sglebius sc->sc_advbase = CARP_DFLTINTV; 372142215Sglebius sc->sc_vhid = -1; /* required setting */ 373142215Sglebius sc->sc_advskew = 0; 374142215Sglebius sc->sc_init_counter = 1; 375142215Sglebius sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */ 376142215Sglebius#ifdef INET6 377142215Sglebius sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; 378142215Sglebius#endif 379160164Smlaier sc->sc_imo.imo_membership = (struct in_multi **)malloc( 380160164Smlaier (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP, 381160164Smlaier M_WAITOK); 382160164Smlaier sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; 383162627Sbms sc->sc_imo.imo_multicast_vif = -1; 384142215Sglebius 385142914Sglebius callout_init(&sc->sc_ad_tmo, NET_CALLOUT_MPSAFE); 386142914Sglebius callout_init(&sc->sc_md_tmo, NET_CALLOUT_MPSAFE); 387142914Sglebius callout_init(&sc->sc_md6_tmo, NET_CALLOUT_MPSAFE); 388142215Sglebius 389142215Sglebius ifp->if_softc = sc; 390142215Sglebius if_initname(ifp, CARP_IFNAME, unit); 391142215Sglebius ifp->if_mtu = ETHERMTU; 392151688Syar ifp->if_flags = IFF_LOOPBACK; 393142215Sglebius ifp->if_ioctl = carp_ioctl; 394142215Sglebius ifp->if_output = carp_looutput; 395142215Sglebius ifp->if_start = carp_start; 396142215Sglebius ifp->if_type = IFT_CARP; 397142215Sglebius ifp->if_snd.ifq_maxlen = ifqmaxlen; 398142215Sglebius ifp->if_hdrlen = 0; 399142215Sglebius if_attach(ifp); 400147256Sbrooks bpfattach(SC2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 401142911Sglebius mtx_lock(&carp_mtx); 402142215Sglebius LIST_INSERT_HEAD(&carpif_list, sc, sc_next); 403142911Sglebius mtx_unlock(&carp_mtx); 404142215Sglebius return (0); 405142215Sglebius} 406142215Sglebius 407142559Sglebiusstatic void 408142215Sglebiuscarp_clone_destroy(struct ifnet *ifp) 409142215Sglebius{ 410142215Sglebius struct carp_softc *sc = ifp->if_softc; 411156947Sglebius 412156947Sglebius if (sc->sc_carpdev) 413156947Sglebius CARP_SCLOCK(sc); 414156947Sglebius carpdetach(sc); 415156947Sglebius if (sc->sc_carpdev) 416156947Sglebius CARP_SCUNLOCK(sc); 417156947Sglebius 418156947Sglebius mtx_lock(&carp_mtx); 419156947Sglebius LIST_REMOVE(sc, sc_next); 420156947Sglebius mtx_unlock(&carp_mtx); 421156947Sglebius bpfdetach(ifp); 422156947Sglebius if_detach(ifp); 423156947Sglebius if_free_type(ifp, IFT_ETHER); 424160164Smlaier free(sc->sc_imo.imo_membership, M_CARP); 425156947Sglebius free(sc, M_CARP); 426156947Sglebius} 427156947Sglebius 428156947Sglebiusstatic void 429156947Sglebiuscarpdetach(struct carp_softc *sc) 430156947Sglebius{ 431142215Sglebius struct carp_if *cif; 432146226Sglebius 433156947Sglebius callout_stop(&sc->sc_ad_tmo); 434156947Sglebius callout_stop(&sc->sc_md_tmo); 435156947Sglebius callout_stop(&sc->sc_md6_tmo); 436156947Sglebius 437146226Sglebius if (sc->sc_suppress) 438146226Sglebius carp_suppress_preempt--; 439146226Sglebius sc->sc_suppress = 0; 440146226Sglebius 441156947Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) 442156947Sglebius carp_suppress_preempt--; 443156947Sglebius sc->sc_sendad_errors = 0; 444142215Sglebius 445156947Sglebius carp_set_state(sc, INIT); 446156947Sglebius SC2IFP(sc)->if_flags &= ~IFF_UP; 447156947Sglebius carp_setrun(sc, 0); 448156947Sglebius carp_multicast_cleanup(sc); 449142215Sglebius 450156947Sglebius if (sc->sc_carpdev != NULL) { 451156947Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 452156947Sglebius CARP_LOCK_ASSERT(cif); 453142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 454142215Sglebius if (!--cif->vhif_nvrs) { 455156947Sglebius ifpromisc(sc->sc_carpdev, 0); 456142564Sglebius sc->sc_carpdev->if_carp = NULL; 457142215Sglebius CARP_LOCK_DESTROY(cif); 458156947Sglebius FREE(cif, M_IFADDR); 459142215Sglebius } 460142215Sglebius } 461166226Sglebius sc->sc_carpdev = NULL; 462156947Sglebius} 463142215Sglebius 464156947Sglebius/* Detach an interface from the carp. */ 465156947Sglebiusstatic void 466156947Sglebiuscarp_ifdetach(void *arg __unused, struct ifnet *ifp) 467156947Sglebius{ 468156947Sglebius struct carp_if *cif = (struct carp_if *)ifp->if_carp; 469156947Sglebius struct carp_softc *sc, *nextsc; 470166226Sglebius 471156947Sglebius if (cif == NULL) 472156947Sglebius return; 473156947Sglebius 474156947Sglebius /* 475156947Sglebius * XXX: At the end of for() cycle the lock will be destroyed. 476156947Sglebius */ 477156947Sglebius CARP_LOCK(cif); 478156947Sglebius for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) { 479156947Sglebius nextsc = TAILQ_NEXT(sc, sc_list); 480156947Sglebius carpdetach(sc); 481156947Sglebius } 482142215Sglebius} 483142215Sglebius 484142215Sglebius/* 485142215Sglebius * process input packet. 486142215Sglebius * we have rearranged checks order compared to the rfc, 487142215Sglebius * but it seems more efficient this way or not possible otherwise. 488142215Sglebius */ 489142215Sglebiusvoid 490142215Sglebiuscarp_input(struct mbuf *m, int hlen) 491142215Sglebius{ 492142215Sglebius struct ip *ip = mtod(m, struct ip *); 493142215Sglebius struct carp_header *ch; 494142215Sglebius int iplen, len; 495142215Sglebius 496142215Sglebius carpstats.carps_ipackets++; 497142215Sglebius 498142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 499142215Sglebius m_freem(m); 500142215Sglebius return; 501142215Sglebius } 502142215Sglebius 503142215Sglebius /* check if received on a valid carp interface */ 504142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 505142215Sglebius carpstats.carps_badif++; 506142452Sglebius CARP_LOG("carp_input: packet received on non-carp " 507142452Sglebius "interface: %s\n", 508142446Sglebius m->m_pkthdr.rcvif->if_xname); 509142215Sglebius m_freem(m); 510142215Sglebius return; 511142215Sglebius } 512142215Sglebius 513142215Sglebius /* verify that the IP TTL is 255. */ 514142215Sglebius if (ip->ip_ttl != CARP_DFLTTL) { 515142215Sglebius carpstats.carps_badttl++; 516142452Sglebius CARP_LOG("carp_input: received ttl %d != 255i on %s\n", 517142446Sglebius ip->ip_ttl, 518142446Sglebius m->m_pkthdr.rcvif->if_xname); 519142215Sglebius m_freem(m); 520142215Sglebius return; 521142215Sglebius } 522142215Sglebius 523142215Sglebius iplen = ip->ip_hl << 2; 524142215Sglebius 525142215Sglebius if (m->m_pkthdr.len < iplen + sizeof(*ch)) { 526142215Sglebius carpstats.carps_badlen++; 527142446Sglebius CARP_LOG("carp_input: received len %zd < " 528142452Sglebius "sizeof(struct carp_header)\n", 529142446Sglebius m->m_len - sizeof(struct ip)); 530142215Sglebius m_freem(m); 531142215Sglebius return; 532142215Sglebius } 533142215Sglebius 534142215Sglebius if (iplen + sizeof(*ch) < m->m_len) { 535142215Sglebius if ((m = m_pullup(m, iplen + sizeof(*ch))) == NULL) { 536142215Sglebius carpstats.carps_hdrops++; 537142452Sglebius CARP_LOG("carp_input: pullup failed\n"); 538142215Sglebius return; 539142215Sglebius } 540142215Sglebius ip = mtod(m, struct ip *); 541142215Sglebius } 542142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 543142215Sglebius 544142215Sglebius /* 545142215Sglebius * verify that the received packet length is 546142215Sglebius * equal to the CARP header 547142215Sglebius */ 548142215Sglebius len = iplen + sizeof(*ch); 549142215Sglebius if (len > m->m_pkthdr.len) { 550142215Sglebius carpstats.carps_badlen++; 551142452Sglebius CARP_LOG("carp_input: packet too short %d on %s\n", 552142446Sglebius m->m_pkthdr.len, 553142446Sglebius m->m_pkthdr.rcvif->if_xname); 554142215Sglebius m_freem(m); 555142215Sglebius return; 556142215Sglebius } 557142215Sglebius 558142215Sglebius if ((m = m_pullup(m, len)) == NULL) { 559142215Sglebius carpstats.carps_hdrops++; 560142215Sglebius return; 561142215Sglebius } 562142215Sglebius ip = mtod(m, struct ip *); 563142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 564142215Sglebius 565142215Sglebius /* verify the CARP checksum */ 566142215Sglebius m->m_data += iplen; 567142215Sglebius if (carp_cksum(m, len - iplen)) { 568142215Sglebius carpstats.carps_badsum++; 569142452Sglebius CARP_LOG("carp_input: checksum failed on %s\n", 570142446Sglebius m->m_pkthdr.rcvif->if_xname); 571142215Sglebius m_freem(m); 572142215Sglebius return; 573142215Sglebius } 574142215Sglebius m->m_data -= iplen; 575142215Sglebius 576142446Sglebius carp_input_c(m, ch, AF_INET); 577142215Sglebius} 578142215Sglebius 579142215Sglebius#ifdef INET6 580142215Sglebiusint 581142215Sglebiuscarp6_input(struct mbuf **mp, int *offp, int proto) 582142215Sglebius{ 583142215Sglebius struct mbuf *m = *mp; 584142215Sglebius struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 585142215Sglebius struct carp_header *ch; 586142215Sglebius u_int len; 587142215Sglebius 588142215Sglebius carpstats.carps_ipackets6++; 589142215Sglebius 590142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 591142215Sglebius m_freem(m); 592142215Sglebius return (IPPROTO_DONE); 593142215Sglebius } 594142215Sglebius 595142215Sglebius /* check if received on a valid carp interface */ 596142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 597142215Sglebius carpstats.carps_badif++; 598142446Sglebius CARP_LOG("carp6_input: packet received on non-carp " 599142452Sglebius "interface: %s\n", 600142446Sglebius m->m_pkthdr.rcvif->if_xname); 601142215Sglebius m_freem(m); 602142215Sglebius return (IPPROTO_DONE); 603142215Sglebius } 604142215Sglebius 605142215Sglebius /* verify that the IP TTL is 255 */ 606142215Sglebius if (ip6->ip6_hlim != CARP_DFLTTL) { 607142215Sglebius carpstats.carps_badttl++; 608142452Sglebius CARP_LOG("carp6_input: received ttl %d != 255 on %s\n", 609142446Sglebius ip6->ip6_hlim, 610142446Sglebius m->m_pkthdr.rcvif->if_xname); 611142215Sglebius m_freem(m); 612142215Sglebius return (IPPROTO_DONE); 613142215Sglebius } 614142215Sglebius 615142215Sglebius /* verify that we have a complete carp packet */ 616142215Sglebius len = m->m_len; 617142215Sglebius IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch)); 618142215Sglebius if (ch == NULL) { 619142215Sglebius carpstats.carps_badlen++; 620143804Sglebius CARP_LOG("carp6_input: packet size %u too small\n", len); 621142215Sglebius return (IPPROTO_DONE); 622142215Sglebius } 623142215Sglebius 624142215Sglebius 625142215Sglebius /* verify the CARP checksum */ 626142215Sglebius m->m_data += *offp; 627142215Sglebius if (carp_cksum(m, sizeof(*ch))) { 628142215Sglebius carpstats.carps_badsum++; 629142452Sglebius CARP_LOG("carp6_input: checksum failed, on %s\n", 630142446Sglebius m->m_pkthdr.rcvif->if_xname); 631142215Sglebius m_freem(m); 632142215Sglebius return (IPPROTO_DONE); 633142215Sglebius } 634142215Sglebius m->m_data -= *offp; 635142215Sglebius 636142446Sglebius carp_input_c(m, ch, AF_INET6); 637142215Sglebius return (IPPROTO_DONE); 638142215Sglebius} 639142215Sglebius#endif /* INET6 */ 640142215Sglebius 641142559Sglebiusstatic void 642142446Sglebiuscarp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) 643142215Sglebius{ 644142215Sglebius struct ifnet *ifp = m->m_pkthdr.rcvif; 645142446Sglebius struct carp_softc *sc; 646142215Sglebius u_int64_t tmp_counter; 647142215Sglebius struct timeval sc_tv, ch_tv; 648142215Sglebius 649142215Sglebius /* verify that the VHID is valid on the receiving interface */ 650142215Sglebius CARP_LOCK(ifp->if_carp); 651142215Sglebius TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list) 652142215Sglebius if (sc->sc_vhid == ch->carp_vhid) 653142215Sglebius break; 654142914Sglebius 655148887Srwatson if (!sc || !((SC2IFP(sc)->if_flags & IFF_UP) && 656148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 657142215Sglebius carpstats.carps_badvhid++; 658142914Sglebius CARP_UNLOCK(ifp->if_carp); 659142215Sglebius m_freem(m); 660142215Sglebius return; 661142215Sglebius } 662142215Sglebius 663147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 664147256Sbrooks SC2IFP(sc)->if_ipackets++; 665147256Sbrooks SC2IFP(sc)->if_ibytes += m->m_pkthdr.len; 666142215Sglebius 667159180Scsjp if (bpf_peers_present(SC2IFP(sc)->if_bpf)) { 668142215Sglebius struct ip *ip = mtod(m, struct ip *); 669142784Sglebius uint32_t af1 = af; 670142215Sglebius 671142215Sglebius /* BPF wants net byte order */ 672142784Sglebius ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2)); 673142784Sglebius ip->ip_off = htons(ip->ip_off); 674147256Sbrooks bpf_mtap2(SC2IFP(sc)->if_bpf, &af1, sizeof(af1), m); 675142215Sglebius } 676142215Sglebius 677142215Sglebius /* verify the CARP version. */ 678142215Sglebius if (ch->carp_version != CARP_VERSION) { 679142215Sglebius carpstats.carps_badver++; 680147256Sbrooks SC2IFP(sc)->if_ierrors++; 681142914Sglebius CARP_UNLOCK(ifp->if_carp); 682142452Sglebius CARP_LOG("%s; invalid version %d\n", 683147256Sbrooks SC2IFP(sc)->if_xname, 684142446Sglebius ch->carp_version); 685142215Sglebius m_freem(m); 686142215Sglebius return; 687142215Sglebius } 688142215Sglebius 689142215Sglebius /* verify the hash */ 690142215Sglebius if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { 691142215Sglebius carpstats.carps_badauth++; 692147256Sbrooks SC2IFP(sc)->if_ierrors++; 693142914Sglebius CARP_UNLOCK(ifp->if_carp); 694147256Sbrooks CARP_LOG("%s: incorrect hash\n", SC2IFP(sc)->if_xname); 695142215Sglebius m_freem(m); 696142215Sglebius return; 697142215Sglebius } 698142215Sglebius 699142215Sglebius tmp_counter = ntohl(ch->carp_counter[0]); 700142215Sglebius tmp_counter = tmp_counter<<32; 701142215Sglebius tmp_counter += ntohl(ch->carp_counter[1]); 702142215Sglebius 703142215Sglebius /* XXX Replay protection goes here */ 704142215Sglebius 705142215Sglebius sc->sc_init_counter = 0; 706142215Sglebius sc->sc_counter = tmp_counter; 707142215Sglebius 708142215Sglebius sc_tv.tv_sec = sc->sc_advbase; 709142215Sglebius if (carp_suppress_preempt && sc->sc_advskew < 240) 710142215Sglebius sc_tv.tv_usec = 240 * 1000000 / 256; 711142215Sglebius else 712142215Sglebius sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; 713142215Sglebius ch_tv.tv_sec = ch->carp_advbase; 714142215Sglebius ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; 715142215Sglebius 716142215Sglebius switch (sc->sc_state) { 717142215Sglebius case INIT: 718142215Sglebius break; 719142215Sglebius case MASTER: 720142215Sglebius /* 721142215Sglebius * If we receive an advertisement from a master who's going to 722142215Sglebius * be more frequent than us, go into BACKUP state. 723142215Sglebius */ 724142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, >) || 725142215Sglebius timevalcmp(&sc_tv, &ch_tv, ==)) { 726142215Sglebius callout_stop(&sc->sc_ad_tmo); 727142446Sglebius CARP_DEBUG("%s: MASTER -> BACKUP " 728142452Sglebius "(more frequent advertisement received)\n", 729147256Sbrooks SC2IFP(sc)->if_xname); 730142215Sglebius carp_set_state(sc, BACKUP); 731142215Sglebius carp_setrun(sc, 0); 732142215Sglebius carp_setroute(sc, RTM_DELETE); 733142215Sglebius } 734142215Sglebius break; 735142215Sglebius case BACKUP: 736142215Sglebius /* 737142215Sglebius * If we're pre-empting masters who advertise slower than us, 738142215Sglebius * and this one claims to be slower, treat him as down. 739142215Sglebius */ 740142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && 741142215Sglebius timevalcmp(&sc_tv, &ch_tv, <)) { 742142446Sglebius CARP_DEBUG("%s: BACKUP -> MASTER " 743142452Sglebius "(preempting a slower master)\n", 744147256Sbrooks SC2IFP(sc)->if_xname); 745142914Sglebius carp_master_down_locked(sc); 746142215Sglebius break; 747142215Sglebius } 748142215Sglebius 749142215Sglebius /* 750142215Sglebius * If the master is going to advertise at such a low frequency 751142215Sglebius * that he's guaranteed to time out, we'd might as well just 752142215Sglebius * treat him as timed out now. 753142215Sglebius */ 754142215Sglebius sc_tv.tv_sec = sc->sc_advbase * 3; 755142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, <)) { 756142446Sglebius CARP_DEBUG("%s: BACKUP -> MASTER " 757142452Sglebius "(master timed out)\n", 758147256Sbrooks SC2IFP(sc)->if_xname); 759142914Sglebius carp_master_down_locked(sc); 760142215Sglebius break; 761142215Sglebius } 762142215Sglebius 763142215Sglebius /* 764142215Sglebius * Otherwise, we reset the counter and wait for the next 765142215Sglebius * advertisement. 766142215Sglebius */ 767142215Sglebius carp_setrun(sc, af); 768142215Sglebius break; 769142215Sglebius } 770142215Sglebius 771142914Sglebius CARP_UNLOCK(ifp->if_carp); 772142914Sglebius 773142215Sglebius m_freem(m); 774142215Sglebius return; 775142215Sglebius} 776142215Sglebius 777142559Sglebiusstatic int 778142215Sglebiuscarp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) 779142215Sglebius{ 780142215Sglebius struct m_tag *mtag; 781147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 782142215Sglebius 783142215Sglebius if (sc->sc_init_counter) { 784142215Sglebius /* this could also be seconds since unix epoch */ 785142215Sglebius sc->sc_counter = arc4random(); 786142215Sglebius sc->sc_counter = sc->sc_counter << 32; 787142215Sglebius sc->sc_counter += arc4random(); 788142215Sglebius } else 789142215Sglebius sc->sc_counter++; 790142215Sglebius 791142215Sglebius ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff); 792142215Sglebius ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff); 793142215Sglebius 794142215Sglebius carp_hmac_generate(sc, ch->carp_counter, ch->carp_md); 795142215Sglebius 796142215Sglebius /* Tag packet for carp_output */ 797142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT); 798142215Sglebius if (mtag == NULL) { 799142215Sglebius m_freem(m); 800147256Sbrooks SC2IFP(sc)->if_oerrors++; 801142215Sglebius return (ENOMEM); 802142215Sglebius } 803142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 804142215Sglebius m_tag_prepend(m, mtag); 805142215Sglebius 806142215Sglebius return (0); 807142215Sglebius} 808142215Sglebius 809142559Sglebiusstatic void 810142215Sglebiuscarp_send_ad_all(void) 811142215Sglebius{ 812142911Sglebius struct carp_softc *sc; 813142215Sglebius 814142911Sglebius mtx_lock(&carp_mtx); 815142911Sglebius LIST_FOREACH(sc, &carpif_list, sc_next) { 816142911Sglebius if (sc->sc_carpdev == NULL) 817142215Sglebius continue; 818142911Sglebius CARP_SCLOCK(sc); 819148887Srwatson if ((SC2IFP(sc)->if_flags & IFF_UP) && 820148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING) && 821142911Sglebius sc->sc_state == MASTER) 822142914Sglebius carp_send_ad_locked(sc); 823142911Sglebius CARP_SCUNLOCK(sc); 824142215Sglebius } 825142911Sglebius mtx_unlock(&carp_mtx); 826142215Sglebius} 827142215Sglebius 828142559Sglebiusstatic void 829142215Sglebiuscarp_send_ad(void *v) 830142215Sglebius{ 831142914Sglebius struct carp_softc *sc = v; 832142914Sglebius 833142914Sglebius CARP_SCLOCK(sc); 834142914Sglebius carp_send_ad_locked(sc); 835142914Sglebius CARP_SCUNLOCK(sc); 836142914Sglebius} 837142914Sglebius 838142914Sglebiusstatic void 839142914Sglebiuscarp_send_ad_locked(struct carp_softc *sc) 840142914Sglebius{ 841142215Sglebius struct carp_header ch; 842142215Sglebius struct timeval tv; 843142215Sglebius struct carp_header *ch_ptr; 844142215Sglebius struct mbuf *m; 845142215Sglebius int len, advbase, advskew; 846142215Sglebius 847142914Sglebius CARP_SCLOCK_ASSERT(sc); 848142914Sglebius 849142215Sglebius /* bow out if we've lost our UPness or RUNNINGuiness */ 850148887Srwatson if (!((SC2IFP(sc)->if_flags & IFF_UP) && 851148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 852142215Sglebius advbase = 255; 853142215Sglebius advskew = 255; 854142215Sglebius } else { 855142215Sglebius advbase = sc->sc_advbase; 856142215Sglebius if (!carp_suppress_preempt || sc->sc_advskew > 240) 857142215Sglebius advskew = sc->sc_advskew; 858142215Sglebius else 859142215Sglebius advskew = 240; 860142215Sglebius tv.tv_sec = advbase; 861142215Sglebius tv.tv_usec = advskew * 1000000 / 256; 862142215Sglebius } 863142215Sglebius 864142215Sglebius ch.carp_version = CARP_VERSION; 865142215Sglebius ch.carp_type = CARP_ADVERTISEMENT; 866142215Sglebius ch.carp_vhid = sc->sc_vhid; 867142215Sglebius ch.carp_advbase = advbase; 868142215Sglebius ch.carp_advskew = advskew; 869142215Sglebius ch.carp_authlen = 7; /* XXX DEFINE */ 870142215Sglebius ch.carp_pad1 = 0; /* must be zero */ 871142215Sglebius ch.carp_cksum = 0; 872142215Sglebius 873142215Sglebius#ifdef INET 874142215Sglebius if (sc->sc_ia) { 875142215Sglebius struct ip *ip; 876142215Sglebius 877142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 878142215Sglebius if (m == NULL) { 879147256Sbrooks SC2IFP(sc)->if_oerrors++; 880142215Sglebius carpstats.carps_onomem++; 881142215Sglebius /* XXX maybe less ? */ 882142215Sglebius if (advbase != 255 || advskew != 255) 883142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 884142215Sglebius carp_send_ad, sc); 885142215Sglebius return; 886142215Sglebius } 887142215Sglebius len = sizeof(*ip) + sizeof(ch); 888142215Sglebius m->m_pkthdr.len = len; 889142215Sglebius m->m_pkthdr.rcvif = NULL; 890142215Sglebius m->m_len = len; 891142215Sglebius MH_ALIGN(m, m->m_len); 892142215Sglebius m->m_flags |= M_MCAST; 893142215Sglebius ip = mtod(m, struct ip *); 894142215Sglebius ip->ip_v = IPVERSION; 895142215Sglebius ip->ip_hl = sizeof(*ip) >> 2; 896142215Sglebius ip->ip_tos = IPTOS_LOWDELAY; 897142215Sglebius ip->ip_len = len; 898142215Sglebius ip->ip_id = ip_newid(); 899142215Sglebius ip->ip_off = IP_DF; 900142215Sglebius ip->ip_ttl = CARP_DFLTTL; 901142215Sglebius ip->ip_p = IPPROTO_CARP; 902142215Sglebius ip->ip_sum = 0; 903142215Sglebius ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr; 904142215Sglebius ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP); 905142215Sglebius 906142215Sglebius ch_ptr = (struct carp_header *)(&ip[1]); 907142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 908142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 909142215Sglebius return; 910142215Sglebius 911142215Sglebius m->m_data += sizeof(*ip); 912142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip)); 913142215Sglebius m->m_data -= sizeof(*ip); 914142215Sglebius 915147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 916147256Sbrooks SC2IFP(sc)->if_opackets++; 917147256Sbrooks SC2IFP(sc)->if_obytes += len; 918142215Sglebius carpstats.carps_opackets++; 919142215Sglebius 920142215Sglebius if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) { 921147256Sbrooks SC2IFP(sc)->if_oerrors++; 922142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 923142215Sglebius sc->sc_sendad_errors++; 924142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 925142215Sglebius carp_suppress_preempt++; 926142914Sglebius if (carp_suppress_preempt == 1) { 927142914Sglebius CARP_SCUNLOCK(sc); 928142215Sglebius carp_send_ad_all(); 929142914Sglebius CARP_SCLOCK(sc); 930142914Sglebius } 931142215Sglebius } 932142215Sglebius sc->sc_sendad_success = 0; 933142215Sglebius } else { 934142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 935142215Sglebius if (++sc->sc_sendad_success >= 936142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 937142215Sglebius carp_suppress_preempt--; 938142215Sglebius sc->sc_sendad_errors = 0; 939142215Sglebius } 940142215Sglebius } else 941142215Sglebius sc->sc_sendad_errors = 0; 942142215Sglebius } 943142215Sglebius } 944142215Sglebius#endif /* INET */ 945142215Sglebius#ifdef INET6 946142215Sglebius if (sc->sc_ia6) { 947142215Sglebius struct ip6_hdr *ip6; 948142215Sglebius 949142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 950142215Sglebius if (m == NULL) { 951147256Sbrooks SC2IFP(sc)->if_oerrors++; 952142215Sglebius carpstats.carps_onomem++; 953142215Sglebius /* XXX maybe less ? */ 954142215Sglebius if (advbase != 255 || advskew != 255) 955142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 956142215Sglebius carp_send_ad, sc); 957142215Sglebius return; 958142215Sglebius } 959142215Sglebius len = sizeof(*ip6) + sizeof(ch); 960142215Sglebius m->m_pkthdr.len = len; 961142215Sglebius m->m_pkthdr.rcvif = NULL; 962142215Sglebius m->m_len = len; 963142215Sglebius MH_ALIGN(m, m->m_len); 964142215Sglebius m->m_flags |= M_MCAST; 965142215Sglebius ip6 = mtod(m, struct ip6_hdr *); 966142215Sglebius bzero(ip6, sizeof(*ip6)); 967142215Sglebius ip6->ip6_vfc |= IPV6_VERSION; 968142215Sglebius ip6->ip6_hlim = CARP_DFLTTL; 969142215Sglebius ip6->ip6_nxt = IPPROTO_CARP; 970142215Sglebius bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src, 971142215Sglebius sizeof(struct in6_addr)); 972142215Sglebius /* set the multicast destination */ 973142215Sglebius 974163069Sbz ip6->ip6_dst.s6_addr16[0] = htons(0xff02); 975142215Sglebius ip6->ip6_dst.s6_addr8[15] = 0x12; 976163069Sbz if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) { 977163069Sbz SC2IFP(sc)->if_oerrors++; 978163069Sbz m_freem(m); 979163069Sbz CARP_LOG("%s: in6_setscope failed\n", __func__); 980163069Sbz return; 981163069Sbz } 982142215Sglebius 983142215Sglebius ch_ptr = (struct carp_header *)(&ip6[1]); 984142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 985142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 986142215Sglebius return; 987142215Sglebius 988142215Sglebius m->m_data += sizeof(*ip6); 989142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6)); 990142215Sglebius m->m_data -= sizeof(*ip6); 991142215Sglebius 992147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 993147256Sbrooks SC2IFP(sc)->if_opackets++; 994147256Sbrooks SC2IFP(sc)->if_obytes += len; 995142215Sglebius carpstats.carps_opackets6++; 996142215Sglebius 997142215Sglebius if (ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL)) { 998147256Sbrooks SC2IFP(sc)->if_oerrors++; 999142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 1000142215Sglebius sc->sc_sendad_errors++; 1001142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 1002142215Sglebius carp_suppress_preempt++; 1003142914Sglebius if (carp_suppress_preempt == 1) { 1004142914Sglebius CARP_SCUNLOCK(sc); 1005142215Sglebius carp_send_ad_all(); 1006142914Sglebius CARP_SCLOCK(sc); 1007142914Sglebius } 1008142215Sglebius } 1009142215Sglebius sc->sc_sendad_success = 0; 1010142215Sglebius } else { 1011142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 1012142215Sglebius if (++sc->sc_sendad_success >= 1013142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 1014142215Sglebius carp_suppress_preempt--; 1015142215Sglebius sc->sc_sendad_errors = 0; 1016142215Sglebius } 1017142215Sglebius } else 1018142215Sglebius sc->sc_sendad_errors = 0; 1019142215Sglebius } 1020142215Sglebius } 1021142215Sglebius#endif /* INET6 */ 1022142215Sglebius 1023142215Sglebius if (advbase != 255 || advskew != 255) 1024142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1025142215Sglebius carp_send_ad, sc); 1026142215Sglebius 1027142215Sglebius} 1028142215Sglebius 1029142215Sglebius/* 1030142215Sglebius * Broadcast a gratuitous ARP request containing 1031142215Sglebius * the virtual router MAC address for each IP address 1032142215Sglebius * associated with the virtual router. 1033142215Sglebius */ 1034142559Sglebiusstatic void 1035142215Sglebiuscarp_send_arp(struct carp_softc *sc) 1036142215Sglebius{ 1037142215Sglebius struct ifaddr *ifa; 1038142215Sglebius 1039147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1040142215Sglebius 1041142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET) 1042142215Sglebius continue; 1043142215Sglebius 1044152315Sru/* arprequest(sc->sc_carpdev, &in, &in, IF_LLADDR(sc->sc_ifp)); */ 1045152315Sru arp_ifinit2(sc->sc_carpdev, ifa, IF_LLADDR(sc->sc_ifp)); 1046142215Sglebius 1047142215Sglebius DELAY(1000); /* XXX */ 1048142215Sglebius } 1049142215Sglebius} 1050142215Sglebius 1051142215Sglebius#ifdef INET6 1052142559Sglebiusstatic void 1053142215Sglebiuscarp_send_na(struct carp_softc *sc) 1054142215Sglebius{ 1055142215Sglebius struct ifaddr *ifa; 1056142215Sglebius struct in6_addr *in6; 1057142215Sglebius static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; 1058142215Sglebius 1059147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1060142215Sglebius 1061142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET6) 1062142215Sglebius continue; 1063142215Sglebius 1064142215Sglebius in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; 1065142564Sglebius nd6_na_output(sc->sc_carpdev, &mcast, in6, 1066142215Sglebius ND_NA_FLAG_OVERRIDE, 1, NULL); 1067142215Sglebius DELAY(1000); /* XXX */ 1068142215Sglebius } 1069142215Sglebius} 1070142215Sglebius#endif /* INET6 */ 1071142215Sglebius 1072142559Sglebiusstatic int 1073142215Sglebiuscarp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) 1074142215Sglebius{ 1075142215Sglebius struct carp_softc *vh; 1076142215Sglebius struct ifaddr *ifa; 1077142215Sglebius int count = 0; 1078142215Sglebius 1079142914Sglebius CARP_LOCK_ASSERT(cif); 1080142914Sglebius 1081142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1082142215Sglebius if ((type == CARP_COUNT_RUNNING && 1083148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1084148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) || 1085142215Sglebius (type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) { 1086147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1087142215Sglebius ifa_list) { 1088142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 1089142215Sglebius ia->ia_addr.sin_addr.s_addr == 1090142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) 1091142215Sglebius count++; 1092142215Sglebius } 1093142215Sglebius } 1094142215Sglebius } 1095142215Sglebius return (count); 1096142215Sglebius} 1097142215Sglebius 1098142215Sglebiusint 1099142215Sglebiuscarp_iamatch(void *v, struct in_ifaddr *ia, 1100142215Sglebius struct in_addr *isaddr, u_int8_t **enaddr) 1101142215Sglebius{ 1102142215Sglebius struct carp_if *cif = v; 1103142215Sglebius struct carp_softc *vh; 1104142215Sglebius int index, count = 0; 1105142215Sglebius struct ifaddr *ifa; 1106142215Sglebius 1107142215Sglebius CARP_LOCK(cif); 1108142215Sglebius 1109142215Sglebius if (carp_opts[CARPCTL_ARPBALANCE]) { 1110142215Sglebius /* 1111142215Sglebius * XXX proof of concept implementation. 1112142215Sglebius * We use the source ip to decide which virtual host should 1113142215Sglebius * handle the request. If we're master of that virtual host, 1114142215Sglebius * then we respond, otherwise, just drop the arp packet on 1115142215Sglebius * the floor. 1116142215Sglebius */ 1117142215Sglebius count = carp_addrcount(cif, ia, CARP_COUNT_RUNNING); 1118142215Sglebius if (count == 0) { 1119142215Sglebius /* should never reach this */ 1120142215Sglebius CARP_UNLOCK(cif); 1121142215Sglebius return (0); 1122142215Sglebius } 1123142215Sglebius 1124142215Sglebius /* this should be a hash, like pf_hash() */ 1125147718Sglebius index = ntohl(isaddr->s_addr) % count; 1126142215Sglebius count = 0; 1127142215Sglebius 1128142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1129148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1130148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) { 1131147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1132142215Sglebius ifa_list) { 1133142215Sglebius if (ifa->ifa_addr->sa_family == 1134142215Sglebius AF_INET && 1135142215Sglebius ia->ia_addr.sin_addr.s_addr == 1136142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) { 1137142215Sglebius if (count == index) { 1138142215Sglebius if (vh->sc_state == 1139142215Sglebius MASTER) { 1140152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1141142215Sglebius CARP_UNLOCK(cif); 1142142215Sglebius return (1); 1143142215Sglebius } else { 1144142215Sglebius CARP_UNLOCK(cif); 1145142215Sglebius return (0); 1146142215Sglebius } 1147142215Sglebius } 1148142215Sglebius count++; 1149142215Sglebius } 1150142215Sglebius } 1151142215Sglebius } 1152142215Sglebius } 1153142215Sglebius } else { 1154142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1155148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1156148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1157152550Sglebius ia->ia_ifp == SC2IFP(vh) && 1158152550Sglebius vh->sc_state == MASTER) { 1159152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1160142215Sglebius CARP_UNLOCK(cif); 1161142215Sglebius return (1); 1162142215Sglebius } 1163142215Sglebius } 1164142215Sglebius } 1165142215Sglebius CARP_UNLOCK(cif); 1166142215Sglebius return (0); 1167142215Sglebius} 1168142215Sglebius 1169142215Sglebius#ifdef INET6 1170142641Smlaierstruct ifaddr * 1171142215Sglebiuscarp_iamatch6(void *v, struct in6_addr *taddr) 1172142215Sglebius{ 1173142215Sglebius struct carp_if *cif = v; 1174142215Sglebius struct carp_softc *vh; 1175142215Sglebius struct ifaddr *ifa; 1176142215Sglebius 1177142215Sglebius CARP_LOCK(cif); 1178142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1179147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, ifa_list) { 1180142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1181142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1182148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1183152550Sglebius (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1184152550Sglebius vh->sc_state == MASTER) { 1185142215Sglebius CARP_UNLOCK(cif); 1186142215Sglebius return (ifa); 1187142215Sglebius } 1188142215Sglebius } 1189142215Sglebius } 1190142215Sglebius CARP_UNLOCK(cif); 1191142215Sglebius 1192142215Sglebius return (NULL); 1193142215Sglebius} 1194142215Sglebius 1195142641Smlaiervoid * 1196142215Sglebiuscarp_macmatch6(void *v, struct mbuf *m, const struct in6_addr *taddr) 1197142215Sglebius{ 1198142215Sglebius struct m_tag *mtag; 1199142215Sglebius struct carp_if *cif = v; 1200142215Sglebius struct carp_softc *sc; 1201142215Sglebius struct ifaddr *ifa; 1202142215Sglebius 1203142215Sglebius CARP_LOCK(cif); 1204142215Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { 1205147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1206142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1207142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1208148887Srwatson (SC2IFP(sc)->if_flags & IFF_UP) && 1209148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING)) { 1210147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 1211142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, 1212142215Sglebius sizeof(struct ifnet *), M_NOWAIT); 1213142215Sglebius if (mtag == NULL) { 1214142215Sglebius /* better a bit than nothing */ 1215142215Sglebius CARP_UNLOCK(cif); 1216152315Sru return (IF_LLADDR(sc->sc_ifp)); 1217142215Sglebius } 1218142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), 1219142215Sglebius sizeof(struct ifnet *)); 1220142215Sglebius m_tag_prepend(m, mtag); 1221142215Sglebius 1222142215Sglebius CARP_UNLOCK(cif); 1223152315Sru return (IF_LLADDR(sc->sc_ifp)); 1224142215Sglebius } 1225142215Sglebius } 1226142215Sglebius } 1227142215Sglebius CARP_UNLOCK(cif); 1228142215Sglebius 1229142215Sglebius return (NULL); 1230142215Sglebius} 1231142215Sglebius#endif 1232142215Sglebius 1233142215Sglebiusstruct ifnet * 1234142215Sglebiuscarp_forus(void *v, void *dhost) 1235142215Sglebius{ 1236142215Sglebius struct carp_if *cif = v; 1237142215Sglebius struct carp_softc *vh; 1238142215Sglebius u_int8_t *ena = dhost; 1239142215Sglebius 1240142215Sglebius if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) 1241142215Sglebius return (NULL); 1242142215Sglebius 1243142215Sglebius CARP_LOCK(cif); 1244142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) 1245148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1246148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1247148887Srwatson vh->sc_state == MASTER && 1248152315Sru !bcmp(dhost, IF_LLADDR(vh->sc_ifp), ETHER_ADDR_LEN)) { 1249142215Sglebius CARP_UNLOCK(cif); 1250147256Sbrooks return (SC2IFP(vh)); 1251142215Sglebius } 1252142215Sglebius 1253142215Sglebius CARP_UNLOCK(cif); 1254142215Sglebius return (NULL); 1255142215Sglebius} 1256142215Sglebius 1257142559Sglebiusstatic void 1258142215Sglebiuscarp_master_down(void *v) 1259142215Sglebius{ 1260142215Sglebius struct carp_softc *sc = v; 1261142215Sglebius 1262142914Sglebius CARP_SCLOCK(sc); 1263142914Sglebius carp_master_down_locked(sc); 1264142914Sglebius CARP_SCUNLOCK(sc); 1265142914Sglebius} 1266142914Sglebius 1267142914Sglebiusstatic void 1268142914Sglebiuscarp_master_down_locked(struct carp_softc *sc) 1269142914Sglebius{ 1270142914Sglebius if (sc->sc_carpdev) 1271142914Sglebius CARP_SCLOCK_ASSERT(sc); 1272142914Sglebius 1273142215Sglebius switch (sc->sc_state) { 1274142215Sglebius case INIT: 1275142215Sglebius printf("%s: master_down event in INIT state\n", 1276147256Sbrooks SC2IFP(sc)->if_xname); 1277142215Sglebius break; 1278142215Sglebius case MASTER: 1279142215Sglebius break; 1280142215Sglebius case BACKUP: 1281142215Sglebius carp_set_state(sc, MASTER); 1282142914Sglebius carp_send_ad_locked(sc); 1283142215Sglebius carp_send_arp(sc); 1284142215Sglebius#ifdef INET6 1285142215Sglebius carp_send_na(sc); 1286142215Sglebius#endif /* INET6 */ 1287142215Sglebius carp_setrun(sc, 0); 1288142215Sglebius carp_setroute(sc, RTM_ADD); 1289142215Sglebius break; 1290142215Sglebius } 1291142215Sglebius} 1292142215Sglebius 1293142215Sglebius/* 1294142215Sglebius * When in backup state, af indicates whether to reset the master down timer 1295142215Sglebius * for v4 or v6. If it's set to zero, reset the ones which are already pending. 1296142215Sglebius */ 1297142559Sglebiusstatic void 1298142215Sglebiuscarp_setrun(struct carp_softc *sc, sa_family_t af) 1299142215Sglebius{ 1300142215Sglebius struct timeval tv; 1301142215Sglebius 1302156947Sglebius if (sc->sc_carpdev == NULL) { 1303156947Sglebius SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1304156947Sglebius carp_set_state(sc, INIT); 1305156947Sglebius return; 1306156947Sglebius } else 1307142914Sglebius CARP_SCLOCK_ASSERT(sc); 1308142914Sglebius 1309147256Sbrooks if (SC2IFP(sc)->if_flags & IFF_UP && 1310142215Sglebius sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6)) 1311148887Srwatson SC2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; 1312142215Sglebius else { 1313148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1314142215Sglebius carp_setroute(sc, RTM_DELETE); 1315142215Sglebius return; 1316142215Sglebius } 1317142215Sglebius 1318142215Sglebius switch (sc->sc_state) { 1319142215Sglebius case INIT: 1320142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) { 1321142914Sglebius carp_send_ad_locked(sc); 1322142215Sglebius carp_send_arp(sc); 1323142215Sglebius#ifdef INET6 1324142215Sglebius carp_send_na(sc); 1325142215Sglebius#endif /* INET6 */ 1326142452Sglebius CARP_DEBUG("%s: INIT -> MASTER (preempting)\n", 1327147256Sbrooks SC2IFP(sc)->if_xname); 1328142215Sglebius carp_set_state(sc, MASTER); 1329142215Sglebius carp_setroute(sc, RTM_ADD); 1330142215Sglebius } else { 1331147256Sbrooks CARP_DEBUG("%s: INIT -> BACKUP\n", SC2IFP(sc)->if_xname); 1332142215Sglebius carp_set_state(sc, BACKUP); 1333142215Sglebius carp_setroute(sc, RTM_DELETE); 1334142215Sglebius carp_setrun(sc, 0); 1335142215Sglebius } 1336142215Sglebius break; 1337142215Sglebius case BACKUP: 1338142215Sglebius callout_stop(&sc->sc_ad_tmo); 1339142215Sglebius tv.tv_sec = 3 * sc->sc_advbase; 1340142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1341142215Sglebius switch (af) { 1342142215Sglebius#ifdef INET 1343142215Sglebius case AF_INET: 1344142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1345142215Sglebius carp_master_down, sc); 1346142215Sglebius break; 1347142215Sglebius#endif /* INET */ 1348142215Sglebius#ifdef INET6 1349142215Sglebius case AF_INET6: 1350142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1351142215Sglebius carp_master_down, sc); 1352142215Sglebius break; 1353142215Sglebius#endif /* INET6 */ 1354142215Sglebius default: 1355142215Sglebius if (sc->sc_naddrs) 1356142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1357142215Sglebius carp_master_down, sc); 1358142215Sglebius if (sc->sc_naddrs6) 1359142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1360142215Sglebius carp_master_down, sc); 1361142215Sglebius break; 1362142215Sglebius } 1363142215Sglebius break; 1364142215Sglebius case MASTER: 1365142215Sglebius tv.tv_sec = sc->sc_advbase; 1366142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1367142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1368142215Sglebius carp_send_ad, sc); 1369142215Sglebius break; 1370142215Sglebius } 1371142215Sglebius} 1372142215Sglebius 1373156947Sglebiusvoid 1374156947Sglebiuscarp_multicast_cleanup(struct carp_softc *sc) 1375156947Sglebius{ 1376156947Sglebius struct ip_moptions *imo = &sc->sc_imo; 1377156947Sglebius#ifdef INET6 1378156947Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1379166226Sglebius#endif 1380156947Sglebius u_int16_t n = imo->imo_num_memberships; 1381166226Sglebius 1382156947Sglebius /* Clean up our own multicast memberships */ 1383156947Sglebius while (n-- > 0) { 1384156947Sglebius if (imo->imo_membership[n] != NULL) { 1385156947Sglebius in_delmulti(imo->imo_membership[n]); 1386156947Sglebius imo->imo_membership[n] = NULL; 1387156947Sglebius } 1388156947Sglebius } 1389156947Sglebius imo->imo_num_memberships = 0; 1390156947Sglebius imo->imo_multicast_ifp = NULL; 1391156947Sglebius 1392156947Sglebius#ifdef INET6 1393156947Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1394156947Sglebius struct in6_multi_mship *imm = 1395156947Sglebius LIST_FIRST(&im6o->im6o_memberships); 1396166226Sglebius 1397156947Sglebius LIST_REMOVE(imm, i6mm_chain); 1398156947Sglebius in6_leavegroup(imm); 1399156947Sglebius } 1400156947Sglebius im6o->im6o_multicast_ifp = NULL; 1401156947Sglebius#endif 1402156947Sglebius} 1403156947Sglebius 1404142559Sglebiusstatic int 1405142215Sglebiuscarp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1406142215Sglebius{ 1407142215Sglebius struct ifnet *ifp; 1408142215Sglebius struct carp_if *cif; 1409142215Sglebius struct in_ifaddr *ia, *ia_if; 1410142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1411142215Sglebius struct in_addr addr; 1412142215Sglebius u_long iaddr = htonl(sin->sin_addr.s_addr); 1413142215Sglebius int own, error; 1414142215Sglebius 1415142215Sglebius if (sin->sin_addr.s_addr == 0) { 1416147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1417142215Sglebius carp_set_state(sc, INIT); 1418142215Sglebius if (sc->sc_naddrs) 1419147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1420142215Sglebius carp_setrun(sc, 0); 1421142215Sglebius return (0); 1422142215Sglebius } 1423142215Sglebius 1424142215Sglebius /* we have to do it by hands to check we won't match on us */ 1425142215Sglebius ia_if = NULL; own = 0; 1426142215Sglebius TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 1427142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1428147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1429142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1430142215Sglebius (iaddr & ia->ia_subnetmask) == ia->ia_subnet) { 1431142215Sglebius if (!ia_if) 1432142215Sglebius ia_if = ia; 1433142215Sglebius if (sin->sin_addr.s_addr == 1434142215Sglebius ia->ia_addr.sin_addr.s_addr) 1435142215Sglebius own++; 1436142215Sglebius } 1437142215Sglebius } 1438142215Sglebius 1439142215Sglebius if (!ia_if) 1440142215Sglebius return (EADDRNOTAVAIL); 1441142215Sglebius 1442142215Sglebius ia = ia_if; 1443142215Sglebius ifp = ia->ia_ifp; 1444142215Sglebius 1445142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1446142215Sglebius (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) 1447142215Sglebius return (EADDRNOTAVAIL); 1448142215Sglebius 1449142215Sglebius if (imo->imo_num_memberships == 0) { 1450142215Sglebius addr.s_addr = htonl(INADDR_CARP_GROUP); 1451142215Sglebius if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == NULL) 1452142215Sglebius return (ENOBUFS); 1453142215Sglebius imo->imo_num_memberships++; 1454142215Sglebius imo->imo_multicast_ifp = ifp; 1455142215Sglebius imo->imo_multicast_ttl = CARP_DFLTTL; 1456142215Sglebius imo->imo_multicast_loop = 0; 1457142215Sglebius } 1458142215Sglebius 1459142215Sglebius if (!ifp->if_carp) { 1460142215Sglebius 1461142215Sglebius MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP, 1462142215Sglebius M_WAITOK|M_ZERO); 1463142215Sglebius if (!cif) { 1464142215Sglebius error = ENOBUFS; 1465142215Sglebius goto cleanup; 1466142215Sglebius } 1467142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1468142215Sglebius FREE(cif, M_CARP); 1469142215Sglebius goto cleanup; 1470142215Sglebius } 1471142215Sglebius 1472142215Sglebius CARP_LOCK_INIT(cif); 1473142215Sglebius CARP_LOCK(cif); 1474142215Sglebius cif->vhif_ifp = ifp; 1475142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1476142215Sglebius ifp->if_carp = cif; 1477142215Sglebius 1478142215Sglebius } else { 1479142215Sglebius struct carp_softc *vr; 1480142215Sglebius 1481142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1482142215Sglebius CARP_LOCK(cif); 1483142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1484142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1485142215Sglebius CARP_UNLOCK(cif); 1486142215Sglebius error = EINVAL; 1487142215Sglebius goto cleanup; 1488142215Sglebius } 1489142215Sglebius } 1490142215Sglebius sc->sc_ia = ia; 1491142564Sglebius sc->sc_carpdev = ifp; 1492142215Sglebius 1493142215Sglebius { /* XXX prevent endless loop if already in queue */ 1494142215Sglebius struct carp_softc *vr, *after = NULL; 1495142215Sglebius int myself = 0; 1496142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1497142215Sglebius 1498142215Sglebius /* XXX: cif should not change, right? So we still hold the lock */ 1499142215Sglebius CARP_LOCK_ASSERT(cif); 1500142215Sglebius 1501142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1502142215Sglebius if (vr == sc) 1503142215Sglebius myself = 1; 1504142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1505142215Sglebius after = vr; 1506142215Sglebius } 1507142215Sglebius 1508142215Sglebius if (!myself) { 1509142215Sglebius /* We're trying to keep things in order */ 1510142215Sglebius if (after == NULL) { 1511142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1512142215Sglebius } else { 1513142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1514142215Sglebius } 1515142215Sglebius cif->vhif_nvrs++; 1516142215Sglebius } 1517142215Sglebius } 1518142215Sglebius 1519142215Sglebius sc->sc_naddrs++; 1520147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1521142215Sglebius if (own) 1522142215Sglebius sc->sc_advskew = 0; 1523144329Sglebius carp_sc_state_locked(sc); 1524142215Sglebius carp_setrun(sc, 0); 1525142215Sglebius 1526142914Sglebius CARP_UNLOCK(cif); 1527142914Sglebius 1528142215Sglebius return (0); 1529142215Sglebius 1530142215Sglebiuscleanup: 1531142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1532142215Sglebius return (error); 1533142215Sglebius} 1534142215Sglebius 1535142559Sglebiusstatic int 1536142215Sglebiuscarp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1537142215Sglebius{ 1538142215Sglebius int error = 0; 1539142215Sglebius 1540142215Sglebius if (!--sc->sc_naddrs) { 1541142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1542142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1543142215Sglebius 1544142914Sglebius CARP_LOCK(cif); 1545142215Sglebius callout_stop(&sc->sc_ad_tmo); 1546148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1547148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1548142215Sglebius sc->sc_vhid = -1; 1549142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1550142215Sglebius imo->imo_multicast_ifp = NULL; 1551142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1552142215Sglebius if (!--cif->vhif_nvrs) { 1553142564Sglebius sc->sc_carpdev->if_carp = NULL; 1554142215Sglebius CARP_LOCK_DESTROY(cif); 1555142215Sglebius FREE(cif, M_IFADDR); 1556142215Sglebius } else { 1557142215Sglebius CARP_UNLOCK(cif); 1558142215Sglebius } 1559142215Sglebius } 1560142215Sglebius 1561142215Sglebius return (error); 1562142215Sglebius} 1563142215Sglebius 1564142215Sglebius#ifdef INET6 1565142559Sglebiusstatic int 1566142215Sglebiuscarp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1567142215Sglebius{ 1568142215Sglebius struct ifnet *ifp; 1569142215Sglebius struct carp_if *cif; 1570142215Sglebius struct in6_ifaddr *ia, *ia_if; 1571142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1572142215Sglebius struct in6_multi_mship *imm; 1573148385Sume struct in6_addr in6; 1574142215Sglebius int own, error; 1575142215Sglebius 1576142215Sglebius if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 1577147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1578142215Sglebius carp_set_state(sc, INIT); 1579142215Sglebius if (sc->sc_naddrs6) 1580147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1581142215Sglebius carp_setrun(sc, 0); 1582142215Sglebius return (0); 1583142215Sglebius } 1584142215Sglebius 1585142215Sglebius /* we have to do it by hands to check we won't match on us */ 1586142215Sglebius ia_if = NULL; own = 0; 1587142215Sglebius for (ia = in6_ifaddr; ia; ia = ia->ia_next) { 1588142215Sglebius int i; 1589142215Sglebius 1590142215Sglebius for (i = 0; i < 4; i++) { 1591142215Sglebius if ((sin6->sin6_addr.s6_addr32[i] & 1592142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i]) != 1593142215Sglebius (ia->ia_addr.sin6_addr.s6_addr32[i] & 1594142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i])) 1595142215Sglebius break; 1596142215Sglebius } 1597142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1598147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1599142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1600142215Sglebius (i == 4)) { 1601142215Sglebius if (!ia_if) 1602142215Sglebius ia_if = ia; 1603142215Sglebius if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 1604142215Sglebius &ia->ia_addr.sin6_addr)) 1605142215Sglebius own++; 1606142215Sglebius } 1607142215Sglebius } 1608142215Sglebius 1609142215Sglebius if (!ia_if) 1610142215Sglebius return (EADDRNOTAVAIL); 1611142215Sglebius ia = ia_if; 1612142215Sglebius ifp = ia->ia_ifp; 1613142215Sglebius 1614142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1615142215Sglebius (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) 1616142215Sglebius return (EADDRNOTAVAIL); 1617142215Sglebius 1618142215Sglebius if (!sc->sc_naddrs6) { 1619142215Sglebius im6o->im6o_multicast_ifp = ifp; 1620142215Sglebius 1621142215Sglebius /* join CARP multicast address */ 1622148385Sume bzero(&in6, sizeof(in6)); 1623148385Sume in6.s6_addr16[0] = htons(0xff02); 1624148385Sume in6.s6_addr8[15] = 0x12; 1625148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1626142215Sglebius goto cleanup; 1627151556Smlaier if ((imm = in6_joingroup(ifp, &in6, &error, 0)) == NULL) 1628148385Sume goto cleanup; 1629142215Sglebius LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 1630142215Sglebius 1631142215Sglebius /* join solicited multicast address */ 1632148385Sume bzero(&in6, sizeof(in6)); 1633148385Sume in6.s6_addr16[0] = htons(0xff02); 1634148385Sume in6.s6_addr32[1] = 0; 1635148385Sume in6.s6_addr32[2] = htonl(1); 1636148385Sume in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; 1637148385Sume in6.s6_addr8[12] = 0xff; 1638148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1639142215Sglebius goto cleanup; 1640151556Smlaier if ((imm = in6_joingroup(ifp, &in6, &error, 0)) == NULL) 1641148385Sume goto cleanup; 1642142215Sglebius LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 1643142215Sglebius } 1644142215Sglebius 1645142215Sglebius if (!ifp->if_carp) { 1646142215Sglebius MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP, 1647142215Sglebius M_WAITOK|M_ZERO); 1648142215Sglebius if (!cif) { 1649142215Sglebius error = ENOBUFS; 1650142215Sglebius goto cleanup; 1651142215Sglebius } 1652142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1653142215Sglebius FREE(cif, M_CARP); 1654142215Sglebius goto cleanup; 1655142215Sglebius } 1656142215Sglebius 1657142215Sglebius CARP_LOCK_INIT(cif); 1658142215Sglebius CARP_LOCK(cif); 1659142215Sglebius cif->vhif_ifp = ifp; 1660142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1661142215Sglebius ifp->if_carp = cif; 1662142215Sglebius 1663142215Sglebius } else { 1664142215Sglebius struct carp_softc *vr; 1665142215Sglebius 1666142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1667142215Sglebius CARP_LOCK(cif); 1668142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1669142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1670142215Sglebius CARP_UNLOCK(cif); 1671142215Sglebius error = EINVAL; 1672142215Sglebius goto cleanup; 1673142215Sglebius } 1674142215Sglebius } 1675142215Sglebius sc->sc_ia6 = ia; 1676142564Sglebius sc->sc_carpdev = ifp; 1677142215Sglebius 1678142215Sglebius { /* XXX prevent endless loop if already in queue */ 1679142215Sglebius struct carp_softc *vr, *after = NULL; 1680142215Sglebius int myself = 0; 1681142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1682142215Sglebius CARP_LOCK_ASSERT(cif); 1683142215Sglebius 1684142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1685142215Sglebius if (vr == sc) 1686142215Sglebius myself = 1; 1687142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1688142215Sglebius after = vr; 1689142215Sglebius } 1690142215Sglebius 1691142215Sglebius if (!myself) { 1692142215Sglebius /* We're trying to keep things in order */ 1693142215Sglebius if (after == NULL) { 1694142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1695142215Sglebius } else { 1696142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1697142215Sglebius } 1698142215Sglebius cif->vhif_nvrs++; 1699142215Sglebius } 1700142215Sglebius } 1701142215Sglebius 1702142215Sglebius sc->sc_naddrs6++; 1703147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1704142215Sglebius if (own) 1705142215Sglebius sc->sc_advskew = 0; 1706144329Sglebius carp_sc_state_locked(sc); 1707142215Sglebius carp_setrun(sc, 0); 1708142215Sglebius 1709142914Sglebius CARP_UNLOCK(cif); 1710142914Sglebius 1711142215Sglebius return (0); 1712142215Sglebius 1713142215Sglebiuscleanup: 1714142215Sglebius /* clean up multicast memberships */ 1715142215Sglebius if (!sc->sc_naddrs6) { 1716142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1717142215Sglebius imm = LIST_FIRST(&im6o->im6o_memberships); 1718142215Sglebius LIST_REMOVE(imm, i6mm_chain); 1719142215Sglebius in6_leavegroup(imm); 1720142215Sglebius } 1721142215Sglebius } 1722142215Sglebius return (error); 1723142215Sglebius} 1724142215Sglebius 1725142559Sglebiusstatic int 1726142215Sglebiuscarp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1727142215Sglebius{ 1728142215Sglebius int error = 0; 1729142215Sglebius 1730142215Sglebius if (!--sc->sc_naddrs6) { 1731142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1732142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1733142215Sglebius 1734142914Sglebius CARP_LOCK(cif); 1735142215Sglebius callout_stop(&sc->sc_ad_tmo); 1736148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1737148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1738142215Sglebius sc->sc_vhid = -1; 1739142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1740142215Sglebius struct in6_multi_mship *imm = 1741142215Sglebius LIST_FIRST(&im6o->im6o_memberships); 1742142215Sglebius 1743142215Sglebius LIST_REMOVE(imm, i6mm_chain); 1744142215Sglebius in6_leavegroup(imm); 1745142215Sglebius } 1746142215Sglebius im6o->im6o_multicast_ifp = NULL; 1747142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1748142215Sglebius if (!--cif->vhif_nvrs) { 1749142215Sglebius CARP_LOCK_DESTROY(cif); 1750142564Sglebius sc->sc_carpdev->if_carp = NULL; 1751142215Sglebius FREE(cif, M_IFADDR); 1752142215Sglebius } else 1753142215Sglebius CARP_UNLOCK(cif); 1754142215Sglebius } 1755142215Sglebius 1756142215Sglebius return (error); 1757142215Sglebius} 1758142215Sglebius#endif /* INET6 */ 1759142215Sglebius 1760142559Sglebiusstatic int 1761142215Sglebiuscarp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) 1762142215Sglebius{ 1763142215Sglebius struct carp_softc *sc = ifp->if_softc, *vr; 1764142215Sglebius struct carpreq carpr; 1765142215Sglebius struct ifaddr *ifa; 1766142215Sglebius struct ifreq *ifr; 1767142215Sglebius struct ifaliasreq *ifra; 1768142914Sglebius int locked = 0, error = 0; 1769142215Sglebius 1770142215Sglebius ifa = (struct ifaddr *)addr; 1771142215Sglebius ifra = (struct ifaliasreq *)addr; 1772142215Sglebius ifr = (struct ifreq *)addr; 1773142215Sglebius 1774142215Sglebius switch (cmd) { 1775142215Sglebius case SIOCSIFADDR: 1776142215Sglebius switch (ifa->ifa_addr->sa_family) { 1777142215Sglebius#ifdef INET 1778142215Sglebius case AF_INET: 1779147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1780142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1781142215Sglebius sizeof(struct sockaddr)); 1782142215Sglebius error = carp_set_addr(sc, satosin(ifa->ifa_addr)); 1783142215Sglebius break; 1784142215Sglebius#endif /* INET */ 1785142215Sglebius#ifdef INET6 1786142215Sglebius case AF_INET6: 1787147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1788142215Sglebius error = carp_set_addr6(sc, satosin6(ifa->ifa_addr)); 1789142215Sglebius break; 1790142215Sglebius#endif /* INET6 */ 1791142215Sglebius default: 1792142215Sglebius error = EAFNOSUPPORT; 1793142215Sglebius break; 1794142215Sglebius } 1795142215Sglebius break; 1796142215Sglebius 1797142215Sglebius case SIOCAIFADDR: 1798142215Sglebius switch (ifa->ifa_addr->sa_family) { 1799142215Sglebius#ifdef INET 1800142215Sglebius case AF_INET: 1801147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1802142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1803142215Sglebius sizeof(struct sockaddr)); 1804142215Sglebius error = carp_set_addr(sc, satosin(&ifra->ifra_addr)); 1805142215Sglebius break; 1806142215Sglebius#endif /* INET */ 1807142215Sglebius#ifdef INET6 1808142215Sglebius case AF_INET6: 1809147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1810142215Sglebius error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr)); 1811142215Sglebius break; 1812142215Sglebius#endif /* INET6 */ 1813142215Sglebius default: 1814142215Sglebius error = EAFNOSUPPORT; 1815142215Sglebius break; 1816142215Sglebius } 1817142215Sglebius break; 1818142215Sglebius 1819142215Sglebius case SIOCDIFADDR: 1820142215Sglebius switch (ifa->ifa_addr->sa_family) { 1821142215Sglebius#ifdef INET 1822142215Sglebius case AF_INET: 1823142215Sglebius error = carp_del_addr(sc, satosin(&ifra->ifra_addr)); 1824142215Sglebius break; 1825142215Sglebius#endif /* INET */ 1826142215Sglebius#ifdef INET6 1827142215Sglebius case AF_INET6: 1828142215Sglebius error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr)); 1829142215Sglebius break; 1830142215Sglebius#endif /* INET6 */ 1831142215Sglebius default: 1832142215Sglebius error = EAFNOSUPPORT; 1833142215Sglebius break; 1834142215Sglebius } 1835142215Sglebius break; 1836142215Sglebius 1837142215Sglebius case SIOCSIFFLAGS: 1838142914Sglebius if (sc->sc_carpdev) { 1839142914Sglebius locked = 1; 1840142914Sglebius CARP_SCLOCK(sc); 1841142914Sglebius } 1842142215Sglebius if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { 1843142215Sglebius callout_stop(&sc->sc_ad_tmo); 1844142215Sglebius callout_stop(&sc->sc_md_tmo); 1845142215Sglebius callout_stop(&sc->sc_md6_tmo); 1846142215Sglebius if (sc->sc_state == MASTER) 1847142914Sglebius carp_send_ad_locked(sc); 1848142215Sglebius carp_set_state(sc, INIT); 1849142215Sglebius carp_setrun(sc, 0); 1850142215Sglebius } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { 1851147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1852142215Sglebius carp_setrun(sc, 0); 1853142215Sglebius } 1854142215Sglebius break; 1855142215Sglebius 1856142215Sglebius case SIOCSVH: 1857164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 1858164033Srwatson if (error) 1859142215Sglebius break; 1860142215Sglebius if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) 1861142215Sglebius break; 1862142215Sglebius error = 1; 1863142914Sglebius if (sc->sc_carpdev) { 1864142914Sglebius locked = 1; 1865142914Sglebius CARP_SCLOCK(sc); 1866142914Sglebius } 1867142215Sglebius if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { 1868142215Sglebius switch (carpr.carpr_state) { 1869142215Sglebius case BACKUP: 1870142215Sglebius callout_stop(&sc->sc_ad_tmo); 1871142215Sglebius carp_set_state(sc, BACKUP); 1872142215Sglebius carp_setrun(sc, 0); 1873142215Sglebius carp_setroute(sc, RTM_DELETE); 1874142215Sglebius break; 1875142215Sglebius case MASTER: 1876142914Sglebius carp_master_down_locked(sc); 1877142215Sglebius break; 1878142215Sglebius default: 1879142215Sglebius break; 1880142215Sglebius } 1881142215Sglebius } 1882142215Sglebius if (carpr.carpr_vhid > 0) { 1883142215Sglebius if (carpr.carpr_vhid > 255) { 1884142215Sglebius error = EINVAL; 1885142215Sglebius break; 1886142215Sglebius } 1887142564Sglebius if (sc->sc_carpdev) { 1888142215Sglebius struct carp_if *cif; 1889142564Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1890142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1891142215Sglebius if (vr != sc && 1892143806Sglebius vr->sc_vhid == carpr.carpr_vhid) 1893143806Sglebius return EEXIST; 1894142215Sglebius } 1895142215Sglebius sc->sc_vhid = carpr.carpr_vhid; 1896152315Sru IF_LLADDR(sc->sc_ifp)[0] = 0; 1897152315Sru IF_LLADDR(sc->sc_ifp)[1] = 0; 1898152315Sru IF_LLADDR(sc->sc_ifp)[2] = 0x5e; 1899152315Sru IF_LLADDR(sc->sc_ifp)[3] = 0; 1900152315Sru IF_LLADDR(sc->sc_ifp)[4] = 1; 1901152315Sru IF_LLADDR(sc->sc_ifp)[5] = sc->sc_vhid; 1902142215Sglebius error--; 1903142215Sglebius } 1904142215Sglebius if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { 1905142215Sglebius if (carpr.carpr_advskew >= 255) { 1906142215Sglebius error = EINVAL; 1907142215Sglebius break; 1908142215Sglebius } 1909142215Sglebius if (carpr.carpr_advbase > 255) { 1910142215Sglebius error = EINVAL; 1911142215Sglebius break; 1912142215Sglebius } 1913142215Sglebius sc->sc_advbase = carpr.carpr_advbase; 1914142215Sglebius sc->sc_advskew = carpr.carpr_advskew; 1915142215Sglebius error--; 1916142215Sglebius } 1917142215Sglebius bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); 1918142215Sglebius if (error > 0) 1919142215Sglebius error = EINVAL; 1920142215Sglebius else { 1921142215Sglebius error = 0; 1922142215Sglebius carp_setrun(sc, 0); 1923142215Sglebius } 1924142215Sglebius break; 1925142215Sglebius 1926142215Sglebius case SIOCGVH: 1927142914Sglebius /* XXX: lockless read */ 1928142215Sglebius bzero(&carpr, sizeof(carpr)); 1929142215Sglebius carpr.carpr_state = sc->sc_state; 1930142215Sglebius carpr.carpr_vhid = sc->sc_vhid; 1931142215Sglebius carpr.carpr_advbase = sc->sc_advbase; 1932142215Sglebius carpr.carpr_advskew = sc->sc_advskew; 1933164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 1934164033Srwatson if (error == 0) 1935142215Sglebius bcopy(sc->sc_key, carpr.carpr_key, 1936142215Sglebius sizeof(carpr.carpr_key)); 1937142215Sglebius error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); 1938142215Sglebius break; 1939142215Sglebius 1940142215Sglebius default: 1941142215Sglebius error = EINVAL; 1942142215Sglebius } 1943142215Sglebius 1944142914Sglebius if (locked) 1945142914Sglebius CARP_SCUNLOCK(sc); 1946142914Sglebius 1947142215Sglebius carp_hmac_prepare(sc); 1948142914Sglebius 1949142215Sglebius return (error); 1950142215Sglebius} 1951142215Sglebius 1952142215Sglebius/* 1953142215Sglebius * XXX: this is looutput. We should eventually use it from there. 1954142215Sglebius */ 1955142215Sglebiusstatic int 1956142215Sglebiuscarp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 1957142215Sglebius struct rtentry *rt) 1958142215Sglebius{ 1959147611Sdwmalone u_int32_t af; 1960147611Sdwmalone 1961142215Sglebius M_ASSERTPKTHDR(m); /* check if we have the packet header */ 1962142215Sglebius 1963142215Sglebius if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 1964142215Sglebius m_freem(m); 1965142215Sglebius return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 1966142215Sglebius rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 1967142215Sglebius } 1968142215Sglebius 1969142215Sglebius ifp->if_opackets++; 1970142215Sglebius ifp->if_obytes += m->m_pkthdr.len; 1971147611Sdwmalone 1972147611Sdwmalone /* BPF writes need to be handled specially. */ 1973147611Sdwmalone if (dst->sa_family == AF_UNSPEC) { 1974147611Sdwmalone bcopy(dst->sa_data, &af, sizeof(af)); 1975147611Sdwmalone dst->sa_family = af; 1976147611Sdwmalone } 1977147611Sdwmalone 1978142215Sglebius#if 1 /* XXX */ 1979142215Sglebius switch (dst->sa_family) { 1980142215Sglebius case AF_INET: 1981142215Sglebius case AF_INET6: 1982142215Sglebius case AF_IPX: 1983142215Sglebius case AF_APPLETALK: 1984142215Sglebius break; 1985142215Sglebius default: 1986142215Sglebius printf("carp_looutput: af=%d unexpected\n", dst->sa_family); 1987142215Sglebius m_freem(m); 1988142215Sglebius return (EAFNOSUPPORT); 1989142215Sglebius } 1990142215Sglebius#endif 1991142215Sglebius return(if_simloop(ifp, m, dst->sa_family, 0)); 1992142215Sglebius} 1993142215Sglebius 1994142215Sglebius/* 1995142215Sglebius * Start output on carp interface. This function should never be called. 1996142215Sglebius */ 1997142559Sglebiusstatic void 1998142215Sglebiuscarp_start(struct ifnet *ifp) 1999142215Sglebius{ 2000142215Sglebius#ifdef DEBUG 2001142215Sglebius printf("%s: start called\n", ifp->if_xname); 2002142215Sglebius#endif 2003142215Sglebius} 2004142215Sglebius 2005142215Sglebiusint 2006142215Sglebiuscarp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, 2007142215Sglebius struct rtentry *rt) 2008142215Sglebius{ 2009142215Sglebius struct m_tag *mtag; 2010142215Sglebius struct carp_softc *sc; 2011142215Sglebius struct ifnet *carp_ifp; 2012142215Sglebius 2013142215Sglebius if (!sa) 2014142215Sglebius return (0); 2015142215Sglebius 2016142215Sglebius switch (sa->sa_family) { 2017142215Sglebius#ifdef INET 2018142215Sglebius case AF_INET: 2019142215Sglebius break; 2020142215Sglebius#endif /* INET */ 2021142215Sglebius#ifdef INET6 2022142215Sglebius case AF_INET6: 2023142215Sglebius break; 2024142215Sglebius#endif /* INET6 */ 2025142215Sglebius default: 2026142215Sglebius return (0); 2027142215Sglebius } 2028142215Sglebius 2029142215Sglebius mtag = m_tag_find(m, PACKET_TAG_CARP, NULL); 2030142215Sglebius if (mtag == NULL) 2031142215Sglebius return (0); 2032142215Sglebius 2033142215Sglebius bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *)); 2034142215Sglebius sc = carp_ifp->if_softc; 2035142215Sglebius 2036142215Sglebius /* Set the source MAC address to Virtual Router MAC Address */ 2037142215Sglebius switch (ifp->if_type) { 2038142798Syar case IFT_ETHER: 2039142798Syar case IFT_L2VLAN: { 2040142215Sglebius struct ether_header *eh; 2041142215Sglebius 2042142215Sglebius eh = mtod(m, struct ether_header *); 2043142215Sglebius eh->ether_shost[0] = 0; 2044142215Sglebius eh->ether_shost[1] = 0; 2045142215Sglebius eh->ether_shost[2] = 0x5e; 2046142215Sglebius eh->ether_shost[3] = 0; 2047142215Sglebius eh->ether_shost[4] = 1; 2048142215Sglebius eh->ether_shost[5] = sc->sc_vhid; 2049142215Sglebius } 2050142215Sglebius break; 2051142215Sglebius case IFT_FDDI: { 2052142215Sglebius struct fddi_header *fh; 2053142215Sglebius 2054142215Sglebius fh = mtod(m, struct fddi_header *); 2055142215Sglebius fh->fddi_shost[0] = 0; 2056142215Sglebius fh->fddi_shost[1] = 0; 2057142215Sglebius fh->fddi_shost[2] = 0x5e; 2058142215Sglebius fh->fddi_shost[3] = 0; 2059142215Sglebius fh->fddi_shost[4] = 1; 2060142215Sglebius fh->fddi_shost[5] = sc->sc_vhid; 2061142215Sglebius } 2062142215Sglebius break; 2063142215Sglebius case IFT_ISO88025: { 2064142215Sglebius struct iso88025_header *th; 2065142215Sglebius th = mtod(m, struct iso88025_header *); 2066142215Sglebius th->iso88025_shost[0] = 3; 2067142215Sglebius th->iso88025_shost[1] = 0; 2068142215Sglebius th->iso88025_shost[2] = 0x40 >> (sc->sc_vhid - 1); 2069142215Sglebius th->iso88025_shost[3] = 0x40000 >> (sc->sc_vhid - 1); 2070142215Sglebius th->iso88025_shost[4] = 0; 2071142215Sglebius th->iso88025_shost[5] = 0; 2072142215Sglebius } 2073142215Sglebius break; 2074142215Sglebius default: 2075142215Sglebius printf("%s: carp is not supported for this interface type\n", 2076142215Sglebius ifp->if_xname); 2077142215Sglebius return (EOPNOTSUPP); 2078142215Sglebius } 2079142215Sglebius 2080142215Sglebius return (0); 2081142215Sglebius} 2082142215Sglebius 2083142559Sglebiusstatic void 2084142215Sglebiuscarp_set_state(struct carp_softc *sc, int state) 2085142215Sglebius{ 2086142914Sglebius 2087142914Sglebius if (sc->sc_carpdev) 2088142914Sglebius CARP_SCLOCK_ASSERT(sc); 2089142914Sglebius 2090142215Sglebius if (sc->sc_state == state) 2091142215Sglebius return; 2092142215Sglebius 2093142215Sglebius sc->sc_state = state; 2094142215Sglebius switch (state) { 2095142215Sglebius case BACKUP: 2096147256Sbrooks SC2IFP(sc)->if_link_state = LINK_STATE_DOWN; 2097142215Sglebius break; 2098142215Sglebius case MASTER: 2099147256Sbrooks SC2IFP(sc)->if_link_state = LINK_STATE_UP; 2100142215Sglebius break; 2101142215Sglebius default: 2102147256Sbrooks SC2IFP(sc)->if_link_state = LINK_STATE_UNKNOWN; 2103142215Sglebius break; 2104142215Sglebius } 2105147256Sbrooks rt_ifmsg(SC2IFP(sc)); 2106142215Sglebius} 2107142215Sglebius 2108142215Sglebiusvoid 2109142215Sglebiuscarp_carpdev_state(void *v) 2110142215Sglebius{ 2111142215Sglebius struct carp_if *cif = v; 2112142914Sglebius 2113142914Sglebius CARP_LOCK(cif); 2114142914Sglebius carp_carpdev_state_locked(cif); 2115142914Sglebius CARP_UNLOCK(cif); 2116142914Sglebius} 2117142914Sglebius 2118142914Sglebiusstatic void 2119142914Sglebiuscarp_carpdev_state_locked(struct carp_if *cif) 2120142914Sglebius{ 2121142215Sglebius struct carp_softc *sc; 2122142215Sglebius 2123144329Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) 2124144329Sglebius carp_sc_state_locked(sc); 2125144329Sglebius} 2126144329Sglebius 2127144329Sglebiusstatic void 2128144329Sglebiuscarp_sc_state_locked(struct carp_softc *sc) 2129144329Sglebius{ 2130144329Sglebius CARP_SCLOCK_ASSERT(sc); 2131144329Sglebius 2132144329Sglebius if (sc->sc_carpdev->if_link_state != LINK_STATE_UP || 2133144329Sglebius !(sc->sc_carpdev->if_flags & IFF_UP)) { 2134147256Sbrooks sc->sc_flags_backup = SC2IFP(sc)->if_flags; 2135148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 2136148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 2137144329Sglebius callout_stop(&sc->sc_ad_tmo); 2138144329Sglebius callout_stop(&sc->sc_md_tmo); 2139144329Sglebius callout_stop(&sc->sc_md6_tmo); 2140144329Sglebius carp_set_state(sc, INIT); 2141144329Sglebius carp_setrun(sc, 0); 2142144329Sglebius if (!sc->sc_suppress) { 2143144329Sglebius carp_suppress_preempt++; 2144144329Sglebius if (carp_suppress_preempt == 1) { 2145144329Sglebius CARP_SCUNLOCK(sc); 2146144329Sglebius carp_send_ad_all(); 2147144329Sglebius CARP_SCLOCK(sc); 2148142215Sglebius } 2149142215Sglebius } 2150144329Sglebius sc->sc_suppress = 1; 2151144329Sglebius } else { 2152147256Sbrooks SC2IFP(sc)->if_flags |= sc->sc_flags_backup; 2153144329Sglebius carp_set_state(sc, INIT); 2154144329Sglebius carp_setrun(sc, 0); 2155144329Sglebius if (sc->sc_suppress) 2156144329Sglebius carp_suppress_preempt--; 2157144329Sglebius sc->sc_suppress = 0; 2158142215Sglebius } 2159144329Sglebius 2160144329Sglebius return; 2161142215Sglebius} 2162142215Sglebius 2163142215Sglebiusstatic int 2164142215Sglebiuscarp_modevent(module_t mod, int type, void *data) 2165142215Sglebius{ 2166142215Sglebius switch (type) { 2167142215Sglebius case MOD_LOAD: 2168156947Sglebius if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 2169156947Sglebius carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 2170156947Sglebius if (if_detach_event_tag == NULL) 2171156947Sglebius return (ENOMEM); 2172142911Sglebius mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); 2173142215Sglebius LIST_INIT(&carpif_list); 2174142215Sglebius if_clone_attach(&carp_cloner); 2175142215Sglebius break; 2176142215Sglebius 2177142215Sglebius case MOD_UNLOAD: 2178156947Sglebius EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag); 2179142215Sglebius if_clone_detach(&carp_cloner); 2180142911Sglebius mtx_destroy(&carp_mtx); 2181142215Sglebius break; 2182142215Sglebius 2183142215Sglebius default: 2184156947Sglebius return (EINVAL); 2185142215Sglebius } 2186142215Sglebius 2187156947Sglebius return (0); 2188142215Sglebius} 2189142215Sglebius 2190142215Sglebiusstatic moduledata_t carp_mod = { 2191142215Sglebius "carp", 2192142215Sglebius carp_modevent, 2193142215Sglebius 0 2194142215Sglebius}; 2195142215Sglebius 2196142215SglebiusDECLARE_MODULE(carp, carp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2197