ip_carp.c revision 221130
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 221130 2011-04-27 19:30:44Z bz $"); 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 69221130Sbz#if defined(INET) || defined(INET6) 70142215Sglebius#include <netinet/in.h> 71142215Sglebius#include <netinet/in_var.h> 72221130Sbz#include <netinet/ip_carp.h> 73221130Sbz#include <netinet/ip.h> 74221130Sbz 75221130Sbz#include <machine/in_cksum.h> 76221130Sbz#endif 77221130Sbz 78221130Sbz#ifdef INET 79142215Sglebius#include <netinet/in_systm.h> 80142215Sglebius#include <netinet/ip_var.h> 81142215Sglebius#include <netinet/if_ether.h> 82142215Sglebius#endif 83142215Sglebius 84142215Sglebius#ifdef INET6 85142215Sglebius#include <netinet/icmp6.h> 86142215Sglebius#include <netinet/ip6.h> 87211157Swill#include <netinet6/ip6protosw.h> 88142215Sglebius#include <netinet6/ip6_var.h> 89148387Sume#include <netinet6/scope6_var.h> 90221130Sbz#include <netinet6/in6_var.h> 91142215Sglebius#include <netinet6/nd6.h> 92142215Sglebius#endif 93142215Sglebius 94142215Sglebius#include <crypto/sha1.h> 95142215Sglebius 96142215Sglebius#define CARP_IFNAME "carp" 97142215Sglebiusstatic MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces"); 98142215SglebiusSYSCTL_DECL(_net_inet_carp); 99142215Sglebius 100142215Sglebiusstruct carp_softc { 101147256Sbrooks struct ifnet *sc_ifp; /* Interface clue */ 102142901Sglebius struct ifnet *sc_carpdev; /* Pointer to parent interface */ 103142215Sglebius struct in_ifaddr *sc_ia; /* primary iface address */ 104221130Sbz#ifdef INET 105142215Sglebius struct ip_moptions sc_imo; 106221130Sbz#endif 107142215Sglebius#ifdef INET6 108142215Sglebius struct in6_ifaddr *sc_ia6; /* primary iface address v6 */ 109142215Sglebius struct ip6_moptions sc_im6o; 110142215Sglebius#endif /* INET6 */ 111142215Sglebius TAILQ_ENTRY(carp_softc) sc_list; 112142215Sglebius 113142215Sglebius enum { INIT = 0, BACKUP, MASTER } sc_state; 114142215Sglebius 115142215Sglebius int sc_flags_backup; 116142215Sglebius int sc_suppress; 117142215Sglebius 118142215Sglebius int sc_sendad_errors; 119142215Sglebius#define CARP_SENDAD_MAX_ERRORS 3 120142215Sglebius int sc_sendad_success; 121142215Sglebius#define CARP_SENDAD_MIN_SUCCESS 3 122142215Sglebius 123142215Sglebius int sc_vhid; 124142215Sglebius int sc_advskew; 125142215Sglebius int sc_naddrs; 126142215Sglebius int sc_naddrs6; 127142215Sglebius int sc_advbase; /* seconds */ 128142215Sglebius int sc_init_counter; 129142215Sglebius u_int64_t sc_counter; 130142215Sglebius 131142215Sglebius /* authentication */ 132142215Sglebius#define CARP_HMAC_PAD 64 133142215Sglebius unsigned char sc_key[CARP_KEY_LEN]; 134142215Sglebius unsigned char sc_pad[CARP_HMAC_PAD]; 135142215Sglebius SHA1_CTX sc_sha1; 136142215Sglebius 137142215Sglebius struct callout sc_ad_tmo; /* advertisement timeout */ 138142215Sglebius struct callout sc_md_tmo; /* master down timeout */ 139142215Sglebius struct callout sc_md6_tmo; /* master down timeout */ 140142215Sglebius 141142215Sglebius LIST_ENTRY(carp_softc) sc_next; /* Interface clue */ 142142215Sglebius}; 143147256Sbrooks#define SC2IFP(sc) ((sc)->sc_ifp) 144142215Sglebius 145142215Sglebiusint carp_suppress_preempt = 0; 146142215Sglebiusint carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */ 147211157SwillSYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP"); 148142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW, 149142215Sglebius &carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets"); 150142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW, 151142215Sglebius &carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode"); 152142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW, 153142215Sglebius &carp_opts[CARPCTL_LOG], 0, "log bad carp packets"); 154142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW, 155142215Sglebius &carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses"); 156146226SglebiusSYSCTL_INT(_net_inet_carp, OID_AUTO, suppress_preempt, CTLFLAG_RD, 157146226Sglebius &carp_suppress_preempt, 0, "Preemption is suppressed"); 158142215Sglebius 159142215Sglebiusstruct carpstats carpstats; 160142215SglebiusSYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW, 161142215Sglebius &carpstats, carpstats, 162142215Sglebius "CARP statistics (struct carpstats, netinet/ip_carp.h)"); 163142215Sglebius 164142215Sglebiusstruct carp_if { 165142215Sglebius TAILQ_HEAD(, carp_softc) vhif_vrs; 166142215Sglebius int vhif_nvrs; 167142215Sglebius 168142215Sglebius struct ifnet *vhif_ifp; 169142215Sglebius struct mtx vhif_mtx; 170142215Sglebius}; 171142914Sglebius 172211157Swill#define CARP_INET 0 173211157Swill#define CARP_INET6 1 174211157Swillstatic int proto_reg[] = {-1, -1}; 175211157Swill 176142914Sglebius/* Get carp_if from softc. Valid after carp_set_addr{,6}. */ 177142914Sglebius#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp) 178142914Sglebius 179142215Sglebius/* lock per carp_if queue */ 180142914Sglebius#define CARP_LOCK_INIT(cif) mtx_init(&(cif)->vhif_mtx, "carp_if", \ 181142215Sglebius NULL, MTX_DEF) 182142914Sglebius#define CARP_LOCK_DESTROY(cif) mtx_destroy(&(cif)->vhif_mtx) 183142215Sglebius#define CARP_LOCK_ASSERT(cif) mtx_assert(&(cif)->vhif_mtx, MA_OWNED) 184142215Sglebius#define CARP_LOCK(cif) mtx_lock(&(cif)->vhif_mtx) 185142215Sglebius#define CARP_UNLOCK(cif) mtx_unlock(&(cif)->vhif_mtx) 186142215Sglebius 187142914Sglebius#define CARP_SCLOCK(sc) mtx_lock(&SC2CIF(sc)->vhif_mtx) 188142914Sglebius#define CARP_SCUNLOCK(sc) mtx_unlock(&SC2CIF(sc)->vhif_mtx) 189142914Sglebius#define CARP_SCLOCK_ASSERT(sc) mtx_assert(&SC2CIF(sc)->vhif_mtx, MA_OWNED) 190142914Sglebius 191142451Sglebius#define CARP_LOG(...) do { \ 192142446Sglebius if (carp_opts[CARPCTL_LOG] > 0) \ 193142446Sglebius log(LOG_INFO, __VA_ARGS__); \ 194142451Sglebius} while (0) 195142215Sglebius 196142451Sglebius#define CARP_DEBUG(...) do { \ 197142446Sglebius if (carp_opts[CARPCTL_LOG] > 1) \ 198142446Sglebius log(LOG_DEBUG, __VA_ARGS__); \ 199142451Sglebius} while (0) 200142446Sglebius 201142559Sglebiusstatic void carp_hmac_prepare(struct carp_softc *); 202142559Sglebiusstatic void carp_hmac_generate(struct carp_softc *, u_int32_t *, 203142559Sglebius unsigned char *); 204142559Sglebiusstatic int carp_hmac_verify(struct carp_softc *, u_int32_t *, 205142559Sglebius unsigned char *); 206142559Sglebiusstatic void carp_setroute(struct carp_softc *, int); 207142559Sglebiusstatic void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); 208160195Ssamstatic int carp_clone_create(struct if_clone *, int, caddr_t); 209142559Sglebiusstatic void carp_clone_destroy(struct ifnet *); 210166228Sglebiusstatic void carpdetach(struct carp_softc *, int); 211142559Sglebiusstatic int carp_prepare_ad(struct mbuf *, struct carp_softc *, 212142559Sglebius struct carp_header *); 213142559Sglebiusstatic void carp_send_ad_all(void); 214142559Sglebiusstatic void carp_send_ad(void *); 215142914Sglebiusstatic void carp_send_ad_locked(struct carp_softc *); 216221130Sbz#ifdef INET 217142559Sglebiusstatic void carp_send_arp(struct carp_softc *); 218221130Sbz#endif 219142559Sglebiusstatic void carp_master_down(void *); 220142914Sglebiusstatic void carp_master_down_locked(struct carp_softc *); 221142559Sglebiusstatic int carp_ioctl(struct ifnet *, u_long, caddr_t); 222142559Sglebiusstatic int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *, 223191148Skmacy struct route *); 224142559Sglebiusstatic void carp_start(struct ifnet *); 225142559Sglebiusstatic void carp_setrun(struct carp_softc *, sa_family_t); 226142559Sglebiusstatic void carp_set_state(struct carp_softc *, int); 227221130Sbz#ifdef INET 228142559Sglebiusstatic int carp_addrcount(struct carp_if *, struct in_ifaddr *, int); 229221130Sbz#endif 230142215Sglebiusenum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING }; 231142215Sglebius 232221130Sbz#ifdef INET 233215790Sglebiusstatic void carp_multicast_cleanup(struct carp_softc *, int dofree); 234142559Sglebiusstatic int carp_set_addr(struct carp_softc *, struct sockaddr_in *); 235142559Sglebiusstatic int carp_del_addr(struct carp_softc *, struct sockaddr_in *); 236221130Sbz#endif 237142914Sglebiusstatic void carp_carpdev_state_locked(struct carp_if *); 238144329Sglebiusstatic void carp_sc_state_locked(struct carp_softc *); 239142215Sglebius#ifdef INET6 240142559Sglebiusstatic void carp_send_na(struct carp_softc *); 241142559Sglebiusstatic int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); 242142559Sglebiusstatic int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *); 243215790Sglebiusstatic void carp_multicast6_cleanup(struct carp_softc *, int dofree); 244142215Sglebius#endif 245142215Sglebius 246142215Sglebiusstatic LIST_HEAD(, carp_softc) carpif_list; 247142911Sglebiusstatic struct mtx carp_mtx; 248142215SglebiusIFC_SIMPLE_DECLARE(carp, 0); 249142215Sglebius 250156947Sglebiusstatic eventhandler_tag if_detach_event_tag; 251156947Sglebius 252142215Sglebiusstatic __inline u_int16_t 253142215Sglebiuscarp_cksum(struct mbuf *m, int len) 254142215Sglebius{ 255142215Sglebius return (in_cksum(m, len)); 256142215Sglebius} 257142215Sglebius 258142559Sglebiusstatic void 259142215Sglebiuscarp_hmac_prepare(struct carp_softc *sc) 260142215Sglebius{ 261142215Sglebius u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; 262142215Sglebius u_int8_t vhid = sc->sc_vhid & 0xff; 263142215Sglebius struct ifaddr *ifa; 264179490Smlaier int i, found; 265179490Smlaier#ifdef INET 266179490Smlaier struct in_addr last, cur, in; 267179490Smlaier#endif 268142215Sglebius#ifdef INET6 269179490Smlaier struct in6_addr last6, cur6, in6; 270142215Sglebius#endif 271142215Sglebius 272142914Sglebius if (sc->sc_carpdev) 273142914Sglebius CARP_SCLOCK(sc); 274142914Sglebius 275142914Sglebius /* XXX: possible race here */ 276142914Sglebius 277142215Sglebius /* compute ipad from key */ 278142215Sglebius bzero(sc->sc_pad, sizeof(sc->sc_pad)); 279142215Sglebius bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); 280142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 281142215Sglebius sc->sc_pad[i] ^= 0x36; 282142215Sglebius 283142215Sglebius /* precompute first part of inner hash */ 284142215Sglebius SHA1Init(&sc->sc_sha1); 285142215Sglebius SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); 286142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version)); 287142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); 288142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); 289142215Sglebius#ifdef INET 290179490Smlaier cur.s_addr = 0; 291179490Smlaier do { 292179490Smlaier found = 0; 293179490Smlaier last = cur; 294179490Smlaier cur.s_addr = 0xffffffff; 295191528Srwatson IF_ADDR_LOCK(SC2IFP(sc)); 296179490Smlaier TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 297179490Smlaier in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr; 298179490Smlaier if (ifa->ifa_addr->sa_family == AF_INET && 299179490Smlaier ntohl(in.s_addr) > ntohl(last.s_addr) && 300179490Smlaier ntohl(in.s_addr) < ntohl(cur.s_addr)) { 301179490Smlaier cur.s_addr = in.s_addr; 302179490Smlaier found++; 303179490Smlaier } 304179490Smlaier } 305191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 306179490Smlaier if (found) 307179490Smlaier SHA1Update(&sc->sc_sha1, (void *)&cur, sizeof(cur)); 308179490Smlaier } while (found); 309142215Sglebius#endif /* INET */ 310142215Sglebius#ifdef INET6 311179490Smlaier memset(&cur6, 0, sizeof(cur6)); 312179490Smlaier do { 313179490Smlaier found = 0; 314179490Smlaier last6 = cur6; 315179490Smlaier memset(&cur6, 0xff, sizeof(cur6)); 316191528Srwatson IF_ADDR_LOCK(SC2IFP(sc)); 317179490Smlaier TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 318142215Sglebius in6 = ifatoia6(ifa)->ia_addr.sin6_addr; 319179490Smlaier if (IN6_IS_SCOPE_EMBED(&in6)) 320179490Smlaier in6.s6_addr16[1] = 0; 321179490Smlaier if (ifa->ifa_addr->sa_family == AF_INET6 && 322179490Smlaier memcmp(&in6, &last6, sizeof(in6)) > 0 && 323179490Smlaier memcmp(&in6, &cur6, sizeof(in6)) < 0) { 324179490Smlaier cur6 = in6; 325179490Smlaier found++; 326179490Smlaier } 327142215Sglebius } 328191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 329179490Smlaier if (found) 330179490Smlaier SHA1Update(&sc->sc_sha1, (void *)&cur6, sizeof(cur6)); 331179490Smlaier } while (found); 332142215Sglebius#endif /* INET6 */ 333142215Sglebius 334142215Sglebius /* convert ipad to opad */ 335142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 336142215Sglebius sc->sc_pad[i] ^= 0x36 ^ 0x5c; 337142914Sglebius 338142914Sglebius if (sc->sc_carpdev) 339142914Sglebius CARP_SCUNLOCK(sc); 340142215Sglebius} 341142215Sglebius 342142559Sglebiusstatic void 343142215Sglebiuscarp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], 344142215Sglebius unsigned char md[20]) 345142215Sglebius{ 346142215Sglebius SHA1_CTX sha1ctx; 347142215Sglebius 348142215Sglebius /* fetch first half of inner hash */ 349142215Sglebius bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); 350142215Sglebius 351142215Sglebius SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter)); 352142215Sglebius SHA1Final(md, &sha1ctx); 353142215Sglebius 354142215Sglebius /* outer hash */ 355142215Sglebius SHA1Init(&sha1ctx); 356142215Sglebius SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); 357142215Sglebius SHA1Update(&sha1ctx, md, 20); 358142215Sglebius SHA1Final(md, &sha1ctx); 359142215Sglebius} 360142215Sglebius 361142559Sglebiusstatic int 362142215Sglebiuscarp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], 363142215Sglebius unsigned char md[20]) 364142215Sglebius{ 365142215Sglebius unsigned char md2[20]; 366142215Sglebius 367142914Sglebius CARP_SCLOCK_ASSERT(sc); 368142914Sglebius 369142215Sglebius carp_hmac_generate(sc, counter, md2); 370142215Sglebius 371142215Sglebius return (bcmp(md, md2, sizeof(md2))); 372142215Sglebius} 373142215Sglebius 374142559Sglebiusstatic void 375142215Sglebiuscarp_setroute(struct carp_softc *sc, int cmd) 376142215Sglebius{ 377142215Sglebius struct ifaddr *ifa; 378142215Sglebius int s; 379142215Sglebius 380142914Sglebius if (sc->sc_carpdev) 381142914Sglebius CARP_SCLOCK_ASSERT(sc); 382142914Sglebius 383142215Sglebius s = splnet(); 384147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 385221130Sbz#ifdef INET 386142914Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 387142914Sglebius sc->sc_carpdev != NULL) { 388142215Sglebius int count = carp_addrcount( 389142564Sglebius (struct carp_if *)sc->sc_carpdev->if_carp, 390142215Sglebius ifatoia(ifa), CARP_COUNT_MASTER); 391142215Sglebius 392142215Sglebius if ((cmd == RTM_ADD && count == 1) || 393142215Sglebius (cmd == RTM_DELETE && count == 0)) 394142215Sglebius rtinit(ifa, cmd, RTF_UP | RTF_HOST); 395142215Sglebius } 396221130Sbz#endif 397142215Sglebius } 398142215Sglebius splx(s); 399142215Sglebius} 400142215Sglebius 401142559Sglebiusstatic int 402160195Ssamcarp_clone_create(struct if_clone *ifc, int unit, caddr_t params) 403142215Sglebius{ 404142215Sglebius 405142215Sglebius struct carp_softc *sc; 406142215Sglebius struct ifnet *ifp; 407142215Sglebius 408184205Sdes sc = malloc(sizeof(*sc), M_CARP, M_WAITOK|M_ZERO); 409147256Sbrooks ifp = SC2IFP(sc) = if_alloc(IFT_ETHER); 410147256Sbrooks if (ifp == NULL) { 411184205Sdes free(sc, M_CARP); 412147256Sbrooks return (ENOSPC); 413147256Sbrooks } 414142215Sglebius 415142215Sglebius sc->sc_flags_backup = 0; 416142215Sglebius sc->sc_suppress = 0; 417142215Sglebius sc->sc_advbase = CARP_DFLTINTV; 418142215Sglebius sc->sc_vhid = -1; /* required setting */ 419142215Sglebius sc->sc_advskew = 0; 420142215Sglebius sc->sc_init_counter = 1; 421142215Sglebius sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */ 422221130Sbz#ifdef INET 423160164Smlaier sc->sc_imo.imo_membership = (struct in_multi **)malloc( 424160164Smlaier (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP, 425160164Smlaier M_WAITOK); 426170613Sbms sc->sc_imo.imo_mfilters = NULL; 427160164Smlaier sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; 428162627Sbms sc->sc_imo.imo_multicast_vif = -1; 429221130Sbz#endif 430191672Sbms#ifdef INET6 431191672Sbms sc->sc_im6o.im6o_membership = (struct in6_multi **)malloc( 432191672Sbms (sizeof(struct in6_multi *) * IPV6_MIN_MEMBERSHIPS), M_CARP, 433191672Sbms M_WAITOK); 434191672Sbms sc->sc_im6o.im6o_mfilters = NULL; 435191672Sbms sc->sc_im6o.im6o_max_memberships = IPV6_MIN_MEMBERSHIPS; 436191672Sbms sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; 437191672Sbms#endif 438142215Sglebius 439171637Srwatson callout_init(&sc->sc_ad_tmo, CALLOUT_MPSAFE); 440171637Srwatson callout_init(&sc->sc_md_tmo, CALLOUT_MPSAFE); 441171637Srwatson callout_init(&sc->sc_md6_tmo, CALLOUT_MPSAFE); 442142215Sglebius 443142215Sglebius ifp->if_softc = sc; 444142215Sglebius if_initname(ifp, CARP_IFNAME, unit); 445142215Sglebius ifp->if_mtu = ETHERMTU; 446151688Syar ifp->if_flags = IFF_LOOPBACK; 447142215Sglebius ifp->if_ioctl = carp_ioctl; 448142215Sglebius ifp->if_output = carp_looutput; 449142215Sglebius ifp->if_start = carp_start; 450142215Sglebius ifp->if_type = IFT_CARP; 451142215Sglebius ifp->if_snd.ifq_maxlen = ifqmaxlen; 452142215Sglebius ifp->if_hdrlen = 0; 453142215Sglebius if_attach(ifp); 454147256Sbrooks bpfattach(SC2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 455142911Sglebius mtx_lock(&carp_mtx); 456142215Sglebius LIST_INSERT_HEAD(&carpif_list, sc, sc_next); 457142911Sglebius mtx_unlock(&carp_mtx); 458142215Sglebius return (0); 459142215Sglebius} 460142215Sglebius 461142559Sglebiusstatic void 462142215Sglebiuscarp_clone_destroy(struct ifnet *ifp) 463142215Sglebius{ 464142215Sglebius struct carp_softc *sc = ifp->if_softc; 465156947Sglebius 466156947Sglebius if (sc->sc_carpdev) 467156947Sglebius CARP_SCLOCK(sc); 468166228Sglebius carpdetach(sc, 1); /* Returns unlocked. */ 469156947Sglebius 470156947Sglebius mtx_lock(&carp_mtx); 471156947Sglebius LIST_REMOVE(sc, sc_next); 472156947Sglebius mtx_unlock(&carp_mtx); 473156947Sglebius bpfdetach(ifp); 474156947Sglebius if_detach(ifp); 475156947Sglebius if_free_type(ifp, IFT_ETHER); 476221130Sbz#ifdef INET 477160164Smlaier free(sc->sc_imo.imo_membership, M_CARP); 478221130Sbz#endif 479191672Sbms#ifdef INET6 480191672Sbms free(sc->sc_im6o.im6o_membership, M_CARP); 481191672Sbms#endif 482156947Sglebius free(sc, M_CARP); 483156947Sglebius} 484156947Sglebius 485166423Sglebius/* 486166423Sglebius * This function can be called on CARP interface destroy path, 487166423Sglebius * and in case of the removal of the underlying interface as 488215790Sglebius * well. We differentiate these two cases: in case of destruction 489215790Sglebius * of the underlying interface, we do not cleanup our multicast 490215790Sglebius * memberships, since they are already freed. But we purge pointers 491215790Sglebius * to multicast structures, since they are no longer valid, to 492215790Sglebius * avoid panic in future calls to carpdetach(). Also, we do not 493166423Sglebius * release the lock on return, because the function will be 494166423Sglebius * called once more, for another CARP instance on the same 495166423Sglebius * interface. 496166423Sglebius */ 497156947Sglebiusstatic void 498166228Sglebiuscarpdetach(struct carp_softc *sc, int unlock) 499156947Sglebius{ 500142215Sglebius struct carp_if *cif; 501146226Sglebius 502156947Sglebius callout_stop(&sc->sc_ad_tmo); 503156947Sglebius callout_stop(&sc->sc_md_tmo); 504156947Sglebius callout_stop(&sc->sc_md6_tmo); 505156947Sglebius 506146226Sglebius if (sc->sc_suppress) 507146226Sglebius carp_suppress_preempt--; 508146226Sglebius sc->sc_suppress = 0; 509146226Sglebius 510156947Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) 511156947Sglebius carp_suppress_preempt--; 512156947Sglebius sc->sc_sendad_errors = 0; 513142215Sglebius 514156947Sglebius carp_set_state(sc, INIT); 515156947Sglebius SC2IFP(sc)->if_flags &= ~IFF_UP; 516156947Sglebius carp_setrun(sc, 0); 517221130Sbz#ifdef INET 518215790Sglebius carp_multicast_cleanup(sc, unlock); 519221130Sbz#endif 520166423Sglebius#ifdef INET6 521215790Sglebius carp_multicast6_cleanup(sc, unlock); 522166423Sglebius#endif 523142215Sglebius 524156947Sglebius if (sc->sc_carpdev != NULL) { 525156947Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 526156947Sglebius CARP_LOCK_ASSERT(cif); 527142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 528142215Sglebius if (!--cif->vhif_nvrs) { 529156947Sglebius ifpromisc(sc->sc_carpdev, 0); 530142564Sglebius sc->sc_carpdev->if_carp = NULL; 531142215Sglebius CARP_LOCK_DESTROY(cif); 532196397Swill free(cif, M_CARP); 533166228Sglebius } else if (unlock) 534166228Sglebius CARP_UNLOCK(cif); 535166228Sglebius sc->sc_carpdev = NULL; 536142215Sglebius } 537156947Sglebius} 538142215Sglebius 539156947Sglebius/* Detach an interface from the carp. */ 540156947Sglebiusstatic void 541156947Sglebiuscarp_ifdetach(void *arg __unused, struct ifnet *ifp) 542156947Sglebius{ 543156947Sglebius struct carp_if *cif = (struct carp_if *)ifp->if_carp; 544156947Sglebius struct carp_softc *sc, *nextsc; 545166226Sglebius 546156947Sglebius if (cif == NULL) 547156947Sglebius return; 548156947Sglebius 549156947Sglebius /* 550156947Sglebius * XXX: At the end of for() cycle the lock will be destroyed. 551156947Sglebius */ 552156947Sglebius CARP_LOCK(cif); 553156947Sglebius for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) { 554156947Sglebius nextsc = TAILQ_NEXT(sc, sc_list); 555166228Sglebius carpdetach(sc, 0); 556156947Sglebius } 557142215Sglebius} 558142215Sglebius 559142215Sglebius/* 560142215Sglebius * process input packet. 561142215Sglebius * we have rearranged checks order compared to the rfc, 562142215Sglebius * but it seems more efficient this way or not possible otherwise. 563142215Sglebius */ 564221130Sbz#ifdef INET 565142215Sglebiusvoid 566142215Sglebiuscarp_input(struct mbuf *m, int hlen) 567142215Sglebius{ 568142215Sglebius struct ip *ip = mtod(m, struct ip *); 569142215Sglebius struct carp_header *ch; 570142215Sglebius int iplen, len; 571142215Sglebius 572190968Srwatson CARPSTATS_INC(carps_ipackets); 573142215Sglebius 574142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 575142215Sglebius m_freem(m); 576142215Sglebius return; 577142215Sglebius } 578142215Sglebius 579142215Sglebius /* check if received on a valid carp interface */ 580142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 581190968Srwatson CARPSTATS_INC(carps_badif); 582200026Sglebius CARP_DEBUG("carp_input: packet received on non-carp " 583142452Sglebius "interface: %s\n", 584142446Sglebius m->m_pkthdr.rcvif->if_xname); 585142215Sglebius m_freem(m); 586142215Sglebius return; 587142215Sglebius } 588142215Sglebius 589142215Sglebius /* verify that the IP TTL is 255. */ 590142215Sglebius if (ip->ip_ttl != CARP_DFLTTL) { 591190968Srwatson CARPSTATS_INC(carps_badttl); 592201801Sru CARP_DEBUG("carp_input: received ttl %d != 255 on %s\n", 593142446Sglebius ip->ip_ttl, 594142446Sglebius m->m_pkthdr.rcvif->if_xname); 595142215Sglebius m_freem(m); 596142215Sglebius return; 597142215Sglebius } 598142215Sglebius 599142215Sglebius iplen = ip->ip_hl << 2; 600142215Sglebius 601142215Sglebius if (m->m_pkthdr.len < iplen + sizeof(*ch)) { 602190968Srwatson CARPSTATS_INC(carps_badlen); 603200026Sglebius CARP_DEBUG("carp_input: received len %zd < " 604195976Sdelphij "sizeof(struct carp_header) on %s\n", 605195976Sdelphij m->m_len - sizeof(struct ip), 606195976Sdelphij m->m_pkthdr.rcvif->if_xname); 607142215Sglebius m_freem(m); 608142215Sglebius return; 609142215Sglebius } 610142215Sglebius 611142215Sglebius if (iplen + sizeof(*ch) < m->m_len) { 612142215Sglebius if ((m = m_pullup(m, iplen + sizeof(*ch))) == NULL) { 613190968Srwatson CARPSTATS_INC(carps_hdrops); 614200026Sglebius CARP_DEBUG("carp_input: pullup failed\n"); 615142215Sglebius return; 616142215Sglebius } 617142215Sglebius ip = mtod(m, struct ip *); 618142215Sglebius } 619142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 620142215Sglebius 621142215Sglebius /* 622142215Sglebius * verify that the received packet length is 623142215Sglebius * equal to the CARP header 624142215Sglebius */ 625142215Sglebius len = iplen + sizeof(*ch); 626142215Sglebius if (len > m->m_pkthdr.len) { 627190968Srwatson CARPSTATS_INC(carps_badlen); 628200026Sglebius CARP_DEBUG("carp_input: packet too short %d on %s\n", 629142446Sglebius m->m_pkthdr.len, 630142446Sglebius m->m_pkthdr.rcvif->if_xname); 631142215Sglebius m_freem(m); 632142215Sglebius return; 633142215Sglebius } 634142215Sglebius 635142215Sglebius if ((m = m_pullup(m, len)) == NULL) { 636190968Srwatson CARPSTATS_INC(carps_hdrops); 637142215Sglebius return; 638142215Sglebius } 639142215Sglebius ip = mtod(m, struct ip *); 640142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 641142215Sglebius 642142215Sglebius /* verify the CARP checksum */ 643142215Sglebius m->m_data += iplen; 644142215Sglebius if (carp_cksum(m, len - iplen)) { 645190968Srwatson CARPSTATS_INC(carps_badsum); 646200026Sglebius CARP_DEBUG("carp_input: checksum failed on %s\n", 647142446Sglebius m->m_pkthdr.rcvif->if_xname); 648142215Sglebius m_freem(m); 649142215Sglebius return; 650142215Sglebius } 651142215Sglebius m->m_data -= iplen; 652142215Sglebius 653142446Sglebius carp_input_c(m, ch, AF_INET); 654142215Sglebius} 655221130Sbz#endif 656142215Sglebius 657142215Sglebius#ifdef INET6 658142215Sglebiusint 659142215Sglebiuscarp6_input(struct mbuf **mp, int *offp, int proto) 660142215Sglebius{ 661142215Sglebius struct mbuf *m = *mp; 662142215Sglebius struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 663142215Sglebius struct carp_header *ch; 664142215Sglebius u_int len; 665142215Sglebius 666190968Srwatson CARPSTATS_INC(carps_ipackets6); 667142215Sglebius 668142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 669142215Sglebius m_freem(m); 670142215Sglebius return (IPPROTO_DONE); 671142215Sglebius } 672142215Sglebius 673142215Sglebius /* check if received on a valid carp interface */ 674142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 675190968Srwatson CARPSTATS_INC(carps_badif); 676200026Sglebius CARP_DEBUG("carp6_input: packet received on non-carp " 677142452Sglebius "interface: %s\n", 678142446Sglebius m->m_pkthdr.rcvif->if_xname); 679142215Sglebius m_freem(m); 680142215Sglebius return (IPPROTO_DONE); 681142215Sglebius } 682142215Sglebius 683142215Sglebius /* verify that the IP TTL is 255 */ 684142215Sglebius if (ip6->ip6_hlim != CARP_DFLTTL) { 685190968Srwatson CARPSTATS_INC(carps_badttl); 686200026Sglebius CARP_DEBUG("carp6_input: received ttl %d != 255 on %s\n", 687142446Sglebius ip6->ip6_hlim, 688142446Sglebius m->m_pkthdr.rcvif->if_xname); 689142215Sglebius m_freem(m); 690142215Sglebius return (IPPROTO_DONE); 691142215Sglebius } 692142215Sglebius 693142215Sglebius /* verify that we have a complete carp packet */ 694142215Sglebius len = m->m_len; 695142215Sglebius IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch)); 696142215Sglebius if (ch == NULL) { 697190968Srwatson CARPSTATS_INC(carps_badlen); 698200026Sglebius CARP_DEBUG("carp6_input: packet size %u too small\n", len); 699142215Sglebius return (IPPROTO_DONE); 700142215Sglebius } 701142215Sglebius 702142215Sglebius 703142215Sglebius /* verify the CARP checksum */ 704142215Sglebius m->m_data += *offp; 705142215Sglebius if (carp_cksum(m, sizeof(*ch))) { 706190968Srwatson CARPSTATS_INC(carps_badsum); 707200026Sglebius CARP_DEBUG("carp6_input: checksum failed, on %s\n", 708142446Sglebius m->m_pkthdr.rcvif->if_xname); 709142215Sglebius m_freem(m); 710142215Sglebius return (IPPROTO_DONE); 711142215Sglebius } 712142215Sglebius m->m_data -= *offp; 713142215Sglebius 714142446Sglebius carp_input_c(m, ch, AF_INET6); 715142215Sglebius return (IPPROTO_DONE); 716142215Sglebius} 717142215Sglebius#endif /* INET6 */ 718142215Sglebius 719142559Sglebiusstatic void 720142446Sglebiuscarp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) 721142215Sglebius{ 722142215Sglebius struct ifnet *ifp = m->m_pkthdr.rcvif; 723142446Sglebius struct carp_softc *sc; 724142215Sglebius u_int64_t tmp_counter; 725142215Sglebius struct timeval sc_tv, ch_tv; 726142215Sglebius 727142215Sglebius /* verify that the VHID is valid on the receiving interface */ 728142215Sglebius CARP_LOCK(ifp->if_carp); 729142215Sglebius TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list) 730142215Sglebius if (sc->sc_vhid == ch->carp_vhid) 731142215Sglebius break; 732142914Sglebius 733148887Srwatson if (!sc || !((SC2IFP(sc)->if_flags & IFF_UP) && 734148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 735190968Srwatson CARPSTATS_INC(carps_badvhid); 736142914Sglebius CARP_UNLOCK(ifp->if_carp); 737142215Sglebius m_freem(m); 738142215Sglebius return; 739142215Sglebius } 740142215Sglebius 741147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 742147256Sbrooks SC2IFP(sc)->if_ipackets++; 743147256Sbrooks SC2IFP(sc)->if_ibytes += m->m_pkthdr.len; 744142215Sglebius 745159180Scsjp if (bpf_peers_present(SC2IFP(sc)->if_bpf)) { 746221130Sbz uint32_t af1 = af; 747221130Sbz#ifdef INET 748142215Sglebius struct ip *ip = mtod(m, struct ip *); 749142215Sglebius 750142215Sglebius /* BPF wants net byte order */ 751221130Sbz if (af == AF_INET) { 752221130Sbz ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2)); 753221130Sbz ip->ip_off = htons(ip->ip_off); 754221130Sbz } 755221130Sbz#endif 756147256Sbrooks bpf_mtap2(SC2IFP(sc)->if_bpf, &af1, sizeof(af1), m); 757142215Sglebius } 758142215Sglebius 759142215Sglebius /* verify the CARP version. */ 760142215Sglebius if (ch->carp_version != CARP_VERSION) { 761190968Srwatson CARPSTATS_INC(carps_badver); 762147256Sbrooks SC2IFP(sc)->if_ierrors++; 763142914Sglebius CARP_UNLOCK(ifp->if_carp); 764200026Sglebius CARP_DEBUG("%s; invalid version %d\n", 765147256Sbrooks SC2IFP(sc)->if_xname, 766142446Sglebius ch->carp_version); 767142215Sglebius m_freem(m); 768142215Sglebius return; 769142215Sglebius } 770142215Sglebius 771142215Sglebius /* verify the hash */ 772142215Sglebius if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { 773190968Srwatson CARPSTATS_INC(carps_badauth); 774147256Sbrooks SC2IFP(sc)->if_ierrors++; 775142914Sglebius CARP_UNLOCK(ifp->if_carp); 776201801Sru CARP_DEBUG("%s: incorrect hash\n", SC2IFP(sc)->if_xname); 777142215Sglebius m_freem(m); 778142215Sglebius return; 779142215Sglebius } 780142215Sglebius 781142215Sglebius tmp_counter = ntohl(ch->carp_counter[0]); 782142215Sglebius tmp_counter = tmp_counter<<32; 783142215Sglebius tmp_counter += ntohl(ch->carp_counter[1]); 784142215Sglebius 785142215Sglebius /* XXX Replay protection goes here */ 786142215Sglebius 787142215Sglebius sc->sc_init_counter = 0; 788142215Sglebius sc->sc_counter = tmp_counter; 789142215Sglebius 790142215Sglebius sc_tv.tv_sec = sc->sc_advbase; 791142215Sglebius if (carp_suppress_preempt && sc->sc_advskew < 240) 792142215Sglebius sc_tv.tv_usec = 240 * 1000000 / 256; 793142215Sglebius else 794142215Sglebius sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; 795142215Sglebius ch_tv.tv_sec = ch->carp_advbase; 796142215Sglebius ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; 797142215Sglebius 798142215Sglebius switch (sc->sc_state) { 799142215Sglebius case INIT: 800142215Sglebius break; 801142215Sglebius case MASTER: 802142215Sglebius /* 803142215Sglebius * If we receive an advertisement from a master who's going to 804142215Sglebius * be more frequent than us, go into BACKUP state. 805142215Sglebius */ 806142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, >) || 807142215Sglebius timevalcmp(&sc_tv, &ch_tv, ==)) { 808142215Sglebius callout_stop(&sc->sc_ad_tmo); 809200026Sglebius CARP_LOG("%s: MASTER -> BACKUP " 810142452Sglebius "(more frequent advertisement received)\n", 811147256Sbrooks SC2IFP(sc)->if_xname); 812142215Sglebius carp_set_state(sc, BACKUP); 813142215Sglebius carp_setrun(sc, 0); 814142215Sglebius carp_setroute(sc, RTM_DELETE); 815142215Sglebius } 816142215Sglebius break; 817142215Sglebius case BACKUP: 818142215Sglebius /* 819142215Sglebius * If we're pre-empting masters who advertise slower than us, 820142215Sglebius * and this one claims to be slower, treat him as down. 821142215Sglebius */ 822142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && 823142215Sglebius timevalcmp(&sc_tv, &ch_tv, <)) { 824200026Sglebius CARP_LOG("%s: BACKUP -> MASTER " 825142452Sglebius "(preempting a slower master)\n", 826147256Sbrooks SC2IFP(sc)->if_xname); 827142914Sglebius carp_master_down_locked(sc); 828142215Sglebius break; 829142215Sglebius } 830142215Sglebius 831142215Sglebius /* 832142215Sglebius * If the master is going to advertise at such a low frequency 833142215Sglebius * that he's guaranteed to time out, we'd might as well just 834142215Sglebius * treat him as timed out now. 835142215Sglebius */ 836142215Sglebius sc_tv.tv_sec = sc->sc_advbase * 3; 837142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, <)) { 838200026Sglebius CARP_LOG("%s: BACKUP -> MASTER " 839142452Sglebius "(master timed out)\n", 840147256Sbrooks SC2IFP(sc)->if_xname); 841142914Sglebius carp_master_down_locked(sc); 842142215Sglebius break; 843142215Sglebius } 844142215Sglebius 845142215Sglebius /* 846142215Sglebius * Otherwise, we reset the counter and wait for the next 847142215Sglebius * advertisement. 848142215Sglebius */ 849142215Sglebius carp_setrun(sc, af); 850142215Sglebius break; 851142215Sglebius } 852142215Sglebius 853142914Sglebius CARP_UNLOCK(ifp->if_carp); 854142914Sglebius 855142215Sglebius m_freem(m); 856142215Sglebius return; 857142215Sglebius} 858142215Sglebius 859142559Sglebiusstatic int 860142215Sglebiuscarp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) 861142215Sglebius{ 862142215Sglebius struct m_tag *mtag; 863147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 864142215Sglebius 865142215Sglebius if (sc->sc_init_counter) { 866142215Sglebius /* this could also be seconds since unix epoch */ 867142215Sglebius sc->sc_counter = arc4random(); 868142215Sglebius sc->sc_counter = sc->sc_counter << 32; 869142215Sglebius sc->sc_counter += arc4random(); 870142215Sglebius } else 871142215Sglebius sc->sc_counter++; 872142215Sglebius 873142215Sglebius ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff); 874142215Sglebius ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff); 875142215Sglebius 876142215Sglebius carp_hmac_generate(sc, ch->carp_counter, ch->carp_md); 877142215Sglebius 878142215Sglebius /* Tag packet for carp_output */ 879142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT); 880142215Sglebius if (mtag == NULL) { 881142215Sglebius m_freem(m); 882147256Sbrooks SC2IFP(sc)->if_oerrors++; 883142215Sglebius return (ENOMEM); 884142215Sglebius } 885142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 886142215Sglebius m_tag_prepend(m, mtag); 887142215Sglebius 888142215Sglebius return (0); 889142215Sglebius} 890142215Sglebius 891142559Sglebiusstatic void 892142215Sglebiuscarp_send_ad_all(void) 893142215Sglebius{ 894142911Sglebius struct carp_softc *sc; 895142215Sglebius 896142911Sglebius mtx_lock(&carp_mtx); 897142911Sglebius LIST_FOREACH(sc, &carpif_list, sc_next) { 898142911Sglebius if (sc->sc_carpdev == NULL) 899142215Sglebius continue; 900142911Sglebius CARP_SCLOCK(sc); 901148887Srwatson if ((SC2IFP(sc)->if_flags & IFF_UP) && 902148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING) && 903142911Sglebius sc->sc_state == MASTER) 904142914Sglebius carp_send_ad_locked(sc); 905142911Sglebius CARP_SCUNLOCK(sc); 906142215Sglebius } 907142911Sglebius mtx_unlock(&carp_mtx); 908142215Sglebius} 909142215Sglebius 910142559Sglebiusstatic void 911142215Sglebiuscarp_send_ad(void *v) 912142215Sglebius{ 913142914Sglebius struct carp_softc *sc = v; 914142914Sglebius 915142914Sglebius CARP_SCLOCK(sc); 916142914Sglebius carp_send_ad_locked(sc); 917142914Sglebius CARP_SCUNLOCK(sc); 918142914Sglebius} 919142914Sglebius 920142914Sglebiusstatic void 921142914Sglebiuscarp_send_ad_locked(struct carp_softc *sc) 922142914Sglebius{ 923142215Sglebius struct carp_header ch; 924142215Sglebius struct timeval tv; 925142215Sglebius struct carp_header *ch_ptr; 926142215Sglebius struct mbuf *m; 927142215Sglebius int len, advbase, advskew; 928142215Sglebius 929142914Sglebius CARP_SCLOCK_ASSERT(sc); 930142914Sglebius 931142215Sglebius /* bow out if we've lost our UPness or RUNNINGuiness */ 932148887Srwatson if (!((SC2IFP(sc)->if_flags & IFF_UP) && 933148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) { 934142215Sglebius advbase = 255; 935142215Sglebius advskew = 255; 936142215Sglebius } else { 937142215Sglebius advbase = sc->sc_advbase; 938142215Sglebius if (!carp_suppress_preempt || sc->sc_advskew > 240) 939142215Sglebius advskew = sc->sc_advskew; 940142215Sglebius else 941142215Sglebius advskew = 240; 942142215Sglebius tv.tv_sec = advbase; 943142215Sglebius tv.tv_usec = advskew * 1000000 / 256; 944142215Sglebius } 945142215Sglebius 946142215Sglebius ch.carp_version = CARP_VERSION; 947142215Sglebius ch.carp_type = CARP_ADVERTISEMENT; 948142215Sglebius ch.carp_vhid = sc->sc_vhid; 949142215Sglebius ch.carp_advbase = advbase; 950142215Sglebius ch.carp_advskew = advskew; 951142215Sglebius ch.carp_authlen = 7; /* XXX DEFINE */ 952142215Sglebius ch.carp_pad1 = 0; /* must be zero */ 953142215Sglebius ch.carp_cksum = 0; 954142215Sglebius 955142215Sglebius#ifdef INET 956142215Sglebius if (sc->sc_ia) { 957142215Sglebius struct ip *ip; 958142215Sglebius 959142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 960142215Sglebius if (m == NULL) { 961147256Sbrooks SC2IFP(sc)->if_oerrors++; 962190968Srwatson CARPSTATS_INC(carps_onomem); 963142215Sglebius /* XXX maybe less ? */ 964142215Sglebius if (advbase != 255 || advskew != 255) 965142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 966142215Sglebius carp_send_ad, sc); 967142215Sglebius return; 968142215Sglebius } 969142215Sglebius len = sizeof(*ip) + sizeof(ch); 970142215Sglebius m->m_pkthdr.len = len; 971142215Sglebius m->m_pkthdr.rcvif = NULL; 972142215Sglebius m->m_len = len; 973142215Sglebius MH_ALIGN(m, m->m_len); 974142215Sglebius m->m_flags |= M_MCAST; 975142215Sglebius ip = mtod(m, struct ip *); 976142215Sglebius ip->ip_v = IPVERSION; 977142215Sglebius ip->ip_hl = sizeof(*ip) >> 2; 978142215Sglebius ip->ip_tos = IPTOS_LOWDELAY; 979142215Sglebius ip->ip_len = len; 980142215Sglebius ip->ip_id = ip_newid(); 981142215Sglebius ip->ip_off = IP_DF; 982142215Sglebius ip->ip_ttl = CARP_DFLTTL; 983142215Sglebius ip->ip_p = IPPROTO_CARP; 984142215Sglebius ip->ip_sum = 0; 985142215Sglebius ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr; 986142215Sglebius ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP); 987142215Sglebius 988142215Sglebius ch_ptr = (struct carp_header *)(&ip[1]); 989142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 990142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 991142215Sglebius return; 992142215Sglebius 993142215Sglebius m->m_data += sizeof(*ip); 994142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip)); 995142215Sglebius m->m_data -= sizeof(*ip); 996142215Sglebius 997147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 998147256Sbrooks SC2IFP(sc)->if_opackets++; 999147256Sbrooks SC2IFP(sc)->if_obytes += len; 1000190968Srwatson CARPSTATS_INC(carps_opackets); 1001142215Sglebius 1002142215Sglebius if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) { 1003147256Sbrooks SC2IFP(sc)->if_oerrors++; 1004142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 1005142215Sglebius sc->sc_sendad_errors++; 1006142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 1007142215Sglebius carp_suppress_preempt++; 1008142914Sglebius if (carp_suppress_preempt == 1) { 1009142914Sglebius CARP_SCUNLOCK(sc); 1010142215Sglebius carp_send_ad_all(); 1011142914Sglebius CARP_SCLOCK(sc); 1012142914Sglebius } 1013142215Sglebius } 1014142215Sglebius sc->sc_sendad_success = 0; 1015142215Sglebius } else { 1016142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 1017142215Sglebius if (++sc->sc_sendad_success >= 1018142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 1019142215Sglebius carp_suppress_preempt--; 1020142215Sglebius sc->sc_sendad_errors = 0; 1021142215Sglebius } 1022142215Sglebius } else 1023142215Sglebius sc->sc_sendad_errors = 0; 1024142215Sglebius } 1025142215Sglebius } 1026142215Sglebius#endif /* INET */ 1027142215Sglebius#ifdef INET6 1028142215Sglebius if (sc->sc_ia6) { 1029142215Sglebius struct ip6_hdr *ip6; 1030142215Sglebius 1031142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 1032142215Sglebius if (m == NULL) { 1033147256Sbrooks SC2IFP(sc)->if_oerrors++; 1034190968Srwatson CARPSTATS_INC(carps_onomem); 1035142215Sglebius /* XXX maybe less ? */ 1036142215Sglebius if (advbase != 255 || advskew != 255) 1037142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1038142215Sglebius carp_send_ad, sc); 1039142215Sglebius return; 1040142215Sglebius } 1041142215Sglebius len = sizeof(*ip6) + sizeof(ch); 1042142215Sglebius m->m_pkthdr.len = len; 1043142215Sglebius m->m_pkthdr.rcvif = NULL; 1044142215Sglebius m->m_len = len; 1045142215Sglebius MH_ALIGN(m, m->m_len); 1046142215Sglebius m->m_flags |= M_MCAST; 1047142215Sglebius ip6 = mtod(m, struct ip6_hdr *); 1048142215Sglebius bzero(ip6, sizeof(*ip6)); 1049142215Sglebius ip6->ip6_vfc |= IPV6_VERSION; 1050142215Sglebius ip6->ip6_hlim = CARP_DFLTTL; 1051142215Sglebius ip6->ip6_nxt = IPPROTO_CARP; 1052142215Sglebius bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src, 1053142215Sglebius sizeof(struct in6_addr)); 1054142215Sglebius /* set the multicast destination */ 1055142215Sglebius 1056163069Sbz ip6->ip6_dst.s6_addr16[0] = htons(0xff02); 1057142215Sglebius ip6->ip6_dst.s6_addr8[15] = 0x12; 1058163069Sbz if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) { 1059163069Sbz SC2IFP(sc)->if_oerrors++; 1060163069Sbz m_freem(m); 1061200026Sglebius CARP_DEBUG("%s: in6_setscope failed\n", __func__); 1062163069Sbz return; 1063163069Sbz } 1064142215Sglebius 1065142215Sglebius ch_ptr = (struct carp_header *)(&ip6[1]); 1066142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 1067142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 1068142215Sglebius return; 1069142215Sglebius 1070142215Sglebius m->m_data += sizeof(*ip6); 1071142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6)); 1072142215Sglebius m->m_data -= sizeof(*ip6); 1073142215Sglebius 1074147256Sbrooks getmicrotime(&SC2IFP(sc)->if_lastchange); 1075147256Sbrooks SC2IFP(sc)->if_opackets++; 1076147256Sbrooks SC2IFP(sc)->if_obytes += len; 1077190968Srwatson CARPSTATS_INC(carps_opackets6); 1078142215Sglebius 1079142215Sglebius if (ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL)) { 1080147256Sbrooks SC2IFP(sc)->if_oerrors++; 1081142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 1082142215Sglebius sc->sc_sendad_errors++; 1083142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 1084142215Sglebius carp_suppress_preempt++; 1085142914Sglebius if (carp_suppress_preempt == 1) { 1086142914Sglebius CARP_SCUNLOCK(sc); 1087142215Sglebius carp_send_ad_all(); 1088142914Sglebius CARP_SCLOCK(sc); 1089142914Sglebius } 1090142215Sglebius } 1091142215Sglebius sc->sc_sendad_success = 0; 1092142215Sglebius } else { 1093142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 1094142215Sglebius if (++sc->sc_sendad_success >= 1095142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 1096142215Sglebius carp_suppress_preempt--; 1097142215Sglebius sc->sc_sendad_errors = 0; 1098142215Sglebius } 1099142215Sglebius } else 1100142215Sglebius sc->sc_sendad_errors = 0; 1101142215Sglebius } 1102142215Sglebius } 1103142215Sglebius#endif /* INET6 */ 1104142215Sglebius 1105142215Sglebius if (advbase != 255 || advskew != 255) 1106142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1107142215Sglebius carp_send_ad, sc); 1108142215Sglebius 1109142215Sglebius} 1110142215Sglebius 1111221130Sbz#ifdef INET 1112142215Sglebius/* 1113142215Sglebius * Broadcast a gratuitous ARP request containing 1114142215Sglebius * the virtual router MAC address for each IP address 1115142215Sglebius * associated with the virtual router. 1116142215Sglebius */ 1117142559Sglebiusstatic void 1118142215Sglebiuscarp_send_arp(struct carp_softc *sc) 1119142215Sglebius{ 1120142215Sglebius struct ifaddr *ifa; 1121142215Sglebius 1122147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1123142215Sglebius 1124142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET) 1125142215Sglebius continue; 1126142215Sglebius 1127152315Sru/* arprequest(sc->sc_carpdev, &in, &in, IF_LLADDR(sc->sc_ifp)); */ 1128152315Sru arp_ifinit2(sc->sc_carpdev, ifa, IF_LLADDR(sc->sc_ifp)); 1129142215Sglebius 1130142215Sglebius DELAY(1000); /* XXX */ 1131142215Sglebius } 1132142215Sglebius} 1133221130Sbz#endif 1134142215Sglebius 1135142215Sglebius#ifdef INET6 1136142559Sglebiusstatic void 1137142215Sglebiuscarp_send_na(struct carp_softc *sc) 1138142215Sglebius{ 1139142215Sglebius struct ifaddr *ifa; 1140142215Sglebius struct in6_addr *in6; 1141142215Sglebius static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; 1142142215Sglebius 1143147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1144142215Sglebius 1145142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET6) 1146142215Sglebius continue; 1147142215Sglebius 1148142215Sglebius in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; 1149142564Sglebius nd6_na_output(sc->sc_carpdev, &mcast, in6, 1150142215Sglebius ND_NA_FLAG_OVERRIDE, 1, NULL); 1151142215Sglebius DELAY(1000); /* XXX */ 1152142215Sglebius } 1153142215Sglebius} 1154142215Sglebius#endif /* INET6 */ 1155142215Sglebius 1156221130Sbz#ifdef INET 1157142559Sglebiusstatic int 1158142215Sglebiuscarp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) 1159142215Sglebius{ 1160142215Sglebius struct carp_softc *vh; 1161142215Sglebius struct ifaddr *ifa; 1162142215Sglebius int count = 0; 1163142215Sglebius 1164142914Sglebius CARP_LOCK_ASSERT(cif); 1165142914Sglebius 1166142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1167142215Sglebius if ((type == CARP_COUNT_RUNNING && 1168148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1169148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) || 1170142215Sglebius (type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) { 1171191528Srwatson IF_ADDR_LOCK(SC2IFP(vh)); 1172147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1173142215Sglebius ifa_list) { 1174142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 1175142215Sglebius ia->ia_addr.sin_addr.s_addr == 1176142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) 1177142215Sglebius count++; 1178142215Sglebius } 1179191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1180142215Sglebius } 1181142215Sglebius } 1182142215Sglebius return (count); 1183142215Sglebius} 1184142215Sglebius 1185142215Sglebiusint 1186211157Swillcarp_iamatch(struct ifnet *ifp, struct in_ifaddr *ia, 1187142215Sglebius struct in_addr *isaddr, u_int8_t **enaddr) 1188142215Sglebius{ 1189211157Swill struct carp_if *cif; 1190142215Sglebius struct carp_softc *vh; 1191142215Sglebius int index, count = 0; 1192142215Sglebius struct ifaddr *ifa; 1193142215Sglebius 1194211157Swill cif = ifp->if_carp; 1195142215Sglebius CARP_LOCK(cif); 1196142215Sglebius 1197142215Sglebius if (carp_opts[CARPCTL_ARPBALANCE]) { 1198142215Sglebius /* 1199142215Sglebius * XXX proof of concept implementation. 1200142215Sglebius * We use the source ip to decide which virtual host should 1201142215Sglebius * handle the request. If we're master of that virtual host, 1202142215Sglebius * then we respond, otherwise, just drop the arp packet on 1203142215Sglebius * the floor. 1204142215Sglebius */ 1205142215Sglebius count = carp_addrcount(cif, ia, CARP_COUNT_RUNNING); 1206142215Sglebius if (count == 0) { 1207142215Sglebius /* should never reach this */ 1208142215Sglebius CARP_UNLOCK(cif); 1209142215Sglebius return (0); 1210142215Sglebius } 1211142215Sglebius 1212142215Sglebius /* this should be a hash, like pf_hash() */ 1213147718Sglebius index = ntohl(isaddr->s_addr) % count; 1214142215Sglebius count = 0; 1215142215Sglebius 1216142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1217148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1218148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) { 1219191528Srwatson IF_ADDR_LOCK(SC2IFP(vh)); 1220147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, 1221142215Sglebius ifa_list) { 1222142215Sglebius if (ifa->ifa_addr->sa_family == 1223142215Sglebius AF_INET && 1224142215Sglebius ia->ia_addr.sin_addr.s_addr == 1225142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) { 1226142215Sglebius if (count == index) { 1227142215Sglebius if (vh->sc_state == 1228142215Sglebius MASTER) { 1229152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1230191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1231142215Sglebius CARP_UNLOCK(cif); 1232142215Sglebius return (1); 1233142215Sglebius } else { 1234191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1235142215Sglebius CARP_UNLOCK(cif); 1236142215Sglebius return (0); 1237142215Sglebius } 1238142215Sglebius } 1239142215Sglebius count++; 1240142215Sglebius } 1241142215Sglebius } 1242191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1243142215Sglebius } 1244142215Sglebius } 1245142215Sglebius } else { 1246142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1247148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1248148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1249152550Sglebius ia->ia_ifp == SC2IFP(vh) && 1250152550Sglebius vh->sc_state == MASTER) { 1251152315Sru *enaddr = IF_LLADDR(vh->sc_ifp); 1252142215Sglebius CARP_UNLOCK(cif); 1253142215Sglebius return (1); 1254142215Sglebius } 1255142215Sglebius } 1256142215Sglebius } 1257142215Sglebius CARP_UNLOCK(cif); 1258142215Sglebius return (0); 1259142215Sglebius} 1260221130Sbz#endif 1261142215Sglebius 1262142215Sglebius#ifdef INET6 1263142641Smlaierstruct ifaddr * 1264211157Swillcarp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr) 1265142215Sglebius{ 1266211157Swill struct carp_if *cif; 1267142215Sglebius struct carp_softc *vh; 1268142215Sglebius struct ifaddr *ifa; 1269142215Sglebius 1270211157Swill cif = ifp->if_carp; 1271142215Sglebius CARP_LOCK(cif); 1272142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1273191528Srwatson IF_ADDR_LOCK(SC2IFP(vh)); 1274147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, ifa_list) { 1275142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1276142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1277148887Srwatson (SC2IFP(vh)->if_flags & IFF_UP) && 1278152550Sglebius (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1279152550Sglebius vh->sc_state == MASTER) { 1280194760Srwatson ifa_ref(ifa); 1281191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1282142215Sglebius CARP_UNLOCK(cif); 1283142215Sglebius return (ifa); 1284142215Sglebius } 1285142215Sglebius } 1286191528Srwatson IF_ADDR_UNLOCK(SC2IFP(vh)); 1287142215Sglebius } 1288142215Sglebius CARP_UNLOCK(cif); 1289142215Sglebius 1290142215Sglebius return (NULL); 1291142215Sglebius} 1292142215Sglebius 1293211157Swillcaddr_t 1294211157Swillcarp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr) 1295142215Sglebius{ 1296142215Sglebius struct m_tag *mtag; 1297211157Swill struct carp_if *cif; 1298142215Sglebius struct carp_softc *sc; 1299142215Sglebius struct ifaddr *ifa; 1300142215Sglebius 1301211157Swill cif = ifp->if_carp; 1302142215Sglebius CARP_LOCK(cif); 1303142215Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { 1304191528Srwatson IF_ADDR_LOCK(SC2IFP(sc)); 1305147256Sbrooks TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { 1306142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1307142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1308148887Srwatson (SC2IFP(sc)->if_flags & IFF_UP) && 1309148887Srwatson (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING)) { 1310147256Sbrooks struct ifnet *ifp = SC2IFP(sc); 1311142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, 1312142215Sglebius sizeof(struct ifnet *), M_NOWAIT); 1313142215Sglebius if (mtag == NULL) { 1314142215Sglebius /* better a bit than nothing */ 1315191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 1316142215Sglebius CARP_UNLOCK(cif); 1317152315Sru return (IF_LLADDR(sc->sc_ifp)); 1318142215Sglebius } 1319142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), 1320142215Sglebius sizeof(struct ifnet *)); 1321142215Sglebius m_tag_prepend(m, mtag); 1322142215Sglebius 1323191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 1324142215Sglebius CARP_UNLOCK(cif); 1325152315Sru return (IF_LLADDR(sc->sc_ifp)); 1326142215Sglebius } 1327142215Sglebius } 1328191528Srwatson IF_ADDR_UNLOCK(SC2IFP(sc)); 1329142215Sglebius } 1330142215Sglebius CARP_UNLOCK(cif); 1331142215Sglebius 1332142215Sglebius return (NULL); 1333142215Sglebius} 1334142215Sglebius#endif 1335142215Sglebius 1336142215Sglebiusstruct ifnet * 1337211157Swillcarp_forus(struct ifnet *ifp, u_char *dhost) 1338142215Sglebius{ 1339211157Swill struct carp_if *cif; 1340142215Sglebius struct carp_softc *vh; 1341142215Sglebius u_int8_t *ena = dhost; 1342142215Sglebius 1343142215Sglebius if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) 1344142215Sglebius return (NULL); 1345142215Sglebius 1346211157Swill cif = ifp->if_carp; 1347142215Sglebius CARP_LOCK(cif); 1348142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) 1349148887Srwatson if ((SC2IFP(vh)->if_flags & IFF_UP) && 1350148887Srwatson (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && 1351148887Srwatson vh->sc_state == MASTER && 1352152315Sru !bcmp(dhost, IF_LLADDR(vh->sc_ifp), ETHER_ADDR_LEN)) { 1353142215Sglebius CARP_UNLOCK(cif); 1354147256Sbrooks return (SC2IFP(vh)); 1355142215Sglebius } 1356142215Sglebius 1357142215Sglebius CARP_UNLOCK(cif); 1358142215Sglebius return (NULL); 1359142215Sglebius} 1360142215Sglebius 1361142559Sglebiusstatic void 1362142215Sglebiuscarp_master_down(void *v) 1363142215Sglebius{ 1364142215Sglebius struct carp_softc *sc = v; 1365142215Sglebius 1366142914Sglebius CARP_SCLOCK(sc); 1367142914Sglebius carp_master_down_locked(sc); 1368142914Sglebius CARP_SCUNLOCK(sc); 1369142914Sglebius} 1370142914Sglebius 1371142914Sglebiusstatic void 1372142914Sglebiuscarp_master_down_locked(struct carp_softc *sc) 1373142914Sglebius{ 1374142914Sglebius if (sc->sc_carpdev) 1375142914Sglebius CARP_SCLOCK_ASSERT(sc); 1376142914Sglebius 1377142215Sglebius switch (sc->sc_state) { 1378142215Sglebius case INIT: 1379142215Sglebius printf("%s: master_down event in INIT state\n", 1380147256Sbrooks SC2IFP(sc)->if_xname); 1381142215Sglebius break; 1382142215Sglebius case MASTER: 1383142215Sglebius break; 1384142215Sglebius case BACKUP: 1385142215Sglebius carp_set_state(sc, MASTER); 1386142914Sglebius carp_send_ad_locked(sc); 1387221130Sbz#ifdef INET 1388142215Sglebius carp_send_arp(sc); 1389221130Sbz#endif 1390142215Sglebius#ifdef INET6 1391142215Sglebius carp_send_na(sc); 1392142215Sglebius#endif /* INET6 */ 1393142215Sglebius carp_setrun(sc, 0); 1394142215Sglebius carp_setroute(sc, RTM_ADD); 1395142215Sglebius break; 1396142215Sglebius } 1397142215Sglebius} 1398142215Sglebius 1399142215Sglebius/* 1400142215Sglebius * When in backup state, af indicates whether to reset the master down timer 1401142215Sglebius * for v4 or v6. If it's set to zero, reset the ones which are already pending. 1402142215Sglebius */ 1403142559Sglebiusstatic void 1404142215Sglebiuscarp_setrun(struct carp_softc *sc, sa_family_t af) 1405142215Sglebius{ 1406142215Sglebius struct timeval tv; 1407142215Sglebius 1408156947Sglebius if (sc->sc_carpdev == NULL) { 1409156947Sglebius SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1410156947Sglebius carp_set_state(sc, INIT); 1411156947Sglebius return; 1412156947Sglebius } else 1413142914Sglebius CARP_SCLOCK_ASSERT(sc); 1414142914Sglebius 1415147256Sbrooks if (SC2IFP(sc)->if_flags & IFF_UP && 1416211059Sdelphij sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6) && 1417211059Sdelphij sc->sc_carpdev->if_link_state == LINK_STATE_UP) 1418148887Srwatson SC2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; 1419142215Sglebius else { 1420148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1421142215Sglebius carp_setroute(sc, RTM_DELETE); 1422142215Sglebius return; 1423142215Sglebius } 1424142215Sglebius 1425142215Sglebius switch (sc->sc_state) { 1426142215Sglebius case INIT: 1427142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) { 1428142914Sglebius carp_send_ad_locked(sc); 1429221130Sbz#ifdef INET 1430142215Sglebius carp_send_arp(sc); 1431221130Sbz#endif 1432142215Sglebius#ifdef INET6 1433142215Sglebius carp_send_na(sc); 1434142215Sglebius#endif /* INET6 */ 1435200026Sglebius CARP_LOG("%s: INIT -> MASTER (preempting)\n", 1436147256Sbrooks SC2IFP(sc)->if_xname); 1437142215Sglebius carp_set_state(sc, MASTER); 1438142215Sglebius carp_setroute(sc, RTM_ADD); 1439142215Sglebius } else { 1440200026Sglebius CARP_LOG("%s: INIT -> BACKUP\n", SC2IFP(sc)->if_xname); 1441142215Sglebius carp_set_state(sc, BACKUP); 1442142215Sglebius carp_setroute(sc, RTM_DELETE); 1443142215Sglebius carp_setrun(sc, 0); 1444142215Sglebius } 1445142215Sglebius break; 1446142215Sglebius case BACKUP: 1447142215Sglebius callout_stop(&sc->sc_ad_tmo); 1448142215Sglebius tv.tv_sec = 3 * sc->sc_advbase; 1449142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1450142215Sglebius switch (af) { 1451142215Sglebius#ifdef INET 1452142215Sglebius case AF_INET: 1453142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1454142215Sglebius carp_master_down, sc); 1455142215Sglebius break; 1456142215Sglebius#endif /* INET */ 1457142215Sglebius#ifdef INET6 1458142215Sglebius case AF_INET6: 1459142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1460142215Sglebius carp_master_down, sc); 1461142215Sglebius break; 1462142215Sglebius#endif /* INET6 */ 1463142215Sglebius default: 1464142215Sglebius if (sc->sc_naddrs) 1465142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1466142215Sglebius carp_master_down, sc); 1467142215Sglebius if (sc->sc_naddrs6) 1468142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1469142215Sglebius carp_master_down, sc); 1470142215Sglebius break; 1471142215Sglebius } 1472142215Sglebius break; 1473142215Sglebius case MASTER: 1474142215Sglebius tv.tv_sec = sc->sc_advbase; 1475142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1476142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1477142215Sglebius carp_send_ad, sc); 1478142215Sglebius break; 1479142215Sglebius } 1480142215Sglebius} 1481142215Sglebius 1482221130Sbz#ifdef INET 1483166423Sglebiusstatic void 1484215790Sglebiuscarp_multicast_cleanup(struct carp_softc *sc, int dofree) 1485156947Sglebius{ 1486156947Sglebius struct ip_moptions *imo = &sc->sc_imo; 1487156947Sglebius u_int16_t n = imo->imo_num_memberships; 1488166226Sglebius 1489156947Sglebius /* Clean up our own multicast memberships */ 1490156947Sglebius while (n-- > 0) { 1491156947Sglebius if (imo->imo_membership[n] != NULL) { 1492215790Sglebius if (dofree) 1493215790Sglebius in_delmulti(imo->imo_membership[n]); 1494156947Sglebius imo->imo_membership[n] = NULL; 1495156947Sglebius } 1496156947Sglebius } 1497170613Sbms KASSERT(imo->imo_mfilters == NULL, 1498170613Sbms ("%s: imo_mfilters != NULL", __func__)); 1499156947Sglebius imo->imo_num_memberships = 0; 1500156947Sglebius imo->imo_multicast_ifp = NULL; 1501166423Sglebius} 1502221130Sbz#endif 1503156947Sglebius 1504156947Sglebius#ifdef INET6 1505166423Sglebiusstatic void 1506215790Sglebiuscarp_multicast6_cleanup(struct carp_softc *sc, int dofree) 1507166423Sglebius{ 1508166423Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1509191672Sbms u_int16_t n = im6o->im6o_num_memberships; 1510166423Sglebius 1511191672Sbms while (n-- > 0) { 1512191672Sbms if (im6o->im6o_membership[n] != NULL) { 1513215790Sglebius if (dofree) 1514215790Sglebius in6_mc_leave(im6o->im6o_membership[n], NULL); 1515191672Sbms im6o->im6o_membership[n] = NULL; 1516191672Sbms } 1517156947Sglebius } 1518191672Sbms KASSERT(im6o->im6o_mfilters == NULL, 1519191672Sbms ("%s: im6o_mfilters != NULL", __func__)); 1520191672Sbms im6o->im6o_num_memberships = 0; 1521156947Sglebius im6o->im6o_multicast_ifp = NULL; 1522166423Sglebius} 1523156947Sglebius#endif 1524156947Sglebius 1525221130Sbz#ifdef INET 1526142559Sglebiusstatic int 1527142215Sglebiuscarp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1528142215Sglebius{ 1529142215Sglebius struct ifnet *ifp; 1530142215Sglebius struct carp_if *cif; 1531142215Sglebius struct in_ifaddr *ia, *ia_if; 1532142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1533142215Sglebius struct in_addr addr; 1534142215Sglebius u_long iaddr = htonl(sin->sin_addr.s_addr); 1535142215Sglebius int own, error; 1536142215Sglebius 1537142215Sglebius if (sin->sin_addr.s_addr == 0) { 1538147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1539142215Sglebius carp_set_state(sc, INIT); 1540142215Sglebius if (sc->sc_naddrs) 1541147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1542180513Seri if (sc->sc_carpdev) 1543180513Seri CARP_SCLOCK(sc); 1544142215Sglebius carp_setrun(sc, 0); 1545180513Seri if (sc->sc_carpdev) 1546180513Seri CARP_SCUNLOCK(sc); 1547142215Sglebius return (0); 1548142215Sglebius } 1549142215Sglebius 1550142215Sglebius /* we have to do it by hands to check we won't match on us */ 1551142215Sglebius ia_if = NULL; own = 0; 1552194951Srwatson IN_IFADDR_RLOCK(); 1553181803Sbz TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { 1554142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1555147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1556142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1557142215Sglebius (iaddr & ia->ia_subnetmask) == ia->ia_subnet) { 1558142215Sglebius if (!ia_if) 1559142215Sglebius ia_if = ia; 1560142215Sglebius if (sin->sin_addr.s_addr == 1561142215Sglebius ia->ia_addr.sin_addr.s_addr) 1562142215Sglebius own++; 1563142215Sglebius } 1564142215Sglebius } 1565142215Sglebius 1566194951Srwatson if (!ia_if) { 1567194951Srwatson IN_IFADDR_RUNLOCK(); 1568142215Sglebius return (EADDRNOTAVAIL); 1569194951Srwatson } 1570142215Sglebius 1571142215Sglebius ia = ia_if; 1572194951Srwatson ifa_ref(&ia->ia_ifa); 1573194951Srwatson IN_IFADDR_RUNLOCK(); 1574194951Srwatson 1575142215Sglebius ifp = ia->ia_ifp; 1576142215Sglebius 1577142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1578194951Srwatson (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) { 1579194951Srwatson ifa_free(&ia->ia_ifa); 1580142215Sglebius return (EADDRNOTAVAIL); 1581194951Srwatson } 1582142215Sglebius 1583142215Sglebius if (imo->imo_num_memberships == 0) { 1584142215Sglebius addr.s_addr = htonl(INADDR_CARP_GROUP); 1585194951Srwatson if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == 1586194951Srwatson NULL) { 1587194951Srwatson ifa_free(&ia->ia_ifa); 1588142215Sglebius return (ENOBUFS); 1589194951Srwatson } 1590142215Sglebius imo->imo_num_memberships++; 1591142215Sglebius imo->imo_multicast_ifp = ifp; 1592142215Sglebius imo->imo_multicast_ttl = CARP_DFLTTL; 1593142215Sglebius imo->imo_multicast_loop = 0; 1594142215Sglebius } 1595142215Sglebius 1596142215Sglebius if (!ifp->if_carp) { 1597142215Sglebius 1598184205Sdes cif = malloc(sizeof(*cif), M_CARP, 1599142215Sglebius M_WAITOK|M_ZERO); 1600142215Sglebius if (!cif) { 1601142215Sglebius error = ENOBUFS; 1602142215Sglebius goto cleanup; 1603142215Sglebius } 1604142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1605184205Sdes free(cif, M_CARP); 1606142215Sglebius goto cleanup; 1607142215Sglebius } 1608142215Sglebius 1609142215Sglebius CARP_LOCK_INIT(cif); 1610142215Sglebius CARP_LOCK(cif); 1611142215Sglebius cif->vhif_ifp = ifp; 1612142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1613142215Sglebius ifp->if_carp = cif; 1614142215Sglebius 1615142215Sglebius } else { 1616142215Sglebius struct carp_softc *vr; 1617142215Sglebius 1618142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1619142215Sglebius CARP_LOCK(cif); 1620142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1621142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1622142215Sglebius CARP_UNLOCK(cif); 1623176093Sglebius error = EEXIST; 1624142215Sglebius goto cleanup; 1625142215Sglebius } 1626142215Sglebius } 1627142215Sglebius sc->sc_ia = ia; 1628142564Sglebius sc->sc_carpdev = ifp; 1629142215Sglebius 1630142215Sglebius { /* XXX prevent endless loop if already in queue */ 1631142215Sglebius struct carp_softc *vr, *after = NULL; 1632142215Sglebius int myself = 0; 1633142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1634142215Sglebius 1635142215Sglebius /* XXX: cif should not change, right? So we still hold the lock */ 1636142215Sglebius CARP_LOCK_ASSERT(cif); 1637142215Sglebius 1638142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1639142215Sglebius if (vr == sc) 1640142215Sglebius myself = 1; 1641142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1642142215Sglebius after = vr; 1643142215Sglebius } 1644142215Sglebius 1645142215Sglebius if (!myself) { 1646142215Sglebius /* We're trying to keep things in order */ 1647142215Sglebius if (after == NULL) { 1648142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1649142215Sglebius } else { 1650142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1651142215Sglebius } 1652142215Sglebius cif->vhif_nvrs++; 1653142215Sglebius } 1654142215Sglebius } 1655142215Sglebius 1656142215Sglebius sc->sc_naddrs++; 1657147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1658142215Sglebius if (own) 1659142215Sglebius sc->sc_advskew = 0; 1660144329Sglebius carp_sc_state_locked(sc); 1661142215Sglebius carp_setrun(sc, 0); 1662142215Sglebius 1663142914Sglebius CARP_UNLOCK(cif); 1664194951Srwatson ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */ 1665142914Sglebius 1666142215Sglebius return (0); 1667142215Sglebius 1668142215Sglebiuscleanup: 1669142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1670194951Srwatson ifa_free(&ia->ia_ifa); 1671142215Sglebius return (error); 1672142215Sglebius} 1673142215Sglebius 1674142559Sglebiusstatic int 1675142215Sglebiuscarp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1676142215Sglebius{ 1677142215Sglebius int error = 0; 1678142215Sglebius 1679142215Sglebius if (!--sc->sc_naddrs) { 1680142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1681142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1682142215Sglebius 1683142914Sglebius CARP_LOCK(cif); 1684142215Sglebius callout_stop(&sc->sc_ad_tmo); 1685148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1686148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1687142215Sglebius sc->sc_vhid = -1; 1688142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1689142215Sglebius imo->imo_multicast_ifp = NULL; 1690142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1691142215Sglebius if (!--cif->vhif_nvrs) { 1692142564Sglebius sc->sc_carpdev->if_carp = NULL; 1693142215Sglebius CARP_LOCK_DESTROY(cif); 1694196397Swill free(cif, M_CARP); 1695142215Sglebius } else { 1696142215Sglebius CARP_UNLOCK(cif); 1697142215Sglebius } 1698142215Sglebius } 1699142215Sglebius 1700142215Sglebius return (error); 1701142215Sglebius} 1702221130Sbz#endif 1703142215Sglebius 1704142215Sglebius#ifdef INET6 1705142559Sglebiusstatic int 1706142215Sglebiuscarp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1707142215Sglebius{ 1708142215Sglebius struct ifnet *ifp; 1709142215Sglebius struct carp_if *cif; 1710142215Sglebius struct in6_ifaddr *ia, *ia_if; 1711142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1712148385Sume struct in6_addr in6; 1713142215Sglebius int own, error; 1714142215Sglebius 1715191672Sbms error = 0; 1716191672Sbms 1717142215Sglebius if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 1718147256Sbrooks if (!(SC2IFP(sc)->if_flags & IFF_UP)) 1719142215Sglebius carp_set_state(sc, INIT); 1720142215Sglebius if (sc->sc_naddrs6) 1721147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1722180513Seri if (sc->sc_carpdev) 1723180513Seri CARP_SCLOCK(sc); 1724142215Sglebius carp_setrun(sc, 0); 1725180513Seri if (sc->sc_carpdev) 1726180513Seri CARP_SCUNLOCK(sc); 1727142215Sglebius return (0); 1728142215Sglebius } 1729142215Sglebius 1730142215Sglebius /* we have to do it by hands to check we won't match on us */ 1731142215Sglebius ia_if = NULL; own = 0; 1732194971Srwatson IN6_IFADDR_RLOCK(); 1733194912Srwatson TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { 1734142215Sglebius int i; 1735142215Sglebius 1736142215Sglebius for (i = 0; i < 4; i++) { 1737142215Sglebius if ((sin6->sin6_addr.s6_addr32[i] & 1738142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i]) != 1739142215Sglebius (ia->ia_addr.sin6_addr.s6_addr32[i] & 1740142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i])) 1741142215Sglebius break; 1742142215Sglebius } 1743142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1744147256Sbrooks if (ia->ia_ifp != SC2IFP(sc) && 1745142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1746142215Sglebius (i == 4)) { 1747142215Sglebius if (!ia_if) 1748142215Sglebius ia_if = ia; 1749142215Sglebius if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 1750142215Sglebius &ia->ia_addr.sin6_addr)) 1751142215Sglebius own++; 1752142215Sglebius } 1753142215Sglebius } 1754142215Sglebius 1755194971Srwatson if (!ia_if) { 1756194971Srwatson IN6_IFADDR_RUNLOCK(); 1757142215Sglebius return (EADDRNOTAVAIL); 1758194971Srwatson } 1759142215Sglebius ia = ia_if; 1760194971Srwatson ifa_ref(&ia->ia_ifa); 1761194971Srwatson IN6_IFADDR_RUNLOCK(); 1762142215Sglebius ifp = ia->ia_ifp; 1763142215Sglebius 1764142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1765194971Srwatson (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) { 1766194971Srwatson ifa_free(&ia->ia_ifa); 1767142215Sglebius return (EADDRNOTAVAIL); 1768194971Srwatson } 1769142215Sglebius 1770142215Sglebius if (!sc->sc_naddrs6) { 1771191672Sbms struct in6_multi *in6m; 1772191672Sbms 1773142215Sglebius im6o->im6o_multicast_ifp = ifp; 1774142215Sglebius 1775142215Sglebius /* join CARP multicast address */ 1776148385Sume bzero(&in6, sizeof(in6)); 1777148385Sume in6.s6_addr16[0] = htons(0xff02); 1778148385Sume in6.s6_addr8[15] = 0x12; 1779148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1780142215Sglebius goto cleanup; 1781191672Sbms in6m = NULL; 1782191672Sbms error = in6_mc_join(ifp, &in6, NULL, &in6m, 0); 1783191672Sbms if (error) 1784148385Sume goto cleanup; 1785191672Sbms im6o->im6o_membership[0] = in6m; 1786191672Sbms im6o->im6o_num_memberships++; 1787142215Sglebius 1788142215Sglebius /* join solicited multicast address */ 1789148385Sume bzero(&in6, sizeof(in6)); 1790148385Sume in6.s6_addr16[0] = htons(0xff02); 1791148385Sume in6.s6_addr32[1] = 0; 1792148385Sume in6.s6_addr32[2] = htonl(1); 1793148385Sume in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; 1794148385Sume in6.s6_addr8[12] = 0xff; 1795148385Sume if (in6_setscope(&in6, ifp, NULL) != 0) 1796142215Sglebius goto cleanup; 1797191672Sbms in6m = NULL; 1798191672Sbms error = in6_mc_join(ifp, &in6, NULL, &in6m, 0); 1799191672Sbms if (error) 1800148385Sume goto cleanup; 1801191672Sbms im6o->im6o_membership[1] = in6m; 1802191672Sbms im6o->im6o_num_memberships++; 1803142215Sglebius } 1804142215Sglebius 1805142215Sglebius if (!ifp->if_carp) { 1806184205Sdes cif = malloc(sizeof(*cif), M_CARP, 1807142215Sglebius M_WAITOK|M_ZERO); 1808142215Sglebius if (!cif) { 1809142215Sglebius error = ENOBUFS; 1810142215Sglebius goto cleanup; 1811142215Sglebius } 1812142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1813184205Sdes free(cif, M_CARP); 1814142215Sglebius goto cleanup; 1815142215Sglebius } 1816142215Sglebius 1817142215Sglebius CARP_LOCK_INIT(cif); 1818142215Sglebius CARP_LOCK(cif); 1819142215Sglebius cif->vhif_ifp = ifp; 1820142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1821142215Sglebius ifp->if_carp = cif; 1822142215Sglebius 1823142215Sglebius } else { 1824142215Sglebius struct carp_softc *vr; 1825142215Sglebius 1826142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1827142215Sglebius CARP_LOCK(cif); 1828142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1829142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1830142215Sglebius CARP_UNLOCK(cif); 1831142215Sglebius error = EINVAL; 1832142215Sglebius goto cleanup; 1833142215Sglebius } 1834142215Sglebius } 1835142215Sglebius sc->sc_ia6 = ia; 1836142564Sglebius sc->sc_carpdev = ifp; 1837142215Sglebius 1838142215Sglebius { /* XXX prevent endless loop if already in queue */ 1839142215Sglebius struct carp_softc *vr, *after = NULL; 1840142215Sglebius int myself = 0; 1841142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1842142215Sglebius CARP_LOCK_ASSERT(cif); 1843142215Sglebius 1844142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1845142215Sglebius if (vr == sc) 1846142215Sglebius myself = 1; 1847142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1848142215Sglebius after = vr; 1849142215Sglebius } 1850142215Sglebius 1851142215Sglebius if (!myself) { 1852142215Sglebius /* We're trying to keep things in order */ 1853142215Sglebius if (after == NULL) { 1854142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1855142215Sglebius } else { 1856142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1857142215Sglebius } 1858142215Sglebius cif->vhif_nvrs++; 1859142215Sglebius } 1860142215Sglebius } 1861142215Sglebius 1862142215Sglebius sc->sc_naddrs6++; 1863147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1864142215Sglebius if (own) 1865142215Sglebius sc->sc_advskew = 0; 1866144329Sglebius carp_sc_state_locked(sc); 1867142215Sglebius carp_setrun(sc, 0); 1868142215Sglebius 1869142914Sglebius CARP_UNLOCK(cif); 1870194971Srwatson ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */ 1871142914Sglebius 1872142215Sglebius return (0); 1873142215Sglebius 1874142215Sglebiuscleanup: 1875191672Sbms if (!sc->sc_naddrs6) 1876215790Sglebius carp_multicast6_cleanup(sc, 1); 1877194971Srwatson ifa_free(&ia->ia_ifa); 1878142215Sglebius return (error); 1879142215Sglebius} 1880142215Sglebius 1881142559Sglebiusstatic int 1882142215Sglebiuscarp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1883142215Sglebius{ 1884142215Sglebius int error = 0; 1885142215Sglebius 1886142215Sglebius if (!--sc->sc_naddrs6) { 1887142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1888142215Sglebius 1889142914Sglebius CARP_LOCK(cif); 1890142215Sglebius callout_stop(&sc->sc_ad_tmo); 1891148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 1892148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 1893142215Sglebius sc->sc_vhid = -1; 1894215790Sglebius carp_multicast6_cleanup(sc, 1); 1895142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1896142215Sglebius if (!--cif->vhif_nvrs) { 1897142215Sglebius CARP_LOCK_DESTROY(cif); 1898142564Sglebius sc->sc_carpdev->if_carp = NULL; 1899196397Swill free(cif, M_CARP); 1900142215Sglebius } else 1901142215Sglebius CARP_UNLOCK(cif); 1902142215Sglebius } 1903142215Sglebius 1904142215Sglebius return (error); 1905142215Sglebius} 1906142215Sglebius#endif /* INET6 */ 1907142215Sglebius 1908142559Sglebiusstatic int 1909142215Sglebiuscarp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) 1910142215Sglebius{ 1911142215Sglebius struct carp_softc *sc = ifp->if_softc, *vr; 1912142215Sglebius struct carpreq carpr; 1913142215Sglebius struct ifaddr *ifa; 1914142215Sglebius struct ifreq *ifr; 1915142215Sglebius struct ifaliasreq *ifra; 1916142914Sglebius int locked = 0, error = 0; 1917142215Sglebius 1918142215Sglebius ifa = (struct ifaddr *)addr; 1919142215Sglebius ifra = (struct ifaliasreq *)addr; 1920142215Sglebius ifr = (struct ifreq *)addr; 1921142215Sglebius 1922142215Sglebius switch (cmd) { 1923142215Sglebius case SIOCSIFADDR: 1924142215Sglebius switch (ifa->ifa_addr->sa_family) { 1925142215Sglebius#ifdef INET 1926142215Sglebius case AF_INET: 1927147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1928142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1929142215Sglebius sizeof(struct sockaddr)); 1930142215Sglebius error = carp_set_addr(sc, satosin(ifa->ifa_addr)); 1931142215Sglebius break; 1932142215Sglebius#endif /* INET */ 1933142215Sglebius#ifdef INET6 1934142215Sglebius case AF_INET6: 1935147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1936142215Sglebius error = carp_set_addr6(sc, satosin6(ifa->ifa_addr)); 1937142215Sglebius break; 1938142215Sglebius#endif /* INET6 */ 1939142215Sglebius default: 1940142215Sglebius error = EAFNOSUPPORT; 1941142215Sglebius break; 1942142215Sglebius } 1943142215Sglebius break; 1944142215Sglebius 1945142215Sglebius case SIOCAIFADDR: 1946142215Sglebius switch (ifa->ifa_addr->sa_family) { 1947142215Sglebius#ifdef INET 1948142215Sglebius case AF_INET: 1949147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1950142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1951142215Sglebius sizeof(struct sockaddr)); 1952142215Sglebius error = carp_set_addr(sc, satosin(&ifra->ifra_addr)); 1953142215Sglebius break; 1954142215Sglebius#endif /* INET */ 1955142215Sglebius#ifdef INET6 1956142215Sglebius case AF_INET6: 1957147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 1958142215Sglebius error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr)); 1959142215Sglebius break; 1960142215Sglebius#endif /* INET6 */ 1961142215Sglebius default: 1962142215Sglebius error = EAFNOSUPPORT; 1963142215Sglebius break; 1964142215Sglebius } 1965142215Sglebius break; 1966142215Sglebius 1967142215Sglebius case SIOCDIFADDR: 1968142215Sglebius switch (ifa->ifa_addr->sa_family) { 1969142215Sglebius#ifdef INET 1970142215Sglebius case AF_INET: 1971142215Sglebius error = carp_del_addr(sc, satosin(&ifra->ifra_addr)); 1972142215Sglebius break; 1973142215Sglebius#endif /* INET */ 1974142215Sglebius#ifdef INET6 1975142215Sglebius case AF_INET6: 1976142215Sglebius error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr)); 1977142215Sglebius break; 1978142215Sglebius#endif /* INET6 */ 1979142215Sglebius default: 1980142215Sglebius error = EAFNOSUPPORT; 1981142215Sglebius break; 1982142215Sglebius } 1983142215Sglebius break; 1984142215Sglebius 1985142215Sglebius case SIOCSIFFLAGS: 1986142914Sglebius if (sc->sc_carpdev) { 1987142914Sglebius locked = 1; 1988142914Sglebius CARP_SCLOCK(sc); 1989142914Sglebius } 1990142215Sglebius if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { 1991142215Sglebius callout_stop(&sc->sc_ad_tmo); 1992142215Sglebius callout_stop(&sc->sc_md_tmo); 1993142215Sglebius callout_stop(&sc->sc_md6_tmo); 1994142215Sglebius if (sc->sc_state == MASTER) 1995142914Sglebius carp_send_ad_locked(sc); 1996142215Sglebius carp_set_state(sc, INIT); 1997142215Sglebius carp_setrun(sc, 0); 1998142215Sglebius } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { 1999147256Sbrooks SC2IFP(sc)->if_flags |= IFF_UP; 2000142215Sglebius carp_setrun(sc, 0); 2001142215Sglebius } 2002142215Sglebius break; 2003142215Sglebius 2004142215Sglebius case SIOCSVH: 2005164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 2006164033Srwatson if (error) 2007142215Sglebius break; 2008142215Sglebius if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) 2009142215Sglebius break; 2010142215Sglebius error = 1; 2011142914Sglebius if (sc->sc_carpdev) { 2012142914Sglebius locked = 1; 2013142914Sglebius CARP_SCLOCK(sc); 2014142914Sglebius } 2015142215Sglebius if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { 2016142215Sglebius switch (carpr.carpr_state) { 2017142215Sglebius case BACKUP: 2018142215Sglebius callout_stop(&sc->sc_ad_tmo); 2019142215Sglebius carp_set_state(sc, BACKUP); 2020142215Sglebius carp_setrun(sc, 0); 2021142215Sglebius carp_setroute(sc, RTM_DELETE); 2022142215Sglebius break; 2023142215Sglebius case MASTER: 2024142914Sglebius carp_master_down_locked(sc); 2025142215Sglebius break; 2026142215Sglebius default: 2027142215Sglebius break; 2028142215Sglebius } 2029142215Sglebius } 2030142215Sglebius if (carpr.carpr_vhid > 0) { 2031142215Sglebius if (carpr.carpr_vhid > 255) { 2032142215Sglebius error = EINVAL; 2033142215Sglebius break; 2034142215Sglebius } 2035142564Sglebius if (sc->sc_carpdev) { 2036142215Sglebius struct carp_if *cif; 2037142564Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 2038142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 2039142215Sglebius if (vr != sc && 2040170373Sglebius vr->sc_vhid == carpr.carpr_vhid) { 2041170373Sglebius error = EEXIST; 2042170373Sglebius break; 2043170373Sglebius } 2044170373Sglebius if (error == EEXIST) 2045170373Sglebius break; 2046142215Sglebius } 2047142215Sglebius sc->sc_vhid = carpr.carpr_vhid; 2048152315Sru IF_LLADDR(sc->sc_ifp)[0] = 0; 2049152315Sru IF_LLADDR(sc->sc_ifp)[1] = 0; 2050152315Sru IF_LLADDR(sc->sc_ifp)[2] = 0x5e; 2051152315Sru IF_LLADDR(sc->sc_ifp)[3] = 0; 2052152315Sru IF_LLADDR(sc->sc_ifp)[4] = 1; 2053152315Sru IF_LLADDR(sc->sc_ifp)[5] = sc->sc_vhid; 2054142215Sglebius error--; 2055142215Sglebius } 2056142215Sglebius if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { 2057142215Sglebius if (carpr.carpr_advskew >= 255) { 2058142215Sglebius error = EINVAL; 2059142215Sglebius break; 2060142215Sglebius } 2061142215Sglebius if (carpr.carpr_advbase > 255) { 2062142215Sglebius error = EINVAL; 2063142215Sglebius break; 2064142215Sglebius } 2065142215Sglebius sc->sc_advbase = carpr.carpr_advbase; 2066142215Sglebius sc->sc_advskew = carpr.carpr_advskew; 2067142215Sglebius error--; 2068142215Sglebius } 2069142215Sglebius bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); 2070142215Sglebius if (error > 0) 2071142215Sglebius error = EINVAL; 2072142215Sglebius else { 2073142215Sglebius error = 0; 2074142215Sglebius carp_setrun(sc, 0); 2075142215Sglebius } 2076142215Sglebius break; 2077142215Sglebius 2078142215Sglebius case SIOCGVH: 2079142914Sglebius /* XXX: lockless read */ 2080142215Sglebius bzero(&carpr, sizeof(carpr)); 2081142215Sglebius carpr.carpr_state = sc->sc_state; 2082142215Sglebius carpr.carpr_vhid = sc->sc_vhid; 2083142215Sglebius carpr.carpr_advbase = sc->sc_advbase; 2084142215Sglebius carpr.carpr_advskew = sc->sc_advskew; 2085164033Srwatson error = priv_check(curthread, PRIV_NETINET_CARP); 2086164033Srwatson if (error == 0) 2087142215Sglebius bcopy(sc->sc_key, carpr.carpr_key, 2088142215Sglebius sizeof(carpr.carpr_key)); 2089142215Sglebius error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); 2090142215Sglebius break; 2091142215Sglebius 2092142215Sglebius default: 2093142215Sglebius error = EINVAL; 2094142215Sglebius } 2095142215Sglebius 2096142914Sglebius if (locked) 2097142914Sglebius CARP_SCUNLOCK(sc); 2098142914Sglebius 2099142215Sglebius carp_hmac_prepare(sc); 2100142914Sglebius 2101142215Sglebius return (error); 2102142215Sglebius} 2103142215Sglebius 2104142215Sglebius/* 2105142215Sglebius * XXX: this is looutput. We should eventually use it from there. 2106142215Sglebius */ 2107142215Sglebiusstatic int 2108142215Sglebiuscarp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 2109191148Skmacy struct route *ro) 2110142215Sglebius{ 2111147611Sdwmalone u_int32_t af; 2112191148Skmacy struct rtentry *rt = NULL; 2113147611Sdwmalone 2114142215Sglebius M_ASSERTPKTHDR(m); /* check if we have the packet header */ 2115142215Sglebius 2116191148Skmacy if (ro != NULL) 2117191148Skmacy rt = ro->ro_rt; 2118142215Sglebius if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 2119142215Sglebius m_freem(m); 2120142215Sglebius return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 2121142215Sglebius rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 2122142215Sglebius } 2123142215Sglebius 2124142215Sglebius ifp->if_opackets++; 2125142215Sglebius ifp->if_obytes += m->m_pkthdr.len; 2126147611Sdwmalone 2127147611Sdwmalone /* BPF writes need to be handled specially. */ 2128147611Sdwmalone if (dst->sa_family == AF_UNSPEC) { 2129147611Sdwmalone bcopy(dst->sa_data, &af, sizeof(af)); 2130147611Sdwmalone dst->sa_family = af; 2131147611Sdwmalone } 2132147611Sdwmalone 2133142215Sglebius#if 1 /* XXX */ 2134142215Sglebius switch (dst->sa_family) { 2135142215Sglebius case AF_INET: 2136142215Sglebius case AF_INET6: 2137142215Sglebius case AF_IPX: 2138142215Sglebius case AF_APPLETALK: 2139142215Sglebius break; 2140142215Sglebius default: 2141142215Sglebius printf("carp_looutput: af=%d unexpected\n", dst->sa_family); 2142142215Sglebius m_freem(m); 2143142215Sglebius return (EAFNOSUPPORT); 2144142215Sglebius } 2145142215Sglebius#endif 2146142215Sglebius return(if_simloop(ifp, m, dst->sa_family, 0)); 2147142215Sglebius} 2148142215Sglebius 2149142215Sglebius/* 2150142215Sglebius * Start output on carp interface. This function should never be called. 2151142215Sglebius */ 2152142559Sglebiusstatic void 2153142215Sglebiuscarp_start(struct ifnet *ifp) 2154142215Sglebius{ 2155142215Sglebius#ifdef DEBUG 2156142215Sglebius printf("%s: start called\n", ifp->if_xname); 2157142215Sglebius#endif 2158142215Sglebius} 2159142215Sglebius 2160142215Sglebiusint 2161142215Sglebiuscarp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, 2162142215Sglebius struct rtentry *rt) 2163142215Sglebius{ 2164142215Sglebius struct m_tag *mtag; 2165142215Sglebius struct carp_softc *sc; 2166142215Sglebius struct ifnet *carp_ifp; 2167142215Sglebius 2168142215Sglebius if (!sa) 2169142215Sglebius return (0); 2170142215Sglebius 2171142215Sglebius switch (sa->sa_family) { 2172142215Sglebius#ifdef INET 2173142215Sglebius case AF_INET: 2174142215Sglebius break; 2175142215Sglebius#endif /* INET */ 2176142215Sglebius#ifdef INET6 2177142215Sglebius case AF_INET6: 2178142215Sglebius break; 2179142215Sglebius#endif /* INET6 */ 2180142215Sglebius default: 2181142215Sglebius return (0); 2182142215Sglebius } 2183142215Sglebius 2184142215Sglebius mtag = m_tag_find(m, PACKET_TAG_CARP, NULL); 2185142215Sglebius if (mtag == NULL) 2186142215Sglebius return (0); 2187142215Sglebius 2188142215Sglebius bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *)); 2189142215Sglebius sc = carp_ifp->if_softc; 2190142215Sglebius 2191142215Sglebius /* Set the source MAC address to Virtual Router MAC Address */ 2192142215Sglebius switch (ifp->if_type) { 2193142798Syar case IFT_ETHER: 2194142798Syar case IFT_L2VLAN: { 2195142215Sglebius struct ether_header *eh; 2196142215Sglebius 2197142215Sglebius eh = mtod(m, struct ether_header *); 2198142215Sglebius eh->ether_shost[0] = 0; 2199142215Sglebius eh->ether_shost[1] = 0; 2200142215Sglebius eh->ether_shost[2] = 0x5e; 2201142215Sglebius eh->ether_shost[3] = 0; 2202142215Sglebius eh->ether_shost[4] = 1; 2203142215Sglebius eh->ether_shost[5] = sc->sc_vhid; 2204142215Sglebius } 2205142215Sglebius break; 2206142215Sglebius case IFT_FDDI: { 2207142215Sglebius struct fddi_header *fh; 2208142215Sglebius 2209142215Sglebius fh = mtod(m, struct fddi_header *); 2210142215Sglebius fh->fddi_shost[0] = 0; 2211142215Sglebius fh->fddi_shost[1] = 0; 2212142215Sglebius fh->fddi_shost[2] = 0x5e; 2213142215Sglebius fh->fddi_shost[3] = 0; 2214142215Sglebius fh->fddi_shost[4] = 1; 2215142215Sglebius fh->fddi_shost[5] = sc->sc_vhid; 2216142215Sglebius } 2217142215Sglebius break; 2218142215Sglebius case IFT_ISO88025: { 2219142215Sglebius struct iso88025_header *th; 2220142215Sglebius th = mtod(m, struct iso88025_header *); 2221142215Sglebius th->iso88025_shost[0] = 3; 2222142215Sglebius th->iso88025_shost[1] = 0; 2223142215Sglebius th->iso88025_shost[2] = 0x40 >> (sc->sc_vhid - 1); 2224142215Sglebius th->iso88025_shost[3] = 0x40000 >> (sc->sc_vhid - 1); 2225142215Sglebius th->iso88025_shost[4] = 0; 2226142215Sglebius th->iso88025_shost[5] = 0; 2227142215Sglebius } 2228142215Sglebius break; 2229142215Sglebius default: 2230142215Sglebius printf("%s: carp is not supported for this interface type\n", 2231142215Sglebius ifp->if_xname); 2232142215Sglebius return (EOPNOTSUPP); 2233142215Sglebius } 2234142215Sglebius 2235142215Sglebius return (0); 2236142215Sglebius} 2237142215Sglebius 2238142559Sglebiusstatic void 2239142215Sglebiuscarp_set_state(struct carp_softc *sc, int state) 2240142215Sglebius{ 2241185636Sglebius int link_state; 2242142914Sglebius 2243142914Sglebius if (sc->sc_carpdev) 2244142914Sglebius CARP_SCLOCK_ASSERT(sc); 2245142914Sglebius 2246142215Sglebius if (sc->sc_state == state) 2247142215Sglebius return; 2248142215Sglebius 2249142215Sglebius sc->sc_state = state; 2250142215Sglebius switch (state) { 2251142215Sglebius case BACKUP: 2252185636Sglebius link_state = LINK_STATE_DOWN; 2253142215Sglebius break; 2254142215Sglebius case MASTER: 2255185636Sglebius link_state = LINK_STATE_UP; 2256142215Sglebius break; 2257142215Sglebius default: 2258185636Sglebius link_state = LINK_STATE_UNKNOWN; 2259142215Sglebius break; 2260142215Sglebius } 2261185636Sglebius if_link_state_change(SC2IFP(sc), link_state); 2262142215Sglebius} 2263142215Sglebius 2264142215Sglebiusvoid 2265211157Swillcarp_carpdev_state(struct ifnet *ifp) 2266142215Sglebius{ 2267211157Swill struct carp_if *cif; 2268142914Sglebius 2269211157Swill cif = ifp->if_carp; 2270142914Sglebius CARP_LOCK(cif); 2271142914Sglebius carp_carpdev_state_locked(cif); 2272142914Sglebius CARP_UNLOCK(cif); 2273142914Sglebius} 2274142914Sglebius 2275142914Sglebiusstatic void 2276142914Sglebiuscarp_carpdev_state_locked(struct carp_if *cif) 2277142914Sglebius{ 2278142215Sglebius struct carp_softc *sc; 2279142215Sglebius 2280144329Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) 2281144329Sglebius carp_sc_state_locked(sc); 2282144329Sglebius} 2283144329Sglebius 2284144329Sglebiusstatic void 2285144329Sglebiuscarp_sc_state_locked(struct carp_softc *sc) 2286144329Sglebius{ 2287144329Sglebius CARP_SCLOCK_ASSERT(sc); 2288144329Sglebius 2289144329Sglebius if (sc->sc_carpdev->if_link_state != LINK_STATE_UP || 2290144329Sglebius !(sc->sc_carpdev->if_flags & IFF_UP)) { 2291147256Sbrooks sc->sc_flags_backup = SC2IFP(sc)->if_flags; 2292148887Srwatson SC2IFP(sc)->if_flags &= ~IFF_UP; 2293148887Srwatson SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 2294144329Sglebius callout_stop(&sc->sc_ad_tmo); 2295144329Sglebius callout_stop(&sc->sc_md_tmo); 2296144329Sglebius callout_stop(&sc->sc_md6_tmo); 2297144329Sglebius carp_set_state(sc, INIT); 2298144329Sglebius carp_setrun(sc, 0); 2299144329Sglebius if (!sc->sc_suppress) { 2300144329Sglebius carp_suppress_preempt++; 2301144329Sglebius if (carp_suppress_preempt == 1) { 2302144329Sglebius CARP_SCUNLOCK(sc); 2303144329Sglebius carp_send_ad_all(); 2304144329Sglebius CARP_SCLOCK(sc); 2305142215Sglebius } 2306142215Sglebius } 2307144329Sglebius sc->sc_suppress = 1; 2308144329Sglebius } else { 2309147256Sbrooks SC2IFP(sc)->if_flags |= sc->sc_flags_backup; 2310144329Sglebius carp_set_state(sc, INIT); 2311144329Sglebius carp_setrun(sc, 0); 2312144329Sglebius if (sc->sc_suppress) 2313144329Sglebius carp_suppress_preempt--; 2314144329Sglebius sc->sc_suppress = 0; 2315142215Sglebius } 2316144329Sglebius 2317144329Sglebius return; 2318142215Sglebius} 2319142215Sglebius 2320211157Swill#ifdef INET 2321211157Swillextern struct domain inetdomain; 2322211157Swillstatic struct protosw in_carp_protosw = { 2323211157Swill .pr_type = SOCK_RAW, 2324211157Swill .pr_domain = &inetdomain, 2325211157Swill .pr_protocol = IPPROTO_CARP, 2326211157Swill .pr_flags = PR_ATOMIC|PR_ADDR, 2327211157Swill .pr_input = carp_input, 2328211157Swill .pr_output = (pr_output_t *)rip_output, 2329211157Swill .pr_ctloutput = rip_ctloutput, 2330211157Swill .pr_usrreqs = &rip_usrreqs 2331211157Swill}; 2332211157Swill#endif 2333211157Swill 2334211157Swill#ifdef INET6 2335211157Swillextern struct domain inet6domain; 2336211157Swillstatic struct ip6protosw in6_carp_protosw = { 2337211157Swill .pr_type = SOCK_RAW, 2338211157Swill .pr_domain = &inet6domain, 2339211157Swill .pr_protocol = IPPROTO_CARP, 2340211157Swill .pr_flags = PR_ATOMIC|PR_ADDR, 2341211157Swill .pr_input = carp6_input, 2342211157Swill .pr_output = rip6_output, 2343211157Swill .pr_ctloutput = rip6_ctloutput, 2344211157Swill .pr_usrreqs = &rip6_usrreqs 2345211157Swill}; 2346211157Swill#endif 2347211157Swill 2348211157Swillstatic void 2349211157Swillcarp_mod_cleanup(void) 2350211157Swill{ 2351211157Swill 2352211157Swill if (if_detach_event_tag == NULL) 2353211157Swill return; 2354211157Swill EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag); 2355211157Swill if_clone_detach(&carp_cloner); 2356211157Swill#ifdef INET 2357211157Swill if (proto_reg[CARP_INET] == 0) { 2358212266Swill (void)ipproto_unregister(IPPROTO_CARP); 2359211157Swill pf_proto_unregister(PF_INET, IPPROTO_CARP, SOCK_RAW); 2360211157Swill proto_reg[CARP_INET] = -1; 2361211157Swill } 2362211157Swill carp_iamatch_p = NULL; 2363211157Swill#endif 2364211157Swill#ifdef INET6 2365211157Swill if (proto_reg[CARP_INET6] == 0) { 2366212266Swill (void)ip6proto_unregister(IPPROTO_CARP); 2367211157Swill pf_proto_unregister(PF_INET6, IPPROTO_CARP, SOCK_RAW); 2368211157Swill proto_reg[CARP_INET6] = -1; 2369211157Swill } 2370211157Swill carp_iamatch6_p = NULL; 2371211157Swill carp_macmatch6_p = NULL; 2372211157Swill#endif 2373211157Swill carp_linkstate_p = NULL; 2374211157Swill carp_forus_p = NULL; 2375211157Swill carp_output_p = NULL; 2376211157Swill mtx_destroy(&carp_mtx); 2377211157Swill} 2378211157Swill 2379142215Sglebiusstatic int 2380211157Swillcarp_mod_load(void) 2381211157Swill{ 2382212266Swill int err; 2383211157Swill 2384211157Swill if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 2385211157Swill carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 2386211157Swill if (if_detach_event_tag == NULL) 2387211157Swill return (ENOMEM); 2388211157Swill mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); 2389211157Swill LIST_INIT(&carpif_list); 2390211157Swill if_clone_attach(&carp_cloner); 2391211157Swill carp_linkstate_p = carp_carpdev_state; 2392211157Swill carp_forus_p = carp_forus; 2393211157Swill carp_output_p = carp_output; 2394211157Swill#ifdef INET6 2395211157Swill carp_iamatch6_p = carp_iamatch6; 2396211157Swill carp_macmatch6_p = carp_macmatch6; 2397211157Swill proto_reg[CARP_INET6] = pf_proto_register(PF_INET6, 2398211157Swill (struct protosw *)&in6_carp_protosw); 2399211157Swill if (proto_reg[CARP_INET6] != 0) { 2400211157Swill printf("carp: error %d attaching to PF_INET6\n", 2401211157Swill proto_reg[CARP_INET6]); 2402211157Swill carp_mod_cleanup(); 2403212898Sglebius return (proto_reg[CARP_INET6]); 2404211157Swill } 2405212266Swill err = ip6proto_register(IPPROTO_CARP); 2406212266Swill if (err) { 2407212266Swill printf("carp: error %d registering with INET6\n", err); 2408212266Swill carp_mod_cleanup(); 2409212898Sglebius return (err); 2410212266Swill } 2411211157Swill#endif 2412211157Swill#ifdef INET 2413211157Swill carp_iamatch_p = carp_iamatch; 2414211157Swill proto_reg[CARP_INET] = pf_proto_register(PF_INET, &in_carp_protosw); 2415211157Swill if (proto_reg[CARP_INET] != 0) { 2416211157Swill printf("carp: error %d attaching to PF_INET\n", 2417211157Swill proto_reg[CARP_INET]); 2418211157Swill carp_mod_cleanup(); 2419212898Sglebius return (proto_reg[CARP_INET]); 2420211157Swill } 2421212266Swill err = ipproto_register(IPPROTO_CARP); 2422212266Swill if (err) { 2423212266Swill printf("carp: error %d registering with INET\n", err); 2424212266Swill carp_mod_cleanup(); 2425212898Sglebius return (err); 2426212266Swill } 2427211157Swill#endif 2428211157Swill return 0; 2429211157Swill} 2430211157Swill 2431211157Swillstatic int 2432142215Sglebiuscarp_modevent(module_t mod, int type, void *data) 2433142215Sglebius{ 2434142215Sglebius switch (type) { 2435142215Sglebius case MOD_LOAD: 2436211157Swill return carp_mod_load(); 2437211157Swill /* NOTREACHED */ 2438142215Sglebius case MOD_UNLOAD: 2439211157Swill /* 2440211157Swill * XXX: For now, disallow module unloading by default due to 2441211157Swill * a race condition where a thread may dereference one of the 2442211157Swill * function pointer hooks after the module has been 2443211157Swill * unloaded, during processing of a packet, causing a panic. 2444211157Swill */ 2445211157Swill#ifdef CARPMOD_CAN_UNLOAD 2446211157Swill carp_mod_cleanup(); 2447211157Swill#else 2448211157Swill return (EBUSY); 2449211157Swill#endif 2450142215Sglebius break; 2451142215Sglebius 2452142215Sglebius default: 2453156947Sglebius return (EINVAL); 2454142215Sglebius } 2455142215Sglebius 2456156947Sglebius return (0); 2457142215Sglebius} 2458142215Sglebius 2459142215Sglebiusstatic moduledata_t carp_mod = { 2460142215Sglebius "carp", 2461142215Sglebius carp_modevent, 2462142215Sglebius 0 2463142215Sglebius}; 2464142215Sglebius 2465212265SwillDECLARE_MODULE(carp, carp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); 2466