ip_carp.c revision 212265
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 212265 2010-09-06 21:03:30Z will $"); 29172467Ssilby 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> 46211157Swill#include <sys/protosw.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> 67196019Srwatson#include <net/vnet.h> 68142215Sglebius 69142215Sglebius#ifdef INET 70142215Sglebius#include <netinet/in.h> 71142215Sglebius#include <netinet/in_var.h> 72142215Sglebius#include <netinet/in_systm.h> 73142215Sglebius#include <netinet/ip.h> 74142215Sglebius#include <netinet/ip_var.h> 75142215Sglebius#include <netinet/if_ether.h> 76142215Sglebius#include <machine/in_cksum.h> 77142215Sglebius#endif 78142215Sglebius 79142215Sglebius#ifdef INET6 80142215Sglebius#include <netinet/icmp6.h> 81142215Sglebius#include <netinet/ip6.h> 82211157Swill#include <netinet6/ip6protosw.h> 83142215Sglebius#include <netinet6/ip6_var.h> 84148387Sume#include <netinet6/scope6_var.h> 85142215Sglebius#include <netinet6/nd6.h> 86142215Sglebius#endif 87142215Sglebius 88142215Sglebius#include <crypto/sha1.h> 89142215Sglebius#include <netinet/ip_carp.h> 90142215Sglebius 91142215Sglebius#define CARP_IFNAME "carp" 92142215Sglebiusstatic MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces"); 93142215SglebiusSYSCTL_DECL(_net_inet_carp); 94142215Sglebius 95142215Sglebiusstruct carp_softc { 96147256Sbrooks struct ifnet *sc_ifp; /* Interface clue */ 97142901Sglebius struct ifnet *sc_carpdev; /* Pointer to parent interface */ 98142215Sglebius struct in_ifaddr *sc_ia; /* primary iface address */ 99142215Sglebius struct ip_moptions sc_imo; 100142215Sglebius#ifdef INET6 101142215Sglebius struct in6_ifaddr *sc_ia6; /* primary iface address v6 */ 102142215Sglebius struct ip6_moptions sc_im6o; 103142215Sglebius#endif /* INET6 */ 104142215Sglebius TAILQ_ENTRY(carp_softc) sc_list; 105142215Sglebius 106142215Sglebius enum { INIT = 0, BACKUP, MASTER } sc_state; 107142215Sglebius 108142215Sglebius int sc_flags_backup; 109142215Sglebius int sc_suppress; 110142215Sglebius 111142215Sglebius int sc_sendad_errors; 112142215Sglebius#define CARP_SENDAD_MAX_ERRORS 3 113142215Sglebius int sc_sendad_success; 114142215Sglebius#define CARP_SENDAD_MIN_SUCCESS 3 115142215Sglebius 116142215Sglebius int sc_vhid; 117142215Sglebius int sc_advskew; 118142215Sglebius int sc_naddrs; 119142215Sglebius int sc_naddrs6; 120142215Sglebius int sc_advbase; /* seconds */ 121142215Sglebius int sc_init_counter; 122142215Sglebius u_int64_t sc_counter; 123142215Sglebius 124142215Sglebius /* authentication */ 125142215Sglebius#define CARP_HMAC_PAD 64 126142215Sglebius unsigned char sc_key[CARP_KEY_LEN]; 127142215Sglebius unsigned char sc_pad[CARP_HMAC_PAD]; 128142215Sglebius SHA1_CTX sc_sha1; 129142215Sglebius 130142215Sglebius struct callout sc_ad_tmo; /* advertisement timeout */ 131142215Sglebius struct callout sc_md_tmo; /* master down timeout */ 132142215Sglebius struct callout sc_md6_tmo; /* master down timeout */ 133142215Sglebius 134142215Sglebius LIST_ENTRY(carp_softc) sc_next; /* Interface clue */ 135142215Sglebius}; 136147256Sbrooks#define SC2IFP(sc) ((sc)->sc_ifp) 137142215Sglebius 138142215Sglebiusint carp_suppress_preempt = 0; 139142215Sglebiusint carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */ 140211157SwillSYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP"); 141142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW, 142142215Sglebius &carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets"); 143142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW, 144142215Sglebius &carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode"); 145142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW, 146142215Sglebius &carp_opts[CARPCTL_LOG], 0, "log bad carp packets"); 147142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW, 148142215Sglebius &carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses"); 149146226SglebiusSYSCTL_INT(_net_inet_carp, OID_AUTO, suppress_preempt, CTLFLAG_RD, 150146226Sglebius &carp_suppress_preempt, 0, "Preemption is suppressed"); 151142215Sglebius 152142215Sglebiusstruct carpstats carpstats; 153142215SglebiusSYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW, 154142215Sglebius &carpstats, carpstats, 155142215Sglebius "CARP statistics (struct carpstats, netinet/ip_carp.h)"); 156142215Sglebius 157142215Sglebiusstruct carp_if { 158142215Sglebius TAILQ_HEAD(, carp_softc) vhif_vrs; 159142215Sglebius int vhif_nvrs; 160142215Sglebius 161142215Sglebius struct ifnet *vhif_ifp; 162142215Sglebius struct mtx vhif_mtx; 163142215Sglebius}; 164142914Sglebius 165211157Swill#define CARP_INET 0 166211157Swill#define CARP_INET6 1 167211157Swillstatic int proto_reg[] = {-1, -1}; 168211157Swill 169142914Sglebius/* Get carp_if from softc. Valid after carp_set_addr{,6}. */ 170142914Sglebius#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp) 171142914Sglebius 172142215Sglebius/* lock per carp_if queue */ 173142914Sglebius#define CARP_LOCK_INIT(cif) mtx_init(&(cif)->vhif_mtx, "carp_if", \ 174142215Sglebius NULL, MTX_DEF) 175142914Sglebius#define CARP_LOCK_DESTROY(cif) mtx_destroy(&(cif)->vhif_mtx) 176142215Sglebius#define CARP_LOCK_ASSERT(cif) mtx_assert(&(cif)->vhif_mtx, MA_OWNED) 177142215Sglebius#define CARP_LOCK(cif) mtx_lock(&(cif)->vhif_mtx) 178142215Sglebius#define CARP_UNLOCK(cif) mtx_unlock(&(cif)->vhif_mtx) 179142215Sglebius 180142914Sglebius#define CARP_SCLOCK(sc) mtx_lock(&SC2CIF(sc)->vhif_mtx) 181142914Sglebius#define CARP_SCUNLOCK(sc) mtx_unlock(&SC2CIF(sc)->vhif_mtx) 182142914Sglebius#define CARP_SCLOCK_ASSERT(sc) mtx_assert(&SC2CIF(sc)->vhif_mtx, MA_OWNED) 183142914Sglebius 184142451Sglebius#define CARP_LOG(...) do { \ 185142446Sglebius if (carp_opts[CARPCTL_LOG] > 0) \ 186142446Sglebius log(LOG_INFO, __VA_ARGS__); \ 187142451Sglebius} while (0) 188142215Sglebius 189142451Sglebius#define CARP_DEBUG(...) do { \ 190142446Sglebius if (carp_opts[CARPCTL_LOG] > 1) \ 191142446Sglebius log(LOG_DEBUG, __VA_ARGS__); \ 192142451Sglebius} while (0) 193142446Sglebius 194142559Sglebiusstatic void carp_hmac_prepare(struct carp_softc *); 195142559Sglebiusstatic void carp_hmac_generate(struct carp_softc *, u_int32_t *, 196142559Sglebius unsigned char *); 197142559Sglebiusstatic int carp_hmac_verify(struct carp_softc *, u_int32_t *, 198142559Sglebius unsigned char *); 199142559Sglebiusstatic void carp_setroute(struct carp_softc *, int); 200142559Sglebiusstatic void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); 201160195Ssamstatic int carp_clone_create(struct if_clone *, int, caddr_t); 202142559Sglebiusstatic void carp_clone_destroy(struct ifnet *); 203166228Sglebiusstatic void carpdetach(struct carp_softc *, int); 204142559Sglebiusstatic int carp_prepare_ad(struct mbuf *, struct carp_softc *, 205142559Sglebius struct carp_header *); 206142559Sglebiusstatic void carp_send_ad_all(void); 207142559Sglebiusstatic void carp_send_ad(void *); 208142914Sglebiusstatic void carp_send_ad_locked(struct carp_softc *); 209142559Sglebiusstatic void carp_send_arp(struct carp_softc *); 210142559Sglebiusstatic void carp_master_down(void *); 211142914Sglebiusstatic void carp_master_down_locked(struct carp_softc *); 212142559Sglebiusstatic int carp_ioctl(struct ifnet *, u_long, caddr_t); 213142559Sglebiusstatic int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *, 214191148Skmacy struct route *); 215142559Sglebiusstatic void carp_start(struct ifnet *); 216142559Sglebiusstatic void carp_setrun(struct carp_softc *, sa_family_t); 217142559Sglebiusstatic void carp_set_state(struct carp_softc *, int); 218142559Sglebiusstatic int carp_addrcount(struct carp_if *, struct in_ifaddr *, int); 219142215Sglebiusenum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING }; 220142215Sglebius 221156947Sglebiusstatic void carp_multicast_cleanup(struct carp_softc *); 222142559Sglebiusstatic int carp_set_addr(struct carp_softc *, struct sockaddr_in *); 223142559Sglebiusstatic int carp_del_addr(struct carp_softc *, struct sockaddr_in *); 224142914Sglebiusstatic void carp_carpdev_state_locked(struct carp_if *); 225144329Sglebiusstatic void carp_sc_state_locked(struct carp_softc *); 226142215Sglebius#ifdef INET6 227142559Sglebiusstatic void carp_send_na(struct carp_softc *); 228142559Sglebiusstatic int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); 229142559Sglebiusstatic int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *); 230166423Sglebiusstatic void carp_multicast6_cleanup(struct carp_softc *); 231142215Sglebius#endif 232142215Sglebius 233142215Sglebiusstatic LIST_HEAD(, carp_softc) carpif_list; 234142911Sglebiusstatic struct mtx carp_mtx; 235142215SglebiusIFC_SIMPLE_DECLARE(carp, 0); 236142215Sglebius 237156947Sglebiusstatic eventhandler_tag if_detach_event_tag; 238156947Sglebius 239142215Sglebiusstatic __inline u_int16_t 240142215Sglebiuscarp_cksum(struct mbuf *m, int len) 241142215Sglebius{ 242142215Sglebius return (in_cksum(m, len)); 243142215Sglebius} 244142215Sglebius 245142559Sglebiusstatic void 246142215Sglebiuscarp_hmac_prepare(struct carp_softc *sc) 247142215Sglebius{ 248142215Sglebius u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; 249142215Sglebius u_int8_t vhid = sc->sc_vhid & 0xff; 250142215Sglebius struct ifaddr *ifa; 251179490Smlaier int i, found; 252179490Smlaier#ifdef INET 253179490Smlaier struct in_addr last, cur, in; 254179490Smlaier#endif 255142215Sglebius#ifdef INET6 256179490Smlaier struct in6_addr last6, cur6, in6; 257142215Sglebius#endif 258142215Sglebius 259142914Sglebius if (sc->sc_carpdev) 260142914Sglebius CARP_SCLOCK(sc); 261142914Sglebius 262142914Sglebius /* XXX: possible race here */ 263142914Sglebius 264142215Sglebius /* compute ipad from key */ 265142215Sglebius bzero(sc->sc_pad, sizeof(sc->sc_pad)); 266142215Sglebius bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); 267142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 268142215Sglebius sc->sc_pad[i] ^= 0x36; 269142215Sglebius 270142215Sglebius /* precompute first part of inner hash */ 271142215Sglebius SHA1Init(&sc->sc_sha1); 272142215Sglebius SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); 273142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version)); 274142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); 275142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); 276142215Sglebius#ifdef INET 277179490Smlaier cur.s_addr = 0; 278179490Smlaier do { 279179490Smlaier found = 0; 280179490Smlaier last = cur; 281179490Smlaier cur.s_addr = 0xffffffff; 282191528Srwatson IF_ADDR_LOCK(SC2IFP(sc)); 283179490Smlaier TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 284179490Smlaier in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr; 285179490Smlaier if (ifa->ifa_addr->sa_family == AF_INET && 286179490Smlaier ntohl(in.s_addr) > ntohl(last.s_addr) && 287179490Smlaier ntohl(in.s_addr) < ntohl(cur.s_addr)) { 288179490Smlaier cur.s_addr = in.s_addr; 289179490Smlaier found++; 290179490Smlaier } 291179490Smlaier } 292191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 293179490Smlaier if (found) 294179490Smlaier SHA1Update(&sc->sc_sha1, (void *)&cur, sizeof(cur)); 295179490Smlaier } while (found); 296142215Sglebius#endif /* INET */ 297142215Sglebius#ifdef INET6 298179490Smlaier memset(&cur6, 0, sizeof(cur6)); 299179490Smlaier do { 300179490Smlaier found = 0; 301179490Smlaier last6 = cur6; 302179490Smlaier memset(&cur6, 0xff, sizeof(cur6)); 303191528Srwatson IF_ADDR_LOCK(SC2IFP(sc)); 304179490Smlaier TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 305142215Sglebius in6 = ifatoia6(ifa)->ia_addr.sin6_addr; 306179490Smlaier if (IN6_IS_SCOPE_EMBED(&in6)) 307179490Smlaier in6.s6_addr16[1] = 0; 308179490Smlaier if (ifa->ifa_addr->sa_family == AF_INET6 && 309179490Smlaier memcmp(&in6, &last6, sizeof(in6)) > 0 && 310179490Smlaier memcmp(&in6, &cur6, sizeof(in6)) < 0) { 311179490Smlaier cur6 = in6; 312179490Smlaier found++; 313179490Smlaier } 314142215Sglebius } 315191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 316179490Smlaier if (found) 317179490Smlaier SHA1Update(&sc->sc_sha1, (void *)&cur6, sizeof(cur6)); 318179490Smlaier } while (found); 319142215Sglebius#endif /* INET6 */ 320142215Sglebius 321142215Sglebius /* convert ipad to opad */ 322142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 323142215Sglebius sc->sc_pad[i] ^= 0x36 ^ 0x5c; 324142914Sglebius 325142914Sglebius if (sc->sc_carpdev) 326142914Sglebius CARP_SCUNLOCK(sc); 327142215Sglebius} 328142215Sglebius 329142559Sglebiusstatic void 330142215Sglebiuscarp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], 331142215Sglebius unsigned char md[20]) 332142215Sglebius{ 333142215Sglebius SHA1_CTX sha1ctx; 334142215Sglebius 335142215Sglebius /* fetch first half of inner hash */ 336142215Sglebius bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); 337142215Sglebius 338142215Sglebius SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter)); 339142215Sglebius SHA1Final(md, &sha1ctx); 340142215Sglebius 341142215Sglebius /* outer hash */ 342142215Sglebius SHA1Init(&sha1ctx); 343142215Sglebius SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); 344142215Sglebius SHA1Update(&sha1ctx, md, 20); 345142215Sglebius SHA1Final(md, &sha1ctx); 346142215Sglebius} 347142215Sglebius 348142559Sglebiusstatic int 349142215Sglebiuscarp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], 350142215Sglebius unsigned char md[20]) 351142215Sglebius{ 352142215Sglebius unsigned char md2[20]; 353142215Sglebius 354142914Sglebius CARP_SCLOCK_ASSERT(sc); 355142914Sglebius 356142215Sglebius carp_hmac_generate(sc, counter, md2); 357142215Sglebius 358142215Sglebius return (bcmp(md, md2, sizeof(md2))); 359142215Sglebius} 360142215Sglebius 361142559Sglebiusstatic void 362142215Sglebiuscarp_setroute(struct carp_softc *sc, int cmd) 363142215Sglebius{ 364142215Sglebius struct ifaddr *ifa; 365142215Sglebius int s; 366142215Sglebius 367142914Sglebius if (sc->sc_carpdev) 368142914Sglebius CARP_SCLOCK_ASSERT(sc); 369142914Sglebius 370142215Sglebius s = splnet(); 371147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 372142914Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 373142914Sglebius sc->sc_carpdev != NULL) { 374142215Sglebius int count = carp_addrcount( 375142564Sglebius (struct carp_if *)sc->sc_carpdev->if_carp, 376142215Sglebius ifatoia(ifa), CARP_COUNT_MASTER); 377142215Sglebius 378142215Sglebius if ((cmd == RTM_ADD && count == 1) || 379142215Sglebius (cmd == RTM_DELETE && count == 0)) 380142215Sglebius rtinit(ifa, cmd, RTF_UP | RTF_HOST); 381142215Sglebius } 382142215Sglebius } 383142215Sglebius splx(s); 384142215Sglebius} 385142215Sglebius 386142559Sglebiusstatic int 387160195Ssamcarp_clone_create(struct if_clone *ifc, int unit, caddr_t params) 388142215Sglebius{ 389142215Sglebius 390142215Sglebius struct carp_softc *sc; 391142215Sglebius struct ifnet *ifp; 392142215Sglebius 393184205Sdes sc = malloc(sizeof(*sc), M_CARP, M_WAITOK|M_ZERO); 394147256Sbrooks ifp = SC2IFP(sc) = if_alloc(IFT_ETHER); 395147256Sbrooks if (ifp == NULL) { 396184205Sdes free(sc, M_CARP); 397147256Sbrooks return (ENOSPC); 398147256Sbrooks } 399142215Sglebius 400142215Sglebius sc->sc_flags_backup = 0; 401142215Sglebius sc->sc_suppress = 0; 402142215Sglebius sc->sc_advbase = CARP_DFLTINTV; 403142215Sglebius sc->sc_vhid = -1; /* required setting */ 404142215Sglebius sc->sc_advskew = 0; 405142215Sglebius sc->sc_init_counter = 1; 406142215Sglebius sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */ 407160164Smlaier sc->sc_imo.imo_membership = (struct in_multi **)malloc( 408160164Smlaier (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP, 409160164Smlaier M_WAITOK); 410170613Sbms sc->sc_imo.imo_mfilters = NULL; 411160164Smlaier sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; 412162627Sbms sc->sc_imo.imo_multicast_vif = -1; 413191672Sbms#ifdef INET6 414191672Sbms sc->sc_im6o.im6o_membership = (struct in6_multi **)malloc( 415191672Sbms (sizeof(struct in6_multi *) * IPV6_MIN_MEMBERSHIPS), M_CARP, 416191672Sbms M_WAITOK); 417191672Sbms sc->sc_im6o.im6o_mfilters = NULL; 418191672Sbms sc->sc_im6o.im6o_max_memberships = IPV6_MIN_MEMBERSHIPS; 419191672Sbms sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; 420191672Sbms#endif 421142215Sglebius 422171637Srwatson callout_init(&sc->sc_ad_tmo, CALLOUT_MPSAFE); 423171637Srwatson callout_init(&sc->sc_md_tmo, CALLOUT_MPSAFE); 424171637Srwatson callout_init(&sc->sc_md6_tmo, CALLOUT_MPSAFE); 425142215Sglebius 426142215Sglebius ifp->if_softc = sc; 427142215Sglebius if_initname(ifp, CARP_IFNAME, unit); 428142215Sglebius ifp->if_mtu = ETHERMTU; 429151688Syar ifp->if_flags = IFF_LOOPBACK; 430142215Sglebius ifp->if_ioctl = carp_ioctl; 431142215Sglebius ifp->if_output = carp_looutput; 432142215Sglebius ifp->if_start = carp_start; 433142215Sglebius ifp->if_type = IFT_CARP; 434142215Sglebius ifp->if_snd.ifq_maxlen = ifqmaxlen; 435142215Sglebius ifp->if_hdrlen = 0; 436142215Sglebius if_attach(ifp); 437147256Sbrooks bpfattach(SC2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 438142911Sglebius mtx_lock(&carp_mtx); 439142215Sglebius LIST_INSERT_HEAD(&carpif_list, sc, sc_next); 440142911Sglebius mtx_unlock(&carp_mtx); 441142215Sglebius return (0); 442142215Sglebius} 443142215Sglebius 444142559Sglebiusstatic void 445142215Sglebiuscarp_clone_destroy(struct ifnet *ifp) 446142215Sglebius{ 447142215Sglebius struct carp_softc *sc = ifp->if_softc; 448156947Sglebius 449156947Sglebius if (sc->sc_carpdev) 450156947Sglebius CARP_SCLOCK(sc); 451166228Sglebius carpdetach(sc, 1); /* Returns unlocked. */ 452156947Sglebius 453156947Sglebius mtx_lock(&carp_mtx); 454156947Sglebius LIST_REMOVE(sc, sc_next); 455156947Sglebius mtx_unlock(&carp_mtx); 456156947Sglebius bpfdetach(ifp); 457156947Sglebius if_detach(ifp); 458156947Sglebius if_free_type(ifp, IFT_ETHER); 459160164Smlaier free(sc->sc_imo.imo_membership, M_CARP); 460191672Sbms#ifdef INET6 461191672Sbms free(sc->sc_im6o.im6o_membership, M_CARP); 462191672Sbms#endif 463156947Sglebius free(sc, M_CARP); 464156947Sglebius} 465156947Sglebius 466166423Sglebius/* 467166423Sglebius * This function can be called on CARP interface destroy path, 468166423Sglebius * and in case of the removal of the underlying interface as 469166423Sglebius * well. We differentiate these two cases. In the latter case 470166423Sglebius * we do not cleanup our multicast memberships, since they 471166423Sglebius * are already freed. Also, in the latter case we do not 472166423Sglebius * release the lock on return, because the function will be 473166423Sglebius * called once more, for another CARP instance on the same 474166423Sglebius * interface. 475166423Sglebius */ 476156947Sglebiusstatic void 477166228Sglebiuscarpdetach(struct carp_softc *sc, int unlock) 478156947Sglebius{ 479142215Sglebius struct carp_if *cif; 480146226Sglebius 481156947Sglebius callout_stop(&sc->sc_ad_tmo); 482156947Sglebius callout_stop(&sc->sc_md_tmo); 483156947Sglebius callout_stop(&sc->sc_md6_tmo); 484156947Sglebius 485146226Sglebius if (sc->sc_suppress) 486146226Sglebius carp_suppress_preempt--; 487146226Sglebius sc->sc_suppress = 0; 488146226Sglebius 489156947Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) 490156947Sglebius carp_suppress_preempt--; 491156947Sglebius sc->sc_sendad_errors = 0; 492142215Sglebius 493156947Sglebius carp_set_state(sc, INIT); 494156947Sglebius SC2IFP(sc)->if_flags &= ~IFF_UP; 495156947Sglebius carp_setrun(sc, 0); 496166423Sglebius if (unlock) 497166423Sglebius carp_multicast_cleanup(sc); 498166423Sglebius#ifdef INET6 499166423Sglebius carp_multicast6_cleanup(sc); 500166423Sglebius#endif 501142215Sglebius 502156947Sglebius if (sc->sc_carpdev != NULL) { 503156947Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 504156947Sglebius CARP_LOCK_ASSERT(cif); 505142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 506142215Sglebius if (!--cif->vhif_nvrs) { 507156947Sglebius ifpromisc(sc->sc_carpdev, 0); 508142564Sglebius sc->sc_carpdev->if_carp = NULL; 509142215Sglebius CARP_LOCK_DESTROY(cif); 510196397Swill free(cif, M_CARP); 511166228Sglebius } else if (unlock) 512166228Sglebius CARP_UNLOCK(cif); 513166228Sglebius sc->sc_carpdev = NULL; 514142215Sglebius } 515156947Sglebius} 516142215Sglebius 517156947Sglebius/* Detach an interface from the carp. */ 518156947Sglebiusstatic void 519156947Sglebiuscarp_ifdetach(void *arg __unused, struct ifnet *ifp) 520156947Sglebius{ 521156947Sglebius struct carp_if *cif = (struct carp_if *)ifp->if_carp; 522156947Sglebius struct carp_softc *sc, *nextsc; 523166226Sglebius 524156947Sglebius if (cif == NULL) 525156947Sglebius return; 526156947Sglebius 527156947Sglebius /* 528156947Sglebius * XXX: At the end of for() cycle the lock will be destroyed. 529156947Sglebius */ 530156947Sglebius CARP_LOCK(cif); 531156947Sglebius for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) { 532156947Sglebius nextsc = TAILQ_NEXT(sc, sc_list); 533166228Sglebius carpdetach(sc, 0); 534156947Sglebius } 535142215Sglebius} 536142215Sglebius 537142215Sglebius/* 538142215Sglebius * process input packet. 539142215Sglebius * we have rearranged checks order compared to the rfc, 540142215Sglebius * but it seems more efficient this way or not possible otherwise. 541142215Sglebius */ 542142215Sglebiusvoid 543142215Sglebiuscarp_input(struct mbuf *m, int hlen) 544142215Sglebius{ 545142215Sglebius struct ip *ip = mtod(m, struct ip *); 546142215Sglebius struct carp_header *ch; 547142215Sglebius int iplen, len; 548142215Sglebius 549190968Srwatson CARPSTATS_INC(carps_ipackets); 550142215Sglebius 551142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 552142215Sglebius m_freem(m); 553142215Sglebius return; 554142215Sglebius } 555142215Sglebius 556142215Sglebius /* check if received on a valid carp interface */ 557142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 558190968Srwatson CARPSTATS_INC(carps_badif); 559200026Sglebius CARP_DEBUG("carp_input: packet received on non-carp " 560142452Sglebius "interface: %s\n", 561142446Sglebius m->m_pkthdr.rcvif->if_xname); 562142215Sglebius m_freem(m); 563142215Sglebius return; 564142215Sglebius } 565142215Sglebius 566142215Sglebius /* verify that the IP TTL is 255. */ 567142215Sglebius if (ip->ip_ttl != CARP_DFLTTL) { 568190968Srwatson CARPSTATS_INC(carps_badttl); 569201801Sru CARP_DEBUG("carp_input: received ttl %d != 255 on %s\n", 570142446Sglebius ip->ip_ttl, 571142446Sglebius m->m_pkthdr.rcvif->if_xname); 572142215Sglebius m_freem(m); 573142215Sglebius return; 574142215Sglebius } 575142215Sglebius 576142215Sglebius iplen = ip->ip_hl << 2; 577142215Sglebius 578142215Sglebius if (m->m_pkthdr.len < iplen + sizeof(*ch)) { 579190968Srwatson CARPSTATS_INC(carps_badlen); 580200026Sglebius CARP_DEBUG("carp_input: received len %zd < " 581195976Sdelphij "sizeof(struct carp_header) on %s\n", 582195976Sdelphij m->m_len - sizeof(struct ip), 583195976Sdelphij m->m_pkthdr.rcvif->if_xname); 584142215Sglebius m_freem(m); 585142215Sglebius return; 586142215Sglebius } 587142215Sglebius 588142215Sglebius if (iplen + sizeof(*ch) < m->m_len) { 589142215Sglebius if ((m = m_pullup(m, iplen + sizeof(*ch))) == NULL) { 590190968Srwatson CARPSTATS_INC(carps_hdrops); 591200026Sglebius CARP_DEBUG("carp_input: pullup failed\n"); 592142215Sglebius return; 593142215Sglebius } 594142215Sglebius ip = mtod(m, struct ip *); 595142215Sglebius } 596142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 597142215Sglebius 598142215Sglebius /* 599142215Sglebius * verify that the received packet length is 600142215Sglebius * equal to the CARP header 601142215Sglebius */ 602142215Sglebius len = iplen + sizeof(*ch); 603142215Sglebius if (len > m->m_pkthdr.len) { 604190968Srwatson CARPSTATS_INC(carps_badlen); 605200026Sglebius CARP_DEBUG("carp_input: packet too short %d on %s\n", 606142446Sglebius m->m_pkthdr.len, 607142446Sglebius m->m_pkthdr.rcvif->if_xname); 608142215Sglebius m_freem(m); 609142215Sglebius return; 610142215Sglebius } 611142215Sglebius 612142215Sglebius if ((m = m_pullup(m, len)) == NULL) { 613190968Srwatson CARPSTATS_INC(carps_hdrops); 614142215Sglebius return; 615142215Sglebius } 616142215Sglebius ip = mtod(m, struct ip *); 617142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 618142215Sglebius 619142215Sglebius /* verify the CARP checksum */ 620142215Sglebius m->m_data += iplen; 621142215Sglebius if (carp_cksum(m, len - iplen)) { 622190968Srwatson CARPSTATS_INC(carps_badsum); 623200026Sglebius CARP_DEBUG("carp_input: checksum failed on %s\n", 624142446Sglebius m->m_pkthdr.rcvif->if_xname); 625142215Sglebius m_freem(m); 626142215Sglebius return; 627142215Sglebius } 628142215Sglebius m->m_data -= iplen; 629142215Sglebius 630142446Sglebius carp_input_c(m, ch, AF_INET); 631142215Sglebius} 632142215Sglebius 633142215Sglebius#ifdef INET6 634142215Sglebiusint 635142215Sglebiuscarp6_input(struct mbuf **mp, int *offp, int proto) 636142215Sglebius{ 637142215Sglebius struct mbuf *m = *mp; 638142215Sglebius struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 639142215Sglebius struct carp_header *ch; 640142215Sglebius u_int len; 641142215Sglebius 642190968Srwatson CARPSTATS_INC(carps_ipackets6); 643142215Sglebius 644142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 645142215Sglebius m_freem(m); 646142215Sglebius return (IPPROTO_DONE); 647142215Sglebius } 648142215Sglebius 649142215Sglebius /* check if received on a valid carp interface */ 650142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 651190968Srwatson CARPSTATS_INC(carps_badif); 652200026Sglebius CARP_DEBUG("carp6_input: packet received on non-carp " 653142452Sglebius "interface: %s\n", 654142446Sglebius m->m_pkthdr.rcvif->if_xname); 655142215Sglebius m_freem(m); 656142215Sglebius return (IPPROTO_DONE); 657142215Sglebius } 658142215Sglebius 659142215Sglebius /* verify that the IP TTL is 255 */ 660142215Sglebius if (ip6->ip6_hlim != CARP_DFLTTL) { 661190968Srwatson CARPSTATS_INC(carps_badttl); 662200026Sglebius CARP_DEBUG("carp6_input: received ttl %d != 255 on %s\n", 663142446Sglebius ip6->ip6_hlim, 664142446Sglebius m->m_pkthdr.rcvif->if_xname); 665142215Sglebius m_freem(m); 666142215Sglebius return (IPPROTO_DONE); 667142215Sglebius } 668142215Sglebius 669142215Sglebius /* verify that we have a complete carp packet */ 670142215Sglebius len = m->m_len; 671142215Sglebius IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch)); 672142215Sglebius if (ch == NULL) { 673190968Srwatson CARPSTATS_INC(carps_badlen); 674200026Sglebius CARP_DEBUG("carp6_input: packet size %u too small\n", len); 675142215Sglebius return (IPPROTO_DONE); 676142215Sglebius } 677142215Sglebius 678142215Sglebius 679142215Sglebius /* verify the CARP checksum */ 680142215Sglebius m->m_data += *offp; 681142215Sglebius if (carp_cksum(m, sizeof(*ch))) { 682190968Srwatson CARPSTATS_INC(carps_badsum); 683200026Sglebius CARP_DEBUG("carp6_input: checksum failed, on %s\n", 684142446Sglebius m->m_pkthdr.rcvif->if_xname); 685142215Sglebius m_freem(m); 686142215Sglebius return (IPPROTO_DONE); 687142215Sglebius } 688142215Sglebius m->m_data -= *offp; 689142215Sglebius 690142446Sglebius carp_input_c(m, ch, AF_INET6); 691142215Sglebius return (IPPROTO_DONE); 692142215Sglebius} 693142215Sglebius#endif /* INET6 */ 694142215Sglebius 695142559Sglebiusstatic void 696142446Sglebiuscarp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) 697142215Sglebius{ 698142215Sglebius struct ifnet *ifp = m->m_pkthdr.rcvif; 699142446Sglebius struct carp_softc *sc; 700142215Sglebius u_int64_t tmp_counter; 701142215Sglebius struct timeval sc_tv, ch_tv; 702142215Sglebius 703142215Sglebius /* verify that the VHID is valid on the receiving interface */ 704142215Sglebius CARP_LOCK(ifp->if_carp); 705142215Sglebius TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list) 706142215Sglebius if (sc->sc_vhid == ch->carp_vhid) 707142215Sglebius break; 708142914Sglebius 709148887Srwatson if (!sc || !((SC2IFP(sc)->if_flags & IFF_UP) && 710148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 711190968Srwatson CARPSTATS_INC(carps_badvhid); 712142914Sglebius CARP_UNLOCK(ifp->if_carp); 713142215Sglebius m_freem(m); 714142215Sglebius return; 715142215Sglebius } 716142215Sglebius 717147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 718147256Sbrooks SC2IFP(sc)->if_ipackets++; 719147256Sbrooks SC2IFP(sc)->if_ibytes += m->m_pkthdr.len; 720142215Sglebius 721159180Scsjp if (bpf_peers_present(SC2IFP(sc)->if_bpf)) { 722142215Sglebius struct ip *ip = mtod(m, struct ip *); 723142784Sglebius uint32_t af1 = af; 724142215Sglebius 725142215Sglebius /* BPF wants net byte order */ 726142784Sglebius ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2)); 727142784Sglebius ip->ip_off = htons(ip->ip_off); 728147256Sbrooks bpf_mtap2(SC2IFP(sc)->if_bpf, &af1, sizeof(af1), m); 729142215Sglebius } 730142215Sglebius 731142215Sglebius /* verify the CARP version. */ 732142215Sglebius if (ch->carp_version != CARP_VERSION) { 733190968Srwatson CARPSTATS_INC(carps_badver); 734147256Sbrooks SC2IFP(sc)->if_ierrors++; 735142914Sglebius CARP_UNLOCK(ifp->if_carp); 736200026Sglebius CARP_DEBUG("%s; invalid version %d\n", 737147256Sbrooks SC2IFP(sc)->if_xname, 738142446Sglebius ch->carp_version); 739142215Sglebius m_freem(m); 740142215Sglebius return; 741142215Sglebius } 742142215Sglebius 743142215Sglebius /* verify the hash */ 744142215Sglebius if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { 745190968Srwatson CARPSTATS_INC(carps_badauth); 746147256Sbrooks SC2IFP(sc)->if_ierrors++; 747142914Sglebius CARP_UNLOCK(ifp->if_carp); 748201801Sru CARP_DEBUG("%s: incorrect hash\n", SC2IFP(sc)->if_xname); 749142215Sglebius m_freem(m); 750142215Sglebius return; 751142215Sglebius } 752142215Sglebius 753142215Sglebius tmp_counter = ntohl(ch->carp_counter[0]); 754142215Sglebius tmp_counter = tmp_counter<<32; 755142215Sglebius tmp_counter += ntohl(ch->carp_counter[1]); 756142215Sglebius 757142215Sglebius /* XXX Replay protection goes here */ 758142215Sglebius 759142215Sglebius sc->sc_init_counter = 0; 760142215Sglebius sc->sc_counter = tmp_counter; 761142215Sglebius 762142215Sglebius sc_tv.tv_sec = sc->sc_advbase; 763142215Sglebius if (carp_suppress_preempt && sc->sc_advskew < 240) 764142215Sglebius sc_tv.tv_usec = 240 * 1000000 / 256; 765142215Sglebius else 766142215Sglebius sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; 767142215Sglebius ch_tv.tv_sec = ch->carp_advbase; 768142215Sglebius ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; 769142215Sglebius 770142215Sglebius switch (sc->sc_state) { 771142215Sglebius case INIT: 772142215Sglebius break; 773142215Sglebius case MASTER: 774142215Sglebius /* 775142215Sglebius * If we receive an advertisement from a master who's going to 776142215Sglebius * be more frequent than us, go into BACKUP state. 777142215Sglebius */ 778142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, >) || 779142215Sglebius timevalcmp(&sc_tv, &ch_tv, ==)) { 780142215Sglebius callout_stop(&sc->sc_ad_tmo); 781200026Sglebius CARP_LOG("%s: MASTER -> BACKUP " 782142452Sglebius "(more frequent advertisement received)\n", 783147256Sbrooks SC2IFP(sc)->if_xname); 784142215Sglebius carp_set_state(sc, BACKUP); 785142215Sglebius carp_setrun(sc, 0); 786142215Sglebius carp_setroute(sc, RTM_DELETE); 787142215Sglebius } 788142215Sglebius break; 789142215Sglebius case BACKUP: 790142215Sglebius /* 791142215Sglebius * If we're pre-empting masters who advertise slower than us, 792142215Sglebius * and this one claims to be slower, treat him as down. 793142215Sglebius */ 794142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && 795142215Sglebius timevalcmp(&sc_tv, &ch_tv, <)) { 796200026Sglebius CARP_LOG("%s: BACKUP -> MASTER " 797142452Sglebius "(preempting a slower master)\n", 798147256Sbrooks SC2IFP(sc)->if_xname); 799142914Sglebius carp_master_down_locked(sc); 800142215Sglebius break; 801142215Sglebius } 802142215Sglebius 803142215Sglebius /* 804142215Sglebius * If the master is going to advertise at such a low frequency 805142215Sglebius * that he's guaranteed to time out, we'd might as well just 806142215Sglebius * treat him as timed out now. 807142215Sglebius */ 808142215Sglebius sc_tv.tv_sec = sc->sc_advbase * 3; 809142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, <)) { 810200026Sglebius CARP_LOG("%s: BACKUP -> MASTER " 811142452Sglebius "(master timed out)\n", 812147256Sbrooks SC2IFP(sc)->if_xname); 813142914Sglebius carp_master_down_locked(sc); 814142215Sglebius break; 815142215Sglebius } 816142215Sglebius 817142215Sglebius /* 818142215Sglebius * Otherwise, we reset the counter and wait for the next 819142215Sglebius * advertisement. 820142215Sglebius */ 821142215Sglebius carp_setrun(sc, af); 822142215Sglebius break; 823142215Sglebius } 824142215Sglebius 825142914Sglebius CARP_UNLOCK(ifp->if_carp); 826142914Sglebius 827142215Sglebius m_freem(m); 828142215Sglebius return; 829142215Sglebius} 830142215Sglebius 831142559Sglebiusstatic int 832142215Sglebiuscarp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) 833142215Sglebius{ 834142215Sglebius struct m_tag *mtag; 835147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 836142215Sglebius 837142215Sglebius if (sc->sc_init_counter) { 838142215Sglebius /* this could also be seconds since unix epoch */ 839142215Sglebius sc->sc_counter = arc4random(); 840142215Sglebius sc->sc_counter = sc->sc_counter << 32; 841142215Sglebius sc->sc_counter += arc4random(); 842142215Sglebius } else 843142215Sglebius sc->sc_counter++; 844142215Sglebius 845142215Sglebius ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff); 846142215Sglebius ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff); 847142215Sglebius 848142215Sglebius carp_hmac_generate(sc, ch->carp_counter, ch->carp_md); 849142215Sglebius 850142215Sglebius /* Tag packet for carp_output */ 851142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT); 852142215Sglebius if (mtag == NULL) { 853142215Sglebius m_freem(m); 854147256Sbrooks SC2IFP(sc)->if_oerrors++; 855142215Sglebius return (ENOMEM); 856142215Sglebius } 857142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 858142215Sglebius m_tag_prepend(m, mtag); 859142215Sglebius 860142215Sglebius return (0); 861142215Sglebius} 862142215Sglebius 863142559Sglebiusstatic void 864142215Sglebiuscarp_send_ad_all(void) 865142215Sglebius{ 866142911Sglebius struct carp_softc *sc; 867142215Sglebius 868142911Sglebius mtx_lock(&carp_mtx); 869142911Sglebius LIST_FOREACH(sc, &carpif_list, sc_next) { 870142911Sglebius if (sc->sc_carpdev == NULL) 871142215Sglebius continue; 872142911Sglebius CARP_SCLOCK(sc); 873148887Srwatson if ((SC2IFP(sc)->if_flags & IFF_UP) && 874148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING) && 875142911Sglebius sc->sc_state == MASTER) 876142914Sglebius carp_send_ad_locked(sc); 877142911Sglebius CARP_SCUNLOCK(sc); 878142215Sglebius } 879142911Sglebius mtx_unlock(&carp_mtx); 880142215Sglebius} 881142215Sglebius 882142559Sglebiusstatic void 883142215Sglebiuscarp_send_ad(void *v) 884142215Sglebius{ 885142914Sglebius struct carp_softc *sc = v; 886142914Sglebius 887142914Sglebius CARP_SCLOCK(sc); 888142914Sglebius carp_send_ad_locked(sc); 889142914Sglebius CARP_SCUNLOCK(sc); 890142914Sglebius} 891142914Sglebius 892142914Sglebiusstatic void 893142914Sglebiuscarp_send_ad_locked(struct carp_softc *sc) 894142914Sglebius{ 895142215Sglebius struct carp_header ch; 896142215Sglebius struct timeval tv; 897142215Sglebius struct carp_header *ch_ptr; 898142215Sglebius struct mbuf *m; 899142215Sglebius int len, advbase, advskew; 900142215Sglebius 901142914Sglebius CARP_SCLOCK_ASSERT(sc); 902142914Sglebius 903142215Sglebius /* bow out if we've lost our UPness or RUNNINGuiness */ 904148887Srwatson if (!((SC2IFP(sc)->if_flags & IFF_UP) && 905148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 906142215Sglebius advbase = 255; 907142215Sglebius advskew = 255; 908142215Sglebius } else { 909142215Sglebius advbase = sc->sc_advbase; 910142215Sglebius if (!carp_suppress_preempt || sc->sc_advskew > 240) 911142215Sglebius advskew = sc->sc_advskew; 912142215Sglebius else 913142215Sglebius advskew = 240; 914142215Sglebius tv.tv_sec = advbase; 915142215Sglebius tv.tv_usec = advskew * 1000000 / 256; 916142215Sglebius } 917142215Sglebius 918142215Sglebius ch.carp_version = CARP_VERSION; 919142215Sglebius ch.carp_type = CARP_ADVERTISEMENT; 920142215Sglebius ch.carp_vhid = sc->sc_vhid; 921142215Sglebius ch.carp_advbase = advbase; 922142215Sglebius ch.carp_advskew = advskew; 923142215Sglebius ch.carp_authlen = 7; /* XXX DEFINE */ 924142215Sglebius ch.carp_pad1 = 0; /* must be zero */ 925142215Sglebius ch.carp_cksum = 0; 926142215Sglebius 927142215Sglebius#ifdef INET 928142215Sglebius if (sc->sc_ia) { 929142215Sglebius struct ip *ip; 930142215Sglebius 931142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 932142215Sglebius if (m == NULL) { 933147256Sbrooks SC2IFP(sc)->if_oerrors++; 934190968Srwatson CARPSTATS_INC(carps_onomem); 935142215Sglebius /* XXX maybe less ? */ 936142215Sglebius if (advbase != 255 || advskew != 255) 937142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 938142215Sglebius carp_send_ad, sc); 939142215Sglebius return; 940142215Sglebius } 941142215Sglebius len = sizeof(*ip) + sizeof(ch); 942142215Sglebius m->m_pkthdr.len = len; 943142215Sglebius m->m_pkthdr.rcvif = NULL; 944142215Sglebius m->m_len = len; 945142215Sglebius MH_ALIGN(m, m->m_len); 946142215Sglebius m->m_flags |= M_MCAST; 947142215Sglebius ip = mtod(m, struct ip *); 948142215Sglebius ip->ip_v = IPVERSION; 949142215Sglebius ip->ip_hl = sizeof(*ip) >> 2; 950142215Sglebius ip->ip_tos = IPTOS_LOWDELAY; 951142215Sglebius ip->ip_len = len; 952142215Sglebius ip->ip_id = ip_newid(); 953142215Sglebius ip->ip_off = IP_DF; 954142215Sglebius ip->ip_ttl = CARP_DFLTTL; 955142215Sglebius ip->ip_p = IPPROTO_CARP; 956142215Sglebius ip->ip_sum = 0; 957142215Sglebius ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr; 958142215Sglebius ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP); 959142215Sglebius 960142215Sglebius ch_ptr = (struct carp_header *)(&ip[1]); 961142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 962142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 963142215Sglebius return; 964142215Sglebius 965142215Sglebius m->m_data += sizeof(*ip); 966142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip)); 967142215Sglebius m->m_data -= sizeof(*ip); 968142215Sglebius 969147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 970147256Sbrooks SC2IFP(sc)->if_opackets++; 971147256Sbrooks SC2IFP(sc)->if_obytes += len; 972190968Srwatson CARPSTATS_INC(carps_opackets); 973142215Sglebius 974142215Sglebius if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) { 975147256Sbrooks SC2IFP(sc)->if_oerrors++; 976142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 977142215Sglebius sc->sc_sendad_errors++; 978142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 979142215Sglebius carp_suppress_preempt++; 980142914Sglebius if (carp_suppress_preempt == 1) { 981142914Sglebius CARP_SCUNLOCK(sc); 982142215Sglebius carp_send_ad_all(); 983142914Sglebius CARP_SCLOCK(sc); 984142914Sglebius } 985142215Sglebius } 986142215Sglebius sc->sc_sendad_success = 0; 987142215Sglebius } else { 988142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 989142215Sglebius if (++sc->sc_sendad_success >= 990142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 991142215Sglebius carp_suppress_preempt--; 992142215Sglebius sc->sc_sendad_errors = 0; 993142215Sglebius } 994142215Sglebius } else 995142215Sglebius sc->sc_sendad_errors = 0; 996142215Sglebius } 997142215Sglebius } 998142215Sglebius#endif /* INET */ 999142215Sglebius#ifdef INET6 1000142215Sglebius if (sc->sc_ia6) { 1001142215Sglebius struct ip6_hdr *ip6; 1002142215Sglebius 1003142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 1004142215Sglebius if (m == NULL) { 1005147256Sbrooks SC2IFP(sc)->if_oerrors++; 1006190968Srwatson CARPSTATS_INC(carps_onomem); 1007142215Sglebius /* XXX maybe less ? */ 1008142215Sglebius if (advbase != 255 || advskew != 255) 1009142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1010142215Sglebius carp_send_ad, sc); 1011142215Sglebius return; 1012142215Sglebius } 1013142215Sglebius len = sizeof(*ip6) + sizeof(ch); 1014142215Sglebius m->m_pkthdr.len = len; 1015142215Sglebius m->m_pkthdr.rcvif = NULL; 1016142215Sglebius m->m_len = len; 1017142215Sglebius MH_ALIGN(m, m->m_len); 1018142215Sglebius m->m_flags |= M_MCAST; 1019142215Sglebius ip6 = mtod(m, struct ip6_hdr *); 1020142215Sglebius bzero(ip6, sizeof(*ip6)); 1021142215Sglebius ip6->ip6_vfc |= IPV6_VERSION; 1022142215Sglebius ip6->ip6_hlim = CARP_DFLTTL; 1023142215Sglebius ip6->ip6_nxt = IPPROTO_CARP; 1024142215Sglebius bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src, 1025142215Sglebius sizeof(struct in6_addr)); 1026142215Sglebius /* set the multicast destination */ 1027142215Sglebius 1028163069Sbz ip6->ip6_dst.s6_addr16[0] = htons(0xff02); 1029142215Sglebius ip6->ip6_dst.s6_addr8[15] = 0x12; 1030163069Sbz if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) { 1031163069Sbz SC2IFP(sc)->if_oerrors++; 1032163069Sbz m_freem(m); 1033200026Sglebius CARP_DEBUG("%s: in6_setscope failed\n", __func__); 1034163069Sbz return; 1035163069Sbz } 1036142215Sglebius 1037142215Sglebius ch_ptr = (struct carp_header *)(&ip6[1]); 1038142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 1039142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 1040142215Sglebius return; 1041142215Sglebius 1042142215Sglebius m->m_data += sizeof(*ip6); 1043142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6)); 1044142215Sglebius m->m_data -= sizeof(*ip6); 1045142215Sglebius 1046147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 1047147256Sbrooks SC2IFP(sc)->if_opackets++; 1048147256Sbrooks SC2IFP(sc)->if_obytes += len; 1049190968Srwatson CARPSTATS_INC(carps_opackets6); 1050142215Sglebius 1051142215Sglebius if (ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL)) { 1052147256Sbrooks SC2IFP(sc)->if_oerrors++; 1053142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 1054142215Sglebius sc->sc_sendad_errors++; 1055142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 1056142215Sglebius carp_suppress_preempt++; 1057142914Sglebius if (carp_suppress_preempt == 1) { 1058142914Sglebius CARP_SCUNLOCK(sc); 1059142215Sglebius carp_send_ad_all(); 1060142914Sglebius CARP_SCLOCK(sc); 1061142914Sglebius } 1062142215Sglebius } 1063142215Sglebius sc->sc_sendad_success = 0; 1064142215Sglebius } else { 1065142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 1066142215Sglebius if (++sc->sc_sendad_success >= 1067142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 1068142215Sglebius carp_suppress_preempt--; 1069142215Sglebius sc->sc_sendad_errors = 0; 1070142215Sglebius } 1071142215Sglebius } else 1072142215Sglebius sc->sc_sendad_errors = 0; 1073142215Sglebius } 1074142215Sglebius } 1075142215Sglebius#endif /* INET6 */ 1076142215Sglebius 1077142215Sglebius if (advbase != 255 || advskew != 255) 1078142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1079142215Sglebius carp_send_ad, sc); 1080142215Sglebius 1081142215Sglebius} 1082142215Sglebius 1083142215Sglebius/* 1084142215Sglebius * Broadcast a gratuitous ARP request containing 1085142215Sglebius * the virtual router MAC address for each IP address 1086142215Sglebius * associated with the virtual router. 1087142215Sglebius */ 1088142559Sglebiusstatic void 1089142215Sglebiuscarp_send_arp(struct carp_softc *sc) 1090142215Sglebius{ 1091142215Sglebius struct ifaddr *ifa; 1092142215Sglebius 1093147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1094142215Sglebius 1095142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET) 1096142215Sglebius continue; 1097142215Sglebius 1098152315Sru/* arprequest(sc->sc_carpdev, &in, &in, IF_LLADDR(sc->sc_ifp)); */ 1099152315Sru arp_ifinit2(sc->sc_carpdev, ifa, IF_LLADDR(sc->sc_ifp)); 1100142215Sglebius 1101142215Sglebius DELAY(1000); /* XXX */ 1102142215Sglebius } 1103142215Sglebius} 1104142215Sglebius 1105142215Sglebius#ifdef INET6 1106142559Sglebiusstatic void 1107142215Sglebiuscarp_send_na(struct carp_softc *sc) 1108142215Sglebius{ 1109142215Sglebius struct ifaddr *ifa; 1110142215Sglebius struct in6_addr *in6; 1111142215Sglebius static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; 1112142215Sglebius 1113147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1114142215Sglebius 1115142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET6) 1116142215Sglebius continue; 1117142215Sglebius 1118142215Sglebius in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; 1119142564Sglebius nd6_na_output(sc->sc_carpdev, &mcast, in6, 1120142215Sglebius ND_NA_FLAG_OVERRIDE, 1, NULL); 1121142215Sglebius DELAY(1000); /* XXX */ 1122142215Sglebius } 1123142215Sglebius} 1124142215Sglebius#endif /* INET6 */ 1125142215Sglebius 1126142559Sglebiusstatic int 1127142215Sglebiuscarp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) 1128142215Sglebius{ 1129142215Sglebius struct carp_softc *vh; 1130142215Sglebius struct ifaddr *ifa; 1131142215Sglebius int count = 0; 1132142215Sglebius 1133142914Sglebius CARP_LOCK_ASSERT(cif); 1134142914Sglebius 1135142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1136142215Sglebius if ((type == CARP_COUNT_RUNNING && 1137148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1138148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) || 1139142215Sglebius (type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) { 1140191528Srwatson IF_ADDR_LOCK(SC2IFP(vh)); 1141147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1142142215Sglebius ifa_list) { 1143142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 1144142215Sglebius ia->ia_addr.sin_addr.s_addr == 1145142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) 1146142215Sglebius count++; 1147142215Sglebius } 1148191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1149142215Sglebius } 1150142215Sglebius } 1151142215Sglebius return (count); 1152142215Sglebius} 1153142215Sglebius 1154142215Sglebiusint 1155211157Swillcarp_iamatch(struct ifnet *ifp, struct in_ifaddr *ia, 1156142215Sglebius struct in_addr *isaddr, u_int8_t **enaddr) 1157142215Sglebius{ 1158211157Swill struct carp_if *cif; 1159142215Sglebius struct carp_softc *vh; 1160142215Sglebius int index, count = 0; 1161142215Sglebius struct ifaddr *ifa; 1162142215Sglebius 1163211157Swill cif = ifp->if_carp; 1164142215Sglebius CARP_LOCK(cif); 1165142215Sglebius 1166142215Sglebius if (carp_opts[CARPCTL_ARPBALANCE]) { 1167142215Sglebius /* 1168142215Sglebius * XXX proof of concept implementation. 1169142215Sglebius * We use the source ip to decide which virtual host should 1170142215Sglebius * handle the request. If we're master of that virtual host, 1171142215Sglebius * then we respond, otherwise, just drop the arp packet on 1172142215Sglebius * the floor. 1173142215Sglebius */ 1174142215Sglebius count = carp_addrcount(cif, ia, CARP_COUNT_RUNNING); 1175142215Sglebius if (count == 0) { 1176142215Sglebius /* should never reach this */ 1177142215Sglebius CARP_UNLOCK(cif); 1178142215Sglebius return (0); 1179142215Sglebius } 1180142215Sglebius 1181142215Sglebius /* this should be a hash, like pf_hash() */ 1182147718Sglebius index = ntohl(isaddr->s_addr) % count; 1183142215Sglebius count = 0; 1184142215Sglebius 1185142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1186148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1187148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) { 1188191528Srwatson IF_ADDR_LOCK(SC2IFP(vh)); 1189147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1190142215Sglebius ifa_list) { 1191142215Sglebius if (ifa->ifa_addr->sa_family == 1192142215Sglebius AF_INET && 1193142215Sglebius ia->ia_addr.sin_addr.s_addr == 1194142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) { 1195142215Sglebius if (count == index) { 1196142215Sglebius if (vh->sc_state == 1197142215Sglebius MASTER) { 1198152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1199191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1200142215Sglebius CARP_UNLOCK(cif); 1201142215Sglebius return (1); 1202142215Sglebius } else { 1203191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1204142215Sglebius CARP_UNLOCK(cif); 1205142215Sglebius return (0); 1206142215Sglebius } 1207142215Sglebius } 1208142215Sglebius count++; 1209142215Sglebius } 1210142215Sglebius } 1211191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1212142215Sglebius } 1213142215Sglebius } 1214142215Sglebius } else { 1215142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1216148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1217148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1218152550Sglebius ia->ia_ifp == SC2IFP(vh) && 1219152550Sglebius vh->sc_state == MASTER) { 1220152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1221142215Sglebius CARP_UNLOCK(cif); 1222142215Sglebius return (1); 1223142215Sglebius } 1224142215Sglebius } 1225142215Sglebius } 1226142215Sglebius CARP_UNLOCK(cif); 1227142215Sglebius return (0); 1228142215Sglebius} 1229142215Sglebius 1230142215Sglebius#ifdef INET6 1231142641Smlaierstruct ifaddr * 1232211157Swillcarp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr) 1233142215Sglebius{ 1234211157Swill struct carp_if *cif; 1235142215Sglebius struct carp_softc *vh; 1236142215Sglebius struct ifaddr *ifa; 1237142215Sglebius 1238211157Swill cif = ifp->if_carp; 1239142215Sglebius CARP_LOCK(cif); 1240142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1241191528Srwatson IF_ADDR_LOCK(SC2IFP(vh)); 1242147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, ifa_list) { 1243142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1244142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1245148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1246152550Sglebius (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1247152550Sglebius vh->sc_state == MASTER) { 1248194760Srwatson ifa_ref(ifa); 1249191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1250142215Sglebius CARP_UNLOCK(cif); 1251142215Sglebius return (ifa); 1252142215Sglebius } 1253142215Sglebius } 1254191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1255142215Sglebius } 1256142215Sglebius CARP_UNLOCK(cif); 1257142215Sglebius 1258142215Sglebius return (NULL); 1259142215Sglebius} 1260142215Sglebius 1261211157Swillcaddr_t 1262211157Swillcarp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr) 1263142215Sglebius{ 1264142215Sglebius struct m_tag *mtag; 1265211157Swill struct carp_if *cif; 1266142215Sglebius struct carp_softc *sc; 1267142215Sglebius struct ifaddr *ifa; 1268142215Sglebius 1269211157Swill cif = ifp->if_carp; 1270142215Sglebius CARP_LOCK(cif); 1271142215Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { 1272191528Srwatson IF_ADDR_LOCK(SC2IFP(sc)); 1273147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1274142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1275142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1276148887Srwatson (SC2IFP(sc)->if_flags & IFF_UP) && 1277148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING)) { 1278147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 1279142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, 1280142215Sglebius sizeof(struct ifnet *), M_NOWAIT); 1281142215Sglebius if (mtag == NULL) { 1282142215Sglebius /* better a bit than nothing */ 1283191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 1284142215Sglebius CARP_UNLOCK(cif); 1285152315Sru return (IF_LLADDR(sc->sc_ifp)); 1286142215Sglebius } 1287142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), 1288142215Sglebius sizeof(struct ifnet *)); 1289142215Sglebius m_tag_prepend(m, mtag); 1290142215Sglebius 1291191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 1292142215Sglebius CARP_UNLOCK(cif); 1293152315Sru return (IF_LLADDR(sc->sc_ifp)); 1294142215Sglebius } 1295142215Sglebius } 1296191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 1297142215Sglebius } 1298142215Sglebius CARP_UNLOCK(cif); 1299142215Sglebius 1300142215Sglebius return (NULL); 1301142215Sglebius} 1302142215Sglebius#endif 1303142215Sglebius 1304142215Sglebiusstruct ifnet * 1305211157Swillcarp_forus(struct ifnet *ifp, u_char *dhost) 1306142215Sglebius{ 1307211157Swill struct carp_if *cif; 1308142215Sglebius struct carp_softc *vh; 1309142215Sglebius u_int8_t *ena = dhost; 1310142215Sglebius 1311142215Sglebius if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) 1312142215Sglebius return (NULL); 1313142215Sglebius 1314211157Swill cif = ifp->if_carp; 1315142215Sglebius CARP_LOCK(cif); 1316142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) 1317148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1318148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1319148887Srwatson vh->sc_state == MASTER && 1320152315Sru !bcmp(dhost, IF_LLADDR(vh->sc_ifp), ETHER_ADDR_LEN)) { 1321142215Sglebius CARP_UNLOCK(cif); 1322147256Sbrooks return (SC2IFP(vh)); 1323142215Sglebius } 1324142215Sglebius 1325142215Sglebius CARP_UNLOCK(cif); 1326142215Sglebius return (NULL); 1327142215Sglebius} 1328142215Sglebius 1329142559Sglebiusstatic void 1330142215Sglebiuscarp_master_down(void *v) 1331142215Sglebius{ 1332142215Sglebius struct carp_softc *sc = v; 1333142215Sglebius 1334142914Sglebius CARP_SCLOCK(sc); 1335142914Sglebius carp_master_down_locked(sc); 1336142914Sglebius CARP_SCUNLOCK(sc); 1337142914Sglebius} 1338142914Sglebius 1339142914Sglebiusstatic void 1340142914Sglebiuscarp_master_down_locked(struct carp_softc *sc) 1341142914Sglebius{ 1342142914Sglebius if (sc->sc_carpdev) 1343142914Sglebius CARP_SCLOCK_ASSERT(sc); 1344142914Sglebius 1345142215Sglebius switch (sc->sc_state) { 1346142215Sglebius case INIT: 1347142215Sglebius printf("%s: master_down event in INIT state\n", 1348147256Sbrooks SC2IFP(sc)->if_xname); 1349142215Sglebius break; 1350142215Sglebius case MASTER: 1351142215Sglebius break; 1352142215Sglebius case BACKUP: 1353142215Sglebius carp_set_state(sc, MASTER); 1354142914Sglebius carp_send_ad_locked(sc); 1355142215Sglebius carp_send_arp(sc); 1356142215Sglebius#ifdef INET6 1357142215Sglebius carp_send_na(sc); 1358142215Sglebius#endif /* INET6 */ 1359142215Sglebius carp_setrun(sc, 0); 1360142215Sglebius carp_setroute(sc, RTM_ADD); 1361142215Sglebius break; 1362142215Sglebius } 1363142215Sglebius} 1364142215Sglebius 1365142215Sglebius/* 1366142215Sglebius * When in backup state, af indicates whether to reset the master down timer 1367142215Sglebius * for v4 or v6. If it's set to zero, reset the ones which are already pending. 1368142215Sglebius */ 1369142559Sglebiusstatic void 1370142215Sglebiuscarp_setrun(struct carp_softc *sc, sa_family_t af) 1371142215Sglebius{ 1372142215Sglebius struct timeval tv; 1373142215Sglebius 1374156947Sglebius if (sc->sc_carpdev == NULL) { 1375156947Sglebius SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1376156947Sglebius carp_set_state(sc, INIT); 1377156947Sglebius return; 1378156947Sglebius } else 1379142914Sglebius CARP_SCLOCK_ASSERT(sc); 1380142914Sglebius 1381147256Sbrooks if (SC2IFP(sc)->if_flags & IFF_UP && 1382211059Sdelphij sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6) && 1383211059Sdelphij sc->sc_carpdev->if_link_state == LINK_STATE_UP) 1384148887Srwatson SC2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; 1385142215Sglebius else { 1386148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1387142215Sglebius carp_setroute(sc, RTM_DELETE); 1388142215Sglebius return; 1389142215Sglebius } 1390142215Sglebius 1391142215Sglebius switch (sc->sc_state) { 1392142215Sglebius case INIT: 1393142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) { 1394142914Sglebius carp_send_ad_locked(sc); 1395142215Sglebius carp_send_arp(sc); 1396142215Sglebius#ifdef INET6 1397142215Sglebius carp_send_na(sc); 1398142215Sglebius#endif /* INET6 */ 1399200026Sglebius CARP_LOG("%s: INIT -> MASTER (preempting)\n", 1400147256Sbrooks SC2IFP(sc)->if_xname); 1401142215Sglebius carp_set_state(sc, MASTER); 1402142215Sglebius carp_setroute(sc, RTM_ADD); 1403142215Sglebius } else { 1404200026Sglebius CARP_LOG("%s: INIT -> BACKUP\n", SC2IFP(sc)->if_xname); 1405142215Sglebius carp_set_state(sc, BACKUP); 1406142215Sglebius carp_setroute(sc, RTM_DELETE); 1407142215Sglebius carp_setrun(sc, 0); 1408142215Sglebius } 1409142215Sglebius break; 1410142215Sglebius case BACKUP: 1411142215Sglebius callout_stop(&sc->sc_ad_tmo); 1412142215Sglebius tv.tv_sec = 3 * sc->sc_advbase; 1413142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1414142215Sglebius switch (af) { 1415142215Sglebius#ifdef INET 1416142215Sglebius case AF_INET: 1417142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1418142215Sglebius carp_master_down, sc); 1419142215Sglebius break; 1420142215Sglebius#endif /* INET */ 1421142215Sglebius#ifdef INET6 1422142215Sglebius case AF_INET6: 1423142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1424142215Sglebius carp_master_down, sc); 1425142215Sglebius break; 1426142215Sglebius#endif /* INET6 */ 1427142215Sglebius default: 1428142215Sglebius if (sc->sc_naddrs) 1429142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1430142215Sglebius carp_master_down, sc); 1431142215Sglebius if (sc->sc_naddrs6) 1432142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1433142215Sglebius carp_master_down, sc); 1434142215Sglebius break; 1435142215Sglebius } 1436142215Sglebius break; 1437142215Sglebius case MASTER: 1438142215Sglebius tv.tv_sec = sc->sc_advbase; 1439142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1440142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1441142215Sglebius carp_send_ad, sc); 1442142215Sglebius break; 1443142215Sglebius } 1444142215Sglebius} 1445142215Sglebius 1446166423Sglebiusstatic void 1447156947Sglebiuscarp_multicast_cleanup(struct carp_softc *sc) 1448156947Sglebius{ 1449156947Sglebius struct ip_moptions *imo = &sc->sc_imo; 1450156947Sglebius u_int16_t n = imo->imo_num_memberships; 1451166226Sglebius 1452156947Sglebius /* Clean up our own multicast memberships */ 1453156947Sglebius while (n-- > 0) { 1454156947Sglebius if (imo->imo_membership[n] != NULL) { 1455156947Sglebius in_delmulti(imo->imo_membership[n]); 1456156947Sglebius imo->imo_membership[n] = NULL; 1457156947Sglebius } 1458156947Sglebius } 1459170613Sbms KASSERT(imo->imo_mfilters == NULL, 1460170613Sbms ("%s: imo_mfilters != NULL", __func__)); 1461156947Sglebius imo->imo_num_memberships = 0; 1462156947Sglebius imo->imo_multicast_ifp = NULL; 1463166423Sglebius} 1464156947Sglebius 1465156947Sglebius#ifdef INET6 1466166423Sglebiusstatic void 1467166423Sglebiuscarp_multicast6_cleanup(struct carp_softc *sc) 1468166423Sglebius{ 1469166423Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1470191672Sbms u_int16_t n = im6o->im6o_num_memberships; 1471166423Sglebius 1472191672Sbms while (n-- > 0) { 1473191672Sbms if (im6o->im6o_membership[n] != NULL) { 1474191672Sbms in6_mc_leave(im6o->im6o_membership[n], NULL); 1475191672Sbms im6o->im6o_membership[n] = NULL; 1476191672Sbms } 1477156947Sglebius } 1478191672Sbms KASSERT(im6o->im6o_mfilters == NULL, 1479191672Sbms ("%s: im6o_mfilters != NULL", __func__)); 1480191672Sbms im6o->im6o_num_memberships = 0; 1481156947Sglebius im6o->im6o_multicast_ifp = NULL; 1482166423Sglebius} 1483156947Sglebius#endif 1484156947Sglebius 1485142559Sglebiusstatic int 1486142215Sglebiuscarp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1487142215Sglebius{ 1488142215Sglebius struct ifnet *ifp; 1489142215Sglebius struct carp_if *cif; 1490142215Sglebius struct in_ifaddr *ia, *ia_if; 1491142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1492142215Sglebius struct in_addr addr; 1493142215Sglebius u_long iaddr = htonl(sin->sin_addr.s_addr); 1494142215Sglebius int own, error; 1495142215Sglebius 1496142215Sglebius if (sin->sin_addr.s_addr == 0) { 1497147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1498142215Sglebius carp_set_state(sc, INIT); 1499142215Sglebius if (sc->sc_naddrs) 1500147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1501180513Seri if (sc->sc_carpdev) 1502180513Seri CARP_SCLOCK(sc); 1503142215Sglebius carp_setrun(sc, 0); 1504180513Seri if (sc->sc_carpdev) 1505180513Seri CARP_SCUNLOCK(sc); 1506142215Sglebius return (0); 1507142215Sglebius } 1508142215Sglebius 1509142215Sglebius /* we have to do it by hands to check we won't match on us */ 1510142215Sglebius ia_if = NULL; own = 0; 1511194951Srwatson IN_IFADDR_RLOCK(); 1512181803Sbz TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { 1513142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1514147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1515142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1516142215Sglebius (iaddr & ia->ia_subnetmask) == ia->ia_subnet) { 1517142215Sglebius if (!ia_if) 1518142215Sglebius ia_if = ia; 1519142215Sglebius if (sin->sin_addr.s_addr == 1520142215Sglebius ia->ia_addr.sin_addr.s_addr) 1521142215Sglebius own++; 1522142215Sglebius } 1523142215Sglebius } 1524142215Sglebius 1525194951Srwatson if (!ia_if) { 1526194951Srwatson IN_IFADDR_RUNLOCK(); 1527142215Sglebius return (EADDRNOTAVAIL); 1528194951Srwatson } 1529142215Sglebius 1530142215Sglebius ia = ia_if; 1531194951Srwatson ifa_ref(&ia->ia_ifa); 1532194951Srwatson IN_IFADDR_RUNLOCK(); 1533194951Srwatson 1534142215Sglebius ifp = ia->ia_ifp; 1535142215Sglebius 1536142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1537194951Srwatson (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) { 1538194951Srwatson ifa_free(&ia->ia_ifa); 1539142215Sglebius return (EADDRNOTAVAIL); 1540194951Srwatson } 1541142215Sglebius 1542142215Sglebius if (imo->imo_num_memberships == 0) { 1543142215Sglebius addr.s_addr = htonl(INADDR_CARP_GROUP); 1544194951Srwatson if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == 1545194951Srwatson NULL) { 1546194951Srwatson ifa_free(&ia->ia_ifa); 1547142215Sglebius return (ENOBUFS); 1548194951Srwatson } 1549142215Sglebius imo->imo_num_memberships++; 1550142215Sglebius imo->imo_multicast_ifp = ifp; 1551142215Sglebius imo->imo_multicast_ttl = CARP_DFLTTL; 1552142215Sglebius imo->imo_multicast_loop = 0; 1553142215Sglebius } 1554142215Sglebius 1555142215Sglebius if (!ifp->if_carp) { 1556142215Sglebius 1557184205Sdes cif = malloc(sizeof(*cif), M_CARP, 1558142215Sglebius M_WAITOK|M_ZERO); 1559142215Sglebius if (!cif) { 1560142215Sglebius error = ENOBUFS; 1561142215Sglebius goto cleanup; 1562142215Sglebius } 1563142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1564184205Sdes free(cif, M_CARP); 1565142215Sglebius goto cleanup; 1566142215Sglebius } 1567142215Sglebius 1568142215Sglebius CARP_LOCK_INIT(cif); 1569142215Sglebius CARP_LOCK(cif); 1570142215Sglebius cif->vhif_ifp = ifp; 1571142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1572142215Sglebius ifp->if_carp = cif; 1573142215Sglebius 1574142215Sglebius } else { 1575142215Sglebius struct carp_softc *vr; 1576142215Sglebius 1577142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1578142215Sglebius CARP_LOCK(cif); 1579142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1580142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1581142215Sglebius CARP_UNLOCK(cif); 1582176093Sglebius error = EEXIST; 1583142215Sglebius goto cleanup; 1584142215Sglebius } 1585142215Sglebius } 1586142215Sglebius sc->sc_ia = ia; 1587142564Sglebius sc->sc_carpdev = ifp; 1588142215Sglebius 1589142215Sglebius { /* XXX prevent endless loop if already in queue */ 1590142215Sglebius struct carp_softc *vr, *after = NULL; 1591142215Sglebius int myself = 0; 1592142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1593142215Sglebius 1594142215Sglebius /* XXX: cif should not change, right? So we still hold the lock */ 1595142215Sglebius CARP_LOCK_ASSERT(cif); 1596142215Sglebius 1597142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1598142215Sglebius if (vr == sc) 1599142215Sglebius myself = 1; 1600142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1601142215Sglebius after = vr; 1602142215Sglebius } 1603142215Sglebius 1604142215Sglebius if (!myself) { 1605142215Sglebius /* We're trying to keep things in order */ 1606142215Sglebius if (after == NULL) { 1607142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1608142215Sglebius } else { 1609142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1610142215Sglebius } 1611142215Sglebius cif->vhif_nvrs++; 1612142215Sglebius } 1613142215Sglebius } 1614142215Sglebius 1615142215Sglebius sc->sc_naddrs++; 1616147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1617142215Sglebius if (own) 1618142215Sglebius sc->sc_advskew = 0; 1619144329Sglebius carp_sc_state_locked(sc); 1620142215Sglebius carp_setrun(sc, 0); 1621142215Sglebius 1622142914Sglebius CARP_UNLOCK(cif); 1623194951Srwatson ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */ 1624142914Sglebius 1625142215Sglebius return (0); 1626142215Sglebius 1627142215Sglebiuscleanup: 1628142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1629194951Srwatson ifa_free(&ia->ia_ifa); 1630142215Sglebius return (error); 1631142215Sglebius} 1632142215Sglebius 1633142559Sglebiusstatic int 1634142215Sglebiuscarp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1635142215Sglebius{ 1636142215Sglebius int error = 0; 1637142215Sglebius 1638142215Sglebius if (!--sc->sc_naddrs) { 1639142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1640142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1641142215Sglebius 1642142914Sglebius CARP_LOCK(cif); 1643142215Sglebius callout_stop(&sc->sc_ad_tmo); 1644148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1645148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1646142215Sglebius sc->sc_vhid = -1; 1647142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1648142215Sglebius imo->imo_multicast_ifp = NULL; 1649142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1650142215Sglebius if (!--cif->vhif_nvrs) { 1651142564Sglebius sc->sc_carpdev->if_carp = NULL; 1652142215Sglebius CARP_LOCK_DESTROY(cif); 1653196397Swill free(cif, M_CARP); 1654142215Sglebius } else { 1655142215Sglebius CARP_UNLOCK(cif); 1656142215Sglebius } 1657142215Sglebius } 1658142215Sglebius 1659142215Sglebius return (error); 1660142215Sglebius} 1661142215Sglebius 1662142215Sglebius#ifdef INET6 1663142559Sglebiusstatic int 1664142215Sglebiuscarp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1665142215Sglebius{ 1666142215Sglebius struct ifnet *ifp; 1667142215Sglebius struct carp_if *cif; 1668142215Sglebius struct in6_ifaddr *ia, *ia_if; 1669142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1670148385Sume struct in6_addr in6; 1671142215Sglebius int own, error; 1672142215Sglebius 1673191672Sbms error = 0; 1674191672Sbms 1675142215Sglebius if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 1676147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1677142215Sglebius carp_set_state(sc, INIT); 1678142215Sglebius if (sc->sc_naddrs6) 1679147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1680180513Seri if (sc->sc_carpdev) 1681180513Seri CARP_SCLOCK(sc); 1682142215Sglebius carp_setrun(sc, 0); 1683180513Seri if (sc->sc_carpdev) 1684180513Seri CARP_SCUNLOCK(sc); 1685142215Sglebius return (0); 1686142215Sglebius } 1687142215Sglebius 1688142215Sglebius /* we have to do it by hands to check we won't match on us */ 1689142215Sglebius ia_if = NULL; own = 0; 1690194971Srwatson IN6_IFADDR_RLOCK(); 1691194912Srwatson TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { 1692142215Sglebius int i; 1693142215Sglebius 1694142215Sglebius for (i = 0; i < 4; i++) { 1695142215Sglebius if ((sin6->sin6_addr.s6_addr32[i] & 1696142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i]) != 1697142215Sglebius (ia->ia_addr.sin6_addr.s6_addr32[i] & 1698142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i])) 1699142215Sglebius break; 1700142215Sglebius } 1701142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1702147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1703142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1704142215Sglebius (i == 4)) { 1705142215Sglebius if (!ia_if) 1706142215Sglebius ia_if = ia; 1707142215Sglebius if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 1708142215Sglebius &ia->ia_addr.sin6_addr)) 1709142215Sglebius own++; 1710142215Sglebius } 1711142215Sglebius } 1712142215Sglebius 1713194971Srwatson if (!ia_if) { 1714194971Srwatson IN6_IFADDR_RUNLOCK(); 1715142215Sglebius return (EADDRNOTAVAIL); 1716194971Srwatson } 1717142215Sglebius ia = ia_if; 1718194971Srwatson ifa_ref(&ia->ia_ifa); 1719194971Srwatson IN6_IFADDR_RUNLOCK(); 1720142215Sglebius ifp = ia->ia_ifp; 1721142215Sglebius 1722142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1723194971Srwatson (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) { 1724194971Srwatson ifa_free(&ia->ia_ifa); 1725142215Sglebius return (EADDRNOTAVAIL); 1726194971Srwatson } 1727142215Sglebius 1728142215Sglebius if (!sc->sc_naddrs6) { 1729191672Sbms struct in6_multi *in6m; 1730191672Sbms 1731142215Sglebius im6o->im6o_multicast_ifp = ifp; 1732142215Sglebius 1733142215Sglebius /* join CARP multicast address */ 1734148385Sume bzero(&in6, sizeof(in6)); 1735148385Sume in6.s6_addr16[0] = htons(0xff02); 1736148385Sume in6.s6_addr8[15] = 0x12; 1737148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1738142215Sglebius goto cleanup; 1739191672Sbms in6m = NULL; 1740191672Sbms error = in6_mc_join(ifp, &in6, NULL, &in6m, 0); 1741191672Sbms if (error) 1742148385Sume goto cleanup; 1743191672Sbms im6o->im6o_membership[0] = in6m; 1744191672Sbms im6o->im6o_num_memberships++; 1745142215Sglebius 1746142215Sglebius /* join solicited multicast address */ 1747148385Sume bzero(&in6, sizeof(in6)); 1748148385Sume in6.s6_addr16[0] = htons(0xff02); 1749148385Sume in6.s6_addr32[1] = 0; 1750148385Sume in6.s6_addr32[2] = htonl(1); 1751148385Sume in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; 1752148385Sume in6.s6_addr8[12] = 0xff; 1753148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1754142215Sglebius goto cleanup; 1755191672Sbms in6m = NULL; 1756191672Sbms error = in6_mc_join(ifp, &in6, NULL, &in6m, 0); 1757191672Sbms if (error) 1758148385Sume goto cleanup; 1759191672Sbms im6o->im6o_membership[1] = in6m; 1760191672Sbms im6o->im6o_num_memberships++; 1761142215Sglebius } 1762142215Sglebius 1763142215Sglebius if (!ifp->if_carp) { 1764184205Sdes cif = malloc(sizeof(*cif), M_CARP, 1765142215Sglebius M_WAITOK|M_ZERO); 1766142215Sglebius if (!cif) { 1767142215Sglebius error = ENOBUFS; 1768142215Sglebius goto cleanup; 1769142215Sglebius } 1770142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1771184205Sdes free(cif, M_CARP); 1772142215Sglebius goto cleanup; 1773142215Sglebius } 1774142215Sglebius 1775142215Sglebius CARP_LOCK_INIT(cif); 1776142215Sglebius CARP_LOCK(cif); 1777142215Sglebius cif->vhif_ifp = ifp; 1778142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1779142215Sglebius ifp->if_carp = cif; 1780142215Sglebius 1781142215Sglebius } else { 1782142215Sglebius struct carp_softc *vr; 1783142215Sglebius 1784142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1785142215Sglebius CARP_LOCK(cif); 1786142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1787142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1788142215Sglebius CARP_UNLOCK(cif); 1789142215Sglebius error = EINVAL; 1790142215Sglebius goto cleanup; 1791142215Sglebius } 1792142215Sglebius } 1793142215Sglebius sc->sc_ia6 = ia; 1794142564Sglebius sc->sc_carpdev = ifp; 1795142215Sglebius 1796142215Sglebius { /* XXX prevent endless loop if already in queue */ 1797142215Sglebius struct carp_softc *vr, *after = NULL; 1798142215Sglebius int myself = 0; 1799142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1800142215Sglebius CARP_LOCK_ASSERT(cif); 1801142215Sglebius 1802142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1803142215Sglebius if (vr == sc) 1804142215Sglebius myself = 1; 1805142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1806142215Sglebius after = vr; 1807142215Sglebius } 1808142215Sglebius 1809142215Sglebius if (!myself) { 1810142215Sglebius /* We're trying to keep things in order */ 1811142215Sglebius if (after == NULL) { 1812142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1813142215Sglebius } else { 1814142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1815142215Sglebius } 1816142215Sglebius cif->vhif_nvrs++; 1817142215Sglebius } 1818142215Sglebius } 1819142215Sglebius 1820142215Sglebius sc->sc_naddrs6++; 1821147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1822142215Sglebius if (own) 1823142215Sglebius sc->sc_advskew = 0; 1824144329Sglebius carp_sc_state_locked(sc); 1825142215Sglebius carp_setrun(sc, 0); 1826142215Sglebius 1827142914Sglebius CARP_UNLOCK(cif); 1828194971Srwatson ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */ 1829142914Sglebius 1830142215Sglebius return (0); 1831142215Sglebius 1832142215Sglebiuscleanup: 1833191672Sbms if (!sc->sc_naddrs6) 1834191672Sbms carp_multicast6_cleanup(sc); 1835194971Srwatson ifa_free(&ia->ia_ifa); 1836142215Sglebius return (error); 1837142215Sglebius} 1838142215Sglebius 1839142559Sglebiusstatic int 1840142215Sglebiuscarp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1841142215Sglebius{ 1842142215Sglebius int error = 0; 1843142215Sglebius 1844142215Sglebius if (!--sc->sc_naddrs6) { 1845142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1846142215Sglebius 1847142914Sglebius CARP_LOCK(cif); 1848142215Sglebius callout_stop(&sc->sc_ad_tmo); 1849148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1850148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1851142215Sglebius sc->sc_vhid = -1; 1852191672Sbms carp_multicast6_cleanup(sc); 1853142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1854142215Sglebius if (!--cif->vhif_nvrs) { 1855142215Sglebius CARP_LOCK_DESTROY(cif); 1856142564Sglebius sc->sc_carpdev->if_carp = NULL; 1857196397Swill free(cif, M_CARP); 1858142215Sglebius } else 1859142215Sglebius CARP_UNLOCK(cif); 1860142215Sglebius } 1861142215Sglebius 1862142215Sglebius return (error); 1863142215Sglebius} 1864142215Sglebius#endif /* INET6 */ 1865142215Sglebius 1866142559Sglebiusstatic int 1867142215Sglebiuscarp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) 1868142215Sglebius{ 1869142215Sglebius struct carp_softc *sc = ifp->if_softc, *vr; 1870142215Sglebius struct carpreq carpr; 1871142215Sglebius struct ifaddr *ifa; 1872142215Sglebius struct ifreq *ifr; 1873142215Sglebius struct ifaliasreq *ifra; 1874142914Sglebius int locked = 0, error = 0; 1875142215Sglebius 1876142215Sglebius ifa = (struct ifaddr *)addr; 1877142215Sglebius ifra = (struct ifaliasreq *)addr; 1878142215Sglebius ifr = (struct ifreq *)addr; 1879142215Sglebius 1880142215Sglebius switch (cmd) { 1881142215Sglebius case SIOCSIFADDR: 1882142215Sglebius switch (ifa->ifa_addr->sa_family) { 1883142215Sglebius#ifdef INET 1884142215Sglebius case AF_INET: 1885147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1886142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1887142215Sglebius sizeof(struct sockaddr)); 1888142215Sglebius error = carp_set_addr(sc, satosin(ifa->ifa_addr)); 1889142215Sglebius break; 1890142215Sglebius#endif /* INET */ 1891142215Sglebius#ifdef INET6 1892142215Sglebius case AF_INET6: 1893147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1894142215Sglebius error = carp_set_addr6(sc, satosin6(ifa->ifa_addr)); 1895142215Sglebius break; 1896142215Sglebius#endif /* INET6 */ 1897142215Sglebius default: 1898142215Sglebius error = EAFNOSUPPORT; 1899142215Sglebius break; 1900142215Sglebius } 1901142215Sglebius break; 1902142215Sglebius 1903142215Sglebius case SIOCAIFADDR: 1904142215Sglebius switch (ifa->ifa_addr->sa_family) { 1905142215Sglebius#ifdef INET 1906142215Sglebius case AF_INET: 1907147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1908142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1909142215Sglebius sizeof(struct sockaddr)); 1910142215Sglebius error = carp_set_addr(sc, satosin(&ifra->ifra_addr)); 1911142215Sglebius break; 1912142215Sglebius#endif /* INET */ 1913142215Sglebius#ifdef INET6 1914142215Sglebius case AF_INET6: 1915147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1916142215Sglebius error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr)); 1917142215Sglebius break; 1918142215Sglebius#endif /* INET6 */ 1919142215Sglebius default: 1920142215Sglebius error = EAFNOSUPPORT; 1921142215Sglebius break; 1922142215Sglebius } 1923142215Sglebius break; 1924142215Sglebius 1925142215Sglebius case SIOCDIFADDR: 1926142215Sglebius switch (ifa->ifa_addr->sa_family) { 1927142215Sglebius#ifdef INET 1928142215Sglebius case AF_INET: 1929142215Sglebius error = carp_del_addr(sc, satosin(&ifra->ifra_addr)); 1930142215Sglebius break; 1931142215Sglebius#endif /* INET */ 1932142215Sglebius#ifdef INET6 1933142215Sglebius case AF_INET6: 1934142215Sglebius error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr)); 1935142215Sglebius break; 1936142215Sglebius#endif /* INET6 */ 1937142215Sglebius default: 1938142215Sglebius error = EAFNOSUPPORT; 1939142215Sglebius break; 1940142215Sglebius } 1941142215Sglebius break; 1942142215Sglebius 1943142215Sglebius case SIOCSIFFLAGS: 1944142914Sglebius if (sc->sc_carpdev) { 1945142914Sglebius locked = 1; 1946142914Sglebius CARP_SCLOCK(sc); 1947142914Sglebius } 1948142215Sglebius if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { 1949142215Sglebius callout_stop(&sc->sc_ad_tmo); 1950142215Sglebius callout_stop(&sc->sc_md_tmo); 1951142215Sglebius callout_stop(&sc->sc_md6_tmo); 1952142215Sglebius if (sc->sc_state == MASTER) 1953142914Sglebius carp_send_ad_locked(sc); 1954142215Sglebius carp_set_state(sc, INIT); 1955142215Sglebius carp_setrun(sc, 0); 1956142215Sglebius } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { 1957147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1958142215Sglebius carp_setrun(sc, 0); 1959142215Sglebius } 1960142215Sglebius break; 1961142215Sglebius 1962142215Sglebius case SIOCSVH: 1963164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 1964164033Srwatson if (error) 1965142215Sglebius break; 1966142215Sglebius if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) 1967142215Sglebius break; 1968142215Sglebius error = 1; 1969142914Sglebius if (sc->sc_carpdev) { 1970142914Sglebius locked = 1; 1971142914Sglebius CARP_SCLOCK(sc); 1972142914Sglebius } 1973142215Sglebius if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { 1974142215Sglebius switch (carpr.carpr_state) { 1975142215Sglebius case BACKUP: 1976142215Sglebius callout_stop(&sc->sc_ad_tmo); 1977142215Sglebius carp_set_state(sc, BACKUP); 1978142215Sglebius carp_setrun(sc, 0); 1979142215Sglebius carp_setroute(sc, RTM_DELETE); 1980142215Sglebius break; 1981142215Sglebius case MASTER: 1982142914Sglebius carp_master_down_locked(sc); 1983142215Sglebius break; 1984142215Sglebius default: 1985142215Sglebius break; 1986142215Sglebius } 1987142215Sglebius } 1988142215Sglebius if (carpr.carpr_vhid > 0) { 1989142215Sglebius if (carpr.carpr_vhid > 255) { 1990142215Sglebius error = EINVAL; 1991142215Sglebius break; 1992142215Sglebius } 1993142564Sglebius if (sc->sc_carpdev) { 1994142215Sglebius struct carp_if *cif; 1995142564Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1996142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1997142215Sglebius if (vr != sc && 1998170373Sglebius vr->sc_vhid == carpr.carpr_vhid) { 1999170373Sglebius error = EEXIST; 2000170373Sglebius break; 2001170373Sglebius } 2002170373Sglebius if (error == EEXIST) 2003170373Sglebius break; 2004142215Sglebius } 2005142215Sglebius sc->sc_vhid = carpr.carpr_vhid; 2006152315Sru IF_LLADDR(sc->sc_ifp)[0] = 0; 2007152315Sru IF_LLADDR(sc->sc_ifp)[1] = 0; 2008152315Sru IF_LLADDR(sc->sc_ifp)[2] = 0x5e; 2009152315Sru IF_LLADDR(sc->sc_ifp)[3] = 0; 2010152315Sru IF_LLADDR(sc->sc_ifp)[4] = 1; 2011152315Sru IF_LLADDR(sc->sc_ifp)[5] = sc->sc_vhid; 2012142215Sglebius error--; 2013142215Sglebius } 2014142215Sglebius if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { 2015142215Sglebius if (carpr.carpr_advskew >= 255) { 2016142215Sglebius error = EINVAL; 2017142215Sglebius break; 2018142215Sglebius } 2019142215Sglebius if (carpr.carpr_advbase > 255) { 2020142215Sglebius error = EINVAL; 2021142215Sglebius break; 2022142215Sglebius } 2023142215Sglebius sc->sc_advbase = carpr.carpr_advbase; 2024142215Sglebius sc->sc_advskew = carpr.carpr_advskew; 2025142215Sglebius error--; 2026142215Sglebius } 2027142215Sglebius bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); 2028142215Sglebius if (error > 0) 2029142215Sglebius error = EINVAL; 2030142215Sglebius else { 2031142215Sglebius error = 0; 2032142215Sglebius carp_setrun(sc, 0); 2033142215Sglebius } 2034142215Sglebius break; 2035142215Sglebius 2036142215Sglebius case SIOCGVH: 2037142914Sglebius /* XXX: lockless read */ 2038142215Sglebius bzero(&carpr, sizeof(carpr)); 2039142215Sglebius carpr.carpr_state = sc->sc_state; 2040142215Sglebius carpr.carpr_vhid = sc->sc_vhid; 2041142215Sglebius carpr.carpr_advbase = sc->sc_advbase; 2042142215Sglebius carpr.carpr_advskew = sc->sc_advskew; 2043164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 2044164033Srwatson if (error == 0) 2045142215Sglebius bcopy(sc->sc_key, carpr.carpr_key, 2046142215Sglebius sizeof(carpr.carpr_key)); 2047142215Sglebius error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); 2048142215Sglebius break; 2049142215Sglebius 2050142215Sglebius default: 2051142215Sglebius error = EINVAL; 2052142215Sglebius } 2053142215Sglebius 2054142914Sglebius if (locked) 2055142914Sglebius CARP_SCUNLOCK(sc); 2056142914Sglebius 2057142215Sglebius carp_hmac_prepare(sc); 2058142914Sglebius 2059142215Sglebius return (error); 2060142215Sglebius} 2061142215Sglebius 2062142215Sglebius/* 2063142215Sglebius * XXX: this is looutput. We should eventually use it from there. 2064142215Sglebius */ 2065142215Sglebiusstatic int 2066142215Sglebiuscarp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 2067191148Skmacy struct route *ro) 2068142215Sglebius{ 2069147611Sdwmalone u_int32_t af; 2070191148Skmacy struct rtentry *rt = NULL; 2071147611Sdwmalone 2072142215Sglebius M_ASSERTPKTHDR(m); /* check if we have the packet header */ 2073142215Sglebius 2074191148Skmacy if (ro != NULL) 2075191148Skmacy rt = ro->ro_rt; 2076142215Sglebius if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 2077142215Sglebius m_freem(m); 2078142215Sglebius return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 2079142215Sglebius rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 2080142215Sglebius } 2081142215Sglebius 2082142215Sglebius ifp->if_opackets++; 2083142215Sglebius ifp->if_obytes += m->m_pkthdr.len; 2084147611Sdwmalone 2085147611Sdwmalone /* BPF writes need to be handled specially. */ 2086147611Sdwmalone if (dst->sa_family == AF_UNSPEC) { 2087147611Sdwmalone bcopy(dst->sa_data, &af, sizeof(af)); 2088147611Sdwmalone dst->sa_family = af; 2089147611Sdwmalone } 2090147611Sdwmalone 2091142215Sglebius#if 1 /* XXX */ 2092142215Sglebius switch (dst->sa_family) { 2093142215Sglebius case AF_INET: 2094142215Sglebius case AF_INET6: 2095142215Sglebius case AF_IPX: 2096142215Sglebius case AF_APPLETALK: 2097142215Sglebius break; 2098142215Sglebius default: 2099142215Sglebius printf("carp_looutput: af=%d unexpected\n", dst->sa_family); 2100142215Sglebius m_freem(m); 2101142215Sglebius return (EAFNOSUPPORT); 2102142215Sglebius } 2103142215Sglebius#endif 2104142215Sglebius return(if_simloop(ifp, m, dst->sa_family, 0)); 2105142215Sglebius} 2106142215Sglebius 2107142215Sglebius/* 2108142215Sglebius * Start output on carp interface. This function should never be called. 2109142215Sglebius */ 2110142559Sglebiusstatic void 2111142215Sglebiuscarp_start(struct ifnet *ifp) 2112142215Sglebius{ 2113142215Sglebius#ifdef DEBUG 2114142215Sglebius printf("%s: start called\n", ifp->if_xname); 2115142215Sglebius#endif 2116142215Sglebius} 2117142215Sglebius 2118142215Sglebiusint 2119142215Sglebiuscarp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, 2120142215Sglebius struct rtentry *rt) 2121142215Sglebius{ 2122142215Sglebius struct m_tag *mtag; 2123142215Sglebius struct carp_softc *sc; 2124142215Sglebius struct ifnet *carp_ifp; 2125142215Sglebius 2126142215Sglebius if (!sa) 2127142215Sglebius return (0); 2128142215Sglebius 2129142215Sglebius switch (sa->sa_family) { 2130142215Sglebius#ifdef INET 2131142215Sglebius case AF_INET: 2132142215Sglebius break; 2133142215Sglebius#endif /* INET */ 2134142215Sglebius#ifdef INET6 2135142215Sglebius case AF_INET6: 2136142215Sglebius break; 2137142215Sglebius#endif /* INET6 */ 2138142215Sglebius default: 2139142215Sglebius return (0); 2140142215Sglebius } 2141142215Sglebius 2142142215Sglebius mtag = m_tag_find(m, PACKET_TAG_CARP, NULL); 2143142215Sglebius if (mtag == NULL) 2144142215Sglebius return (0); 2145142215Sglebius 2146142215Sglebius bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *)); 2147142215Sglebius sc = carp_ifp->if_softc; 2148142215Sglebius 2149142215Sglebius /* Set the source MAC address to Virtual Router MAC Address */ 2150142215Sglebius switch (ifp->if_type) { 2151142798Syar case IFT_ETHER: 2152142798Syar case IFT_L2VLAN: { 2153142215Sglebius struct ether_header *eh; 2154142215Sglebius 2155142215Sglebius eh = mtod(m, struct ether_header *); 2156142215Sglebius eh->ether_shost[0] = 0; 2157142215Sglebius eh->ether_shost[1] = 0; 2158142215Sglebius eh->ether_shost[2] = 0x5e; 2159142215Sglebius eh->ether_shost[3] = 0; 2160142215Sglebius eh->ether_shost[4] = 1; 2161142215Sglebius eh->ether_shost[5] = sc->sc_vhid; 2162142215Sglebius } 2163142215Sglebius break; 2164142215Sglebius case IFT_FDDI: { 2165142215Sglebius struct fddi_header *fh; 2166142215Sglebius 2167142215Sglebius fh = mtod(m, struct fddi_header *); 2168142215Sglebius fh->fddi_shost[0] = 0; 2169142215Sglebius fh->fddi_shost[1] = 0; 2170142215Sglebius fh->fddi_shost[2] = 0x5e; 2171142215Sglebius fh->fddi_shost[3] = 0; 2172142215Sglebius fh->fddi_shost[4] = 1; 2173142215Sglebius fh->fddi_shost[5] = sc->sc_vhid; 2174142215Sglebius } 2175142215Sglebius break; 2176142215Sglebius case IFT_ISO88025: { 2177142215Sglebius struct iso88025_header *th; 2178142215Sglebius th = mtod(m, struct iso88025_header *); 2179142215Sglebius th->iso88025_shost[0] = 3; 2180142215Sglebius th->iso88025_shost[1] = 0; 2181142215Sglebius th->iso88025_shost[2] = 0x40 >> (sc->sc_vhid - 1); 2182142215Sglebius th->iso88025_shost[3] = 0x40000 >> (sc->sc_vhid - 1); 2183142215Sglebius th->iso88025_shost[4] = 0; 2184142215Sglebius th->iso88025_shost[5] = 0; 2185142215Sglebius } 2186142215Sglebius break; 2187142215Sglebius default: 2188142215Sglebius printf("%s: carp is not supported for this interface type\n", 2189142215Sglebius ifp->if_xname); 2190142215Sglebius return (EOPNOTSUPP); 2191142215Sglebius } 2192142215Sglebius 2193142215Sglebius return (0); 2194142215Sglebius} 2195142215Sglebius 2196142559Sglebiusstatic void 2197142215Sglebiuscarp_set_state(struct carp_softc *sc, int state) 2198142215Sglebius{ 2199185636Sglebius int link_state; 2200142914Sglebius 2201142914Sglebius if (sc->sc_carpdev) 2202142914Sglebius CARP_SCLOCK_ASSERT(sc); 2203142914Sglebius 2204142215Sglebius if (sc->sc_state == state) 2205142215Sglebius return; 2206142215Sglebius 2207142215Sglebius sc->sc_state = state; 2208142215Sglebius switch (state) { 2209142215Sglebius case BACKUP: 2210185636Sglebius link_state = LINK_STATE_DOWN; 2211142215Sglebius break; 2212142215Sglebius case MASTER: 2213185636Sglebius link_state = LINK_STATE_UP; 2214142215Sglebius break; 2215142215Sglebius default: 2216185636Sglebius link_state = LINK_STATE_UNKNOWN; 2217142215Sglebius break; 2218142215Sglebius } 2219185636Sglebius if_link_state_change(SC2IFP(sc), link_state); 2220142215Sglebius} 2221142215Sglebius 2222142215Sglebiusvoid 2223211157Swillcarp_carpdev_state(struct ifnet *ifp) 2224142215Sglebius{ 2225211157Swill struct carp_if *cif; 2226142914Sglebius 2227211157Swill cif = ifp->if_carp; 2228142914Sglebius CARP_LOCK(cif); 2229142914Sglebius carp_carpdev_state_locked(cif); 2230142914Sglebius CARP_UNLOCK(cif); 2231142914Sglebius} 2232142914Sglebius 2233142914Sglebiusstatic void 2234142914Sglebiuscarp_carpdev_state_locked(struct carp_if *cif) 2235142914Sglebius{ 2236142215Sglebius struct carp_softc *sc; 2237142215Sglebius 2238144329Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) 2239144329Sglebius carp_sc_state_locked(sc); 2240144329Sglebius} 2241144329Sglebius 2242144329Sglebiusstatic void 2243144329Sglebiuscarp_sc_state_locked(struct carp_softc *sc) 2244144329Sglebius{ 2245144329Sglebius CARP_SCLOCK_ASSERT(sc); 2246144329Sglebius 2247144329Sglebius if (sc->sc_carpdev->if_link_state != LINK_STATE_UP || 2248144329Sglebius !(sc->sc_carpdev->if_flags & IFF_UP)) { 2249147256Sbrooks sc->sc_flags_backup = SC2IFP(sc)->if_flags; 2250148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 2251148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 2252144329Sglebius callout_stop(&sc->sc_ad_tmo); 2253144329Sglebius callout_stop(&sc->sc_md_tmo); 2254144329Sglebius callout_stop(&sc->sc_md6_tmo); 2255144329Sglebius carp_set_state(sc, INIT); 2256144329Sglebius carp_setrun(sc, 0); 2257144329Sglebius if (!sc->sc_suppress) { 2258144329Sglebius carp_suppress_preempt++; 2259144329Sglebius if (carp_suppress_preempt == 1) { 2260144329Sglebius CARP_SCUNLOCK(sc); 2261144329Sglebius carp_send_ad_all(); 2262144329Sglebius CARP_SCLOCK(sc); 2263142215Sglebius } 2264142215Sglebius } 2265144329Sglebius sc->sc_suppress = 1; 2266144329Sglebius } else { 2267147256Sbrooks SC2IFP(sc)->if_flags |= sc->sc_flags_backup; 2268144329Sglebius carp_set_state(sc, INIT); 2269144329Sglebius carp_setrun(sc, 0); 2270144329Sglebius if (sc->sc_suppress) 2271144329Sglebius carp_suppress_preempt--; 2272144329Sglebius sc->sc_suppress = 0; 2273142215Sglebius } 2274144329Sglebius 2275144329Sglebius return; 2276142215Sglebius} 2277142215Sglebius 2278211157Swill#ifdef INET 2279211157Swillextern struct domain inetdomain; 2280211157Swillstatic struct protosw in_carp_protosw = { 2281211157Swill .pr_type = SOCK_RAW, 2282211157Swill .pr_domain = &inetdomain, 2283211157Swill .pr_protocol = IPPROTO_CARP, 2284211157Swill .pr_flags = PR_ATOMIC|PR_ADDR, 2285211157Swill .pr_input = carp_input, 2286211157Swill .pr_output = (pr_output_t *)rip_output, 2287211157Swill .pr_ctloutput = rip_ctloutput, 2288211157Swill .pr_usrreqs = &rip_usrreqs 2289211157Swill}; 2290211157Swill#endif 2291211157Swill 2292211157Swill#ifdef INET6 2293211157Swillextern struct domain inet6domain; 2294211157Swillstatic struct ip6protosw in6_carp_protosw = { 2295211157Swill .pr_type = SOCK_RAW, 2296211157Swill .pr_domain = &inet6domain, 2297211157Swill .pr_protocol = IPPROTO_CARP, 2298211157Swill .pr_flags = PR_ATOMIC|PR_ADDR, 2299211157Swill .pr_input = carp6_input, 2300211157Swill .pr_output = rip6_output, 2301211157Swill .pr_ctloutput = rip6_ctloutput, 2302211157Swill .pr_usrreqs = &rip6_usrreqs 2303211157Swill}; 2304211157Swill#endif 2305211157Swill 2306211157Swillstatic void 2307211157Swillcarp_mod_cleanup(void) 2308211157Swill{ 2309211157Swill 2310211157Swill if (if_detach_event_tag == NULL) 2311211157Swill return; 2312211157Swill EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag); 2313211157Swill if_clone_detach(&carp_cloner); 2314211157Swill#ifdef INET 2315211157Swill if (proto_reg[CARP_INET] == 0) { 2316211157Swill pf_proto_unregister(PF_INET, IPPROTO_CARP, SOCK_RAW); 2317211157Swill proto_reg[CARP_INET] = -1; 2318211157Swill } 2319211157Swill carp_iamatch_p = NULL; 2320211157Swill#endif 2321211157Swill#ifdef INET6 2322211157Swill if (proto_reg[CARP_INET6] == 0) { 2323211157Swill pf_proto_unregister(PF_INET6, IPPROTO_CARP, SOCK_RAW); 2324211157Swill proto_reg[CARP_INET6] = -1; 2325211157Swill } 2326211157Swill carp_iamatch6_p = NULL; 2327211157Swill carp_macmatch6_p = NULL; 2328211157Swill#endif 2329211157Swill carp_linkstate_p = NULL; 2330211157Swill carp_forus_p = NULL; 2331211157Swill carp_output_p = NULL; 2332211157Swill mtx_destroy(&carp_mtx); 2333211157Swill} 2334211157Swill 2335142215Sglebiusstatic int 2336211157Swillcarp_mod_load(void) 2337211157Swill{ 2338211157Swill 2339211157Swill if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 2340211157Swill carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 2341211157Swill if (if_detach_event_tag == NULL) 2342211157Swill return (ENOMEM); 2343211157Swill mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); 2344211157Swill LIST_INIT(&carpif_list); 2345211157Swill if_clone_attach(&carp_cloner); 2346211157Swill carp_linkstate_p = carp_carpdev_state; 2347211157Swill carp_forus_p = carp_forus; 2348211157Swill carp_output_p = carp_output; 2349211157Swill#ifdef INET6 2350211157Swill carp_iamatch6_p = carp_iamatch6; 2351211157Swill carp_macmatch6_p = carp_macmatch6; 2352211157Swill proto_reg[CARP_INET6] = pf_proto_register(PF_INET6, 2353211157Swill (struct protosw *)&in6_carp_protosw); 2354211157Swill if (proto_reg[CARP_INET6] != 0) { 2355211157Swill printf("carp: error %d attaching to PF_INET6\n", 2356211157Swill proto_reg[CARP_INET6]); 2357211157Swill carp_mod_cleanup(); 2358211157Swill return (EINVAL); 2359211157Swill } 2360211157Swill#endif 2361211157Swill#ifdef INET 2362211157Swill carp_iamatch_p = carp_iamatch; 2363211157Swill proto_reg[CARP_INET] = pf_proto_register(PF_INET, &in_carp_protosw); 2364211157Swill if (proto_reg[CARP_INET] != 0) { 2365211157Swill printf("carp: error %d attaching to PF_INET\n", 2366211157Swill proto_reg[CARP_INET]); 2367211157Swill carp_mod_cleanup(); 2368211157Swill return (EINVAL); 2369211157Swill } 2370211157Swill#endif 2371211157Swill return 0; 2372211157Swill} 2373211157Swill 2374211157Swillstatic int 2375142215Sglebiuscarp_modevent(module_t mod, int type, void *data) 2376142215Sglebius{ 2377142215Sglebius switch (type) { 2378142215Sglebius case MOD_LOAD: 2379211157Swill return carp_mod_load(); 2380211157Swill /* NOTREACHED */ 2381142215Sglebius case MOD_UNLOAD: 2382211157Swill /* 2383211157Swill * XXX: For now, disallow module unloading by default due to 2384211157Swill * a race condition where a thread may dereference one of the 2385211157Swill * function pointer hooks after the module has been 2386211157Swill * unloaded, during processing of a packet, causing a panic. 2387211157Swill */ 2388211157Swill#ifdef CARPMOD_CAN_UNLOAD 2389211157Swill carp_mod_cleanup(); 2390211157Swill#else 2391211157Swill return (EBUSY); 2392211157Swill#endif 2393142215Sglebius break; 2394142215Sglebius 2395142215Sglebius default: 2396156947Sglebius return (EINVAL); 2397142215Sglebius } 2398142215Sglebius 2399156947Sglebius return (0); 2400142215Sglebius} 2401142215Sglebius 2402142215Sglebiusstatic moduledata_t carp_mod = { 2403142215Sglebius "carp", 2404142215Sglebius carp_modevent, 2405142215Sglebius 0 2406142215Sglebius}; 2407142215Sglebius 2408212265SwillDECLARE_MODULE(carp, carp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); 2409