ip_carp.c revision 144329
1142215Sglebius/* $FreeBSD: head/sys/netinet/ip_carp.c 144329 2005-03-30 11:44:43Z glebius $ */ 2142215Sglebius 3142215Sglebius/* 4142215Sglebius * Copyright (c) 2002 Michael Shalayeff. All rights reserved. 5142215Sglebius * Copyright (c) 2003 Ryan McBride. All rights reserved. 6142215Sglebius * 7142215Sglebius * Redistribution and use in source and binary forms, with or without 8142215Sglebius * modification, are permitted provided that the following conditions 9142215Sglebius * are met: 10142215Sglebius * 1. Redistributions of source code must retain the above copyright 11142215Sglebius * notice, this list of conditions and the following disclaimer. 12142215Sglebius * 2. Redistributions in binary form must reproduce the above copyright 13142215Sglebius * notice, this list of conditions and the following disclaimer in the 14142215Sglebius * documentation and/or other materials provided with the distribution. 15142215Sglebius * 16142215Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17142215Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18142215Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19142215Sglebius * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 20142215Sglebius * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21142215Sglebius * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22142215Sglebius * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23142215Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24142215Sglebius * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25142215Sglebius * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26142215Sglebius * THE POSSIBILITY OF SUCH DAMAGE. 27142215Sglebius */ 28142215Sglebius 29142215Sglebius#include "opt_carp.h" 30142215Sglebius#include "opt_bpf.h" 31142215Sglebius#include "opt_inet.h" 32142215Sglebius#include "opt_inet6.h" 33142215Sglebius 34142215Sglebius#include <sys/types.h> 35142215Sglebius#include <sys/param.h> 36142215Sglebius#include <sys/systm.h> 37142215Sglebius#include <sys/conf.h> 38142215Sglebius#include <sys/kernel.h> 39142215Sglebius#include <sys/limits.h> 40142215Sglebius#include <sys/malloc.h> 41142215Sglebius#include <sys/mbuf.h> 42142215Sglebius#include <sys/module.h> 43142215Sglebius#include <sys/time.h> 44142215Sglebius#include <sys/proc.h> 45142215Sglebius#include <sys/sysctl.h> 46142215Sglebius#include <sys/syslog.h> 47142215Sglebius#include <sys/signalvar.h> 48142215Sglebius#include <sys/filio.h> 49142215Sglebius#include <sys/sockio.h> 50142215Sglebius 51142215Sglebius#include <sys/socket.h> 52142215Sglebius#include <sys/vnode.h> 53142215Sglebius 54142215Sglebius#include <machine/stdarg.h> 55142215Sglebius 56142215Sglebius#include <net/bpf.h> 57142215Sglebius#include <net/ethernet.h> 58142215Sglebius#include <net/fddi.h> 59142215Sglebius#include <net/iso88025.h> 60142215Sglebius#include <net/if.h> 61142215Sglebius#include <net/if_clone.h> 62142215Sglebius#include <net/if_types.h> 63142215Sglebius#include <net/route.h> 64142215Sglebius 65142215Sglebius#ifdef INET 66142215Sglebius#include <netinet/in.h> 67142215Sglebius#include <netinet/in_var.h> 68142215Sglebius#include <netinet/in_systm.h> 69142215Sglebius#include <netinet/ip.h> 70142215Sglebius#include <netinet/ip_var.h> 71142215Sglebius#include <netinet/if_ether.h> 72142215Sglebius#include <machine/in_cksum.h> 73142215Sglebius#endif 74142215Sglebius 75142215Sglebius#ifdef INET6 76142215Sglebius#include <netinet/icmp6.h> 77142215Sglebius#include <netinet/ip6.h> 78142215Sglebius#include <netinet6/ip6_var.h> 79142215Sglebius#include <netinet6/nd6.h> 80142215Sglebius#include <net/if_dl.h> 81142215Sglebius#endif 82142215Sglebius 83142215Sglebius#include <crypto/sha1.h> 84142215Sglebius#include <netinet/ip_carp.h> 85142215Sglebius 86142215Sglebius#define CARP_IFNAME "carp" 87142215Sglebiusstatic MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces"); 88142215SglebiusSYSCTL_DECL(_net_inet_carp); 89142215Sglebius 90142215Sglebiusstruct carp_softc { 91142215Sglebius struct arpcom sc_ac; /* Interface clue */ 92142564Sglebius#define sc_if sc_ac.ac_if 93142901Sglebius struct ifnet *sc_carpdev; /* Pointer to parent interface */ 94142215Sglebius struct in_ifaddr *sc_ia; /* primary iface address */ 95142215Sglebius struct ip_moptions sc_imo; 96142215Sglebius#ifdef INET6 97142215Sglebius struct in6_ifaddr *sc_ia6; /* primary iface address v6 */ 98142215Sglebius struct ip6_moptions sc_im6o; 99142215Sglebius#endif /* INET6 */ 100142215Sglebius TAILQ_ENTRY(carp_softc) sc_list; 101142215Sglebius 102142215Sglebius enum { INIT = 0, BACKUP, MASTER } sc_state; 103142215Sglebius 104142215Sglebius int sc_flags_backup; 105142215Sglebius int sc_suppress; 106142215Sglebius 107142215Sglebius int sc_sendad_errors; 108142215Sglebius#define CARP_SENDAD_MAX_ERRORS 3 109142215Sglebius int sc_sendad_success; 110142215Sglebius#define CARP_SENDAD_MIN_SUCCESS 3 111142215Sglebius 112142215Sglebius int sc_vhid; 113142215Sglebius int sc_advskew; 114142215Sglebius int sc_naddrs; 115142215Sglebius int sc_naddrs6; 116142215Sglebius int sc_advbase; /* seconds */ 117142215Sglebius int sc_init_counter; 118142215Sglebius u_int64_t sc_counter; 119142215Sglebius 120142215Sglebius /* authentication */ 121142215Sglebius#define CARP_HMAC_PAD 64 122142215Sglebius unsigned char sc_key[CARP_KEY_LEN]; 123142215Sglebius unsigned char sc_pad[CARP_HMAC_PAD]; 124142215Sglebius SHA1_CTX sc_sha1; 125142215Sglebius 126142215Sglebius struct callout sc_ad_tmo; /* advertisement timeout */ 127142215Sglebius struct callout sc_md_tmo; /* master down timeout */ 128142215Sglebius struct callout sc_md6_tmo; /* master down timeout */ 129142215Sglebius 130142215Sglebius LIST_ENTRY(carp_softc) sc_next; /* Interface clue */ 131142215Sglebius}; 132142215Sglebius 133142215Sglebiusint carp_suppress_preempt = 0; 134142215Sglebiusint carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */ 135142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW, 136142215Sglebius &carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets"); 137142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW, 138142215Sglebius &carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode"); 139142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW, 140142215Sglebius &carp_opts[CARPCTL_LOG], 0, "log bad carp packets"); 141142215SglebiusSYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW, 142142215Sglebius &carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses"); 143142215Sglebius 144142215Sglebiusstruct carpstats carpstats; 145142215SglebiusSYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW, 146142215Sglebius &carpstats, carpstats, 147142215Sglebius "CARP statistics (struct carpstats, netinet/ip_carp.h)"); 148142215Sglebius 149142215Sglebiusstruct carp_if { 150142215Sglebius TAILQ_HEAD(, carp_softc) vhif_vrs; 151142215Sglebius int vhif_nvrs; 152142215Sglebius 153142914Sglebius struct callout cif_tmo; 154142215Sglebius struct ifnet *vhif_ifp; 155142215Sglebius struct mtx vhif_mtx; 156142215Sglebius}; 157142914Sglebius 158142914Sglebius/* Get carp_if from softc. Valid after carp_set_addr{,6}. */ 159142914Sglebius#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp) 160142914Sglebius 161142215Sglebius/* lock per carp_if queue */ 162142914Sglebius#define CARP_LOCK_INIT(cif) mtx_init(&(cif)->vhif_mtx, "carp_if", \ 163142215Sglebius NULL, MTX_DEF) 164142914Sglebius#define CARP_LOCK_DESTROY(cif) mtx_destroy(&(cif)->vhif_mtx) 165142215Sglebius#define CARP_LOCK_ASSERT(cif) mtx_assert(&(cif)->vhif_mtx, MA_OWNED) 166142215Sglebius#define CARP_LOCK(cif) mtx_lock(&(cif)->vhif_mtx) 167142215Sglebius#define CARP_UNLOCK(cif) mtx_unlock(&(cif)->vhif_mtx) 168142215Sglebius 169142914Sglebius#define CARP_SCLOCK(sc) mtx_lock(&SC2CIF(sc)->vhif_mtx) 170142914Sglebius#define CARP_SCUNLOCK(sc) mtx_unlock(&SC2CIF(sc)->vhif_mtx) 171142914Sglebius#define CARP_SCLOCK_ASSERT(sc) mtx_assert(&SC2CIF(sc)->vhif_mtx, MA_OWNED) 172142914Sglebius 173142451Sglebius#define CARP_LOG(...) do { \ 174142446Sglebius if (carp_opts[CARPCTL_LOG] > 0) \ 175142446Sglebius log(LOG_INFO, __VA_ARGS__); \ 176142451Sglebius} while (0) 177142215Sglebius 178142451Sglebius#define CARP_DEBUG(...) do { \ 179142446Sglebius if (carp_opts[CARPCTL_LOG] > 1) \ 180142446Sglebius log(LOG_DEBUG, __VA_ARGS__); \ 181142451Sglebius} while (0) 182142446Sglebius 183142559Sglebiusstatic void carp_hmac_prepare(struct carp_softc *); 184142559Sglebiusstatic void carp_hmac_generate(struct carp_softc *, u_int32_t *, 185142559Sglebius unsigned char *); 186142559Sglebiusstatic int carp_hmac_verify(struct carp_softc *, u_int32_t *, 187142559Sglebius unsigned char *); 188142559Sglebiusstatic void carp_setroute(struct carp_softc *, int); 189142559Sglebiusstatic void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); 190142559Sglebiusstatic int carp_clone_create(struct if_clone *, int); 191142559Sglebiusstatic void carp_clone_destroy(struct ifnet *); 192142559Sglebiusstatic void carpdetach(struct carp_softc *); 193142559Sglebiusstatic int carp_prepare_ad(struct mbuf *, struct carp_softc *, 194142559Sglebius struct carp_header *); 195142559Sglebiusstatic void carp_send_ad_all(void); 196142559Sglebiusstatic void carp_send_ad(void *); 197142914Sglebiusstatic void carp_send_ad_locked(struct carp_softc *); 198142559Sglebiusstatic void carp_send_arp(struct carp_softc *); 199142559Sglebiusstatic void carp_master_down(void *); 200142914Sglebiusstatic void carp_master_down_locked(struct carp_softc *); 201142559Sglebiusstatic int carp_ioctl(struct ifnet *, u_long, caddr_t); 202142559Sglebiusstatic int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *, 203142559Sglebius struct rtentry *); 204142559Sglebiusstatic void carp_start(struct ifnet *); 205142559Sglebiusstatic void carp_setrun(struct carp_softc *, sa_family_t); 206142559Sglebiusstatic void carp_set_state(struct carp_softc *, int); 207142559Sglebiusstatic int carp_addrcount(struct carp_if *, struct in_ifaddr *, int); 208142215Sglebiusenum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING }; 209142215Sglebius 210142559Sglebiusstatic int carp_set_addr(struct carp_softc *, struct sockaddr_in *); 211142559Sglebiusstatic int carp_del_addr(struct carp_softc *, struct sockaddr_in *); 212142914Sglebiusstatic void carp_carpdev_state1(void *); 213142914Sglebiusstatic void carp_carpdev_state_locked(struct carp_if *); 214144329Sglebiusstatic void carp_sc_state_locked(struct carp_softc *); 215142215Sglebius#ifdef INET6 216142559Sglebiusstatic void carp_send_na(struct carp_softc *); 217142559Sglebiusstatic int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); 218142559Sglebiusstatic int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *); 219142215Sglebius#endif 220142215Sglebius 221142215Sglebiusstatic LIST_HEAD(, carp_softc) carpif_list; 222142911Sglebiusstatic struct mtx carp_mtx; 223142215SglebiusIFC_SIMPLE_DECLARE(carp, 0); 224142215Sglebius 225142215Sglebiusstatic __inline u_int16_t 226142215Sglebiuscarp_cksum(struct mbuf *m, int len) 227142215Sglebius{ 228142215Sglebius return (in_cksum(m, len)); 229142215Sglebius} 230142215Sglebius 231142559Sglebiusstatic void 232142215Sglebiuscarp_hmac_prepare(struct carp_softc *sc) 233142215Sglebius{ 234142215Sglebius u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT; 235142215Sglebius u_int8_t vhid = sc->sc_vhid & 0xff; 236142215Sglebius struct ifaddr *ifa; 237142215Sglebius int i; 238142215Sglebius#ifdef INET6 239142215Sglebius struct in6_addr in6; 240142215Sglebius#endif 241142215Sglebius 242142914Sglebius if (sc->sc_carpdev) 243142914Sglebius CARP_SCLOCK(sc); 244142914Sglebius 245142914Sglebius /* XXX: possible race here */ 246142914Sglebius 247142215Sglebius /* compute ipad from key */ 248142215Sglebius bzero(sc->sc_pad, sizeof(sc->sc_pad)); 249142215Sglebius bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); 250142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 251142215Sglebius sc->sc_pad[i] ^= 0x36; 252142215Sglebius 253142215Sglebius /* precompute first part of inner hash */ 254142215Sglebius SHA1Init(&sc->sc_sha1); 255142215Sglebius SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); 256142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version)); 257142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); 258142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); 259142215Sglebius#ifdef INET 260142215Sglebius TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { 261142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET) 262142215Sglebius SHA1Update(&sc->sc_sha1, 263142215Sglebius (void *)&ifatoia(ifa)->ia_addr.sin_addr.s_addr, 264142215Sglebius sizeof(struct in_addr)); 265142215Sglebius } 266142215Sglebius#endif /* INET */ 267142215Sglebius#ifdef INET6 268142215Sglebius TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { 269142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET6) { 270142215Sglebius in6 = ifatoia6(ifa)->ia_addr.sin6_addr; 271142215Sglebius if (IN6_IS_ADDR_LINKLOCAL(&in6)) 272142215Sglebius in6.s6_addr16[1] = 0; 273142215Sglebius SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(in6)); 274142215Sglebius } 275142215Sglebius } 276142215Sglebius#endif /* INET6 */ 277142215Sglebius 278142215Sglebius /* convert ipad to opad */ 279142215Sglebius for (i = 0; i < sizeof(sc->sc_pad); i++) 280142215Sglebius sc->sc_pad[i] ^= 0x36 ^ 0x5c; 281142914Sglebius 282142914Sglebius if (sc->sc_carpdev) 283142914Sglebius CARP_SCUNLOCK(sc); 284142215Sglebius} 285142215Sglebius 286142559Sglebiusstatic void 287142215Sglebiuscarp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], 288142215Sglebius unsigned char md[20]) 289142215Sglebius{ 290142215Sglebius SHA1_CTX sha1ctx; 291142215Sglebius 292142215Sglebius /* fetch first half of inner hash */ 293142215Sglebius bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); 294142215Sglebius 295142215Sglebius SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter)); 296142215Sglebius SHA1Final(md, &sha1ctx); 297142215Sglebius 298142215Sglebius /* outer hash */ 299142215Sglebius SHA1Init(&sha1ctx); 300142215Sglebius SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); 301142215Sglebius SHA1Update(&sha1ctx, md, 20); 302142215Sglebius SHA1Final(md, &sha1ctx); 303142215Sglebius} 304142215Sglebius 305142559Sglebiusstatic int 306142215Sglebiuscarp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], 307142215Sglebius unsigned char md[20]) 308142215Sglebius{ 309142215Sglebius unsigned char md2[20]; 310142215Sglebius 311142914Sglebius CARP_SCLOCK_ASSERT(sc); 312142914Sglebius 313142215Sglebius carp_hmac_generate(sc, counter, md2); 314142215Sglebius 315142215Sglebius return (bcmp(md, md2, sizeof(md2))); 316142215Sglebius} 317142215Sglebius 318142559Sglebiusstatic void 319142215Sglebiuscarp_setroute(struct carp_softc *sc, int cmd) 320142215Sglebius{ 321142215Sglebius struct ifaddr *ifa; 322142215Sglebius int s; 323142215Sglebius 324142914Sglebius if (sc->sc_carpdev) 325142914Sglebius CARP_SCLOCK_ASSERT(sc); 326142914Sglebius 327142215Sglebius s = splnet(); 328142215Sglebius TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { 329142914Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 330142914Sglebius sc->sc_carpdev != NULL) { 331142215Sglebius int count = carp_addrcount( 332142564Sglebius (struct carp_if *)sc->sc_carpdev->if_carp, 333142215Sglebius ifatoia(ifa), CARP_COUNT_MASTER); 334142215Sglebius 335142215Sglebius if ((cmd == RTM_ADD && count == 1) || 336142215Sglebius (cmd == RTM_DELETE && count == 0)) 337142215Sglebius rtinit(ifa, cmd, RTF_UP | RTF_HOST); 338142215Sglebius } 339142215Sglebius#ifdef INET6 340142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET6) { 341142215Sglebius if (cmd == RTM_ADD) 342142215Sglebius in6_ifaddloop(ifa); 343142215Sglebius else 344142215Sglebius in6_ifremloop(ifa); 345142215Sglebius } 346142215Sglebius#endif /* INET6 */ 347142215Sglebius } 348142215Sglebius splx(s); 349142215Sglebius} 350142215Sglebius 351142559Sglebiusstatic int 352142215Sglebiuscarp_clone_create(struct if_clone *ifc, int unit) 353142215Sglebius{ 354142215Sglebius 355142215Sglebius struct carp_softc *sc; 356142215Sglebius struct ifnet *ifp; 357142215Sglebius 358142215Sglebius MALLOC(sc, struct carp_softc *, sizeof(*sc), M_CARP, M_WAITOK|M_ZERO); 359142215Sglebius 360142215Sglebius sc->sc_flags_backup = 0; 361142215Sglebius sc->sc_suppress = 0; 362142215Sglebius sc->sc_advbase = CARP_DFLTINTV; 363142215Sglebius sc->sc_vhid = -1; /* required setting */ 364142215Sglebius sc->sc_advskew = 0; 365142215Sglebius sc->sc_init_counter = 1; 366142215Sglebius sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */ 367142215Sglebius#ifdef INET6 368142215Sglebius sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; 369142215Sglebius#endif 370142215Sglebius 371142914Sglebius callout_init(&sc->sc_ad_tmo, NET_CALLOUT_MPSAFE); 372142914Sglebius callout_init(&sc->sc_md_tmo, NET_CALLOUT_MPSAFE); 373142914Sglebius callout_init(&sc->sc_md6_tmo, NET_CALLOUT_MPSAFE); 374142215Sglebius 375142215Sglebius ifp = &sc->sc_if; 376142215Sglebius ifp->if_softc = sc; 377142215Sglebius if_initname(ifp, CARP_IFNAME, unit); 378142215Sglebius ifp->if_mtu = ETHERMTU; 379142215Sglebius ifp->if_flags = 0; 380142215Sglebius ifp->if_ioctl = carp_ioctl; 381142215Sglebius ifp->if_output = carp_looutput; 382142215Sglebius ifp->if_start = carp_start; 383142215Sglebius ifp->if_type = IFT_CARP; 384142215Sglebius ifp->if_snd.ifq_maxlen = ifqmaxlen; 385142215Sglebius ifp->if_hdrlen = 0; 386142215Sglebius if_attach(ifp); 387142911Sglebius bpfattach(&sc->sc_if, DLT_NULL, sizeof(u_int32_t)); 388142911Sglebius mtx_lock(&carp_mtx); 389142215Sglebius LIST_INSERT_HEAD(&carpif_list, sc, sc_next); 390142911Sglebius mtx_unlock(&carp_mtx); 391142215Sglebius return (0); 392142215Sglebius} 393142215Sglebius 394142559Sglebiusstatic void 395142215Sglebiuscarp_clone_destroy(struct ifnet *ifp) 396142215Sglebius{ 397142215Sglebius struct carp_softc *sc = ifp->if_softc; 398142215Sglebius struct carp_if *cif; 399142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 400142215Sglebius#ifdef INET6 401142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 402142215Sglebius#endif 403142215Sglebius 404142215Sglebius/* carpdetach(sc); */ 405142215Sglebius 406142215Sglebius callout_stop(&sc->sc_ad_tmo); 407142215Sglebius callout_stop(&sc->sc_md_tmo); 408142215Sglebius callout_stop(&sc->sc_md6_tmo); 409142215Sglebius 410142215Sglebius if (imo->imo_num_memberships) { 411142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 412142215Sglebius imo->imo_multicast_ifp = NULL; 413142215Sglebius } 414142215Sglebius#ifdef INET6 415142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 416142215Sglebius struct in6_multi_mship *imm = 417142215Sglebius LIST_FIRST(&im6o->im6o_memberships); 418142215Sglebius LIST_REMOVE(imm, i6mm_chain); 419142215Sglebius in6_leavegroup(imm); 420142215Sglebius } 421142215Sglebius im6o->im6o_multicast_ifp = NULL; 422142215Sglebius#endif 423142215Sglebius 424142215Sglebius /* Remove ourself from parents if_carp queue */ 425142564Sglebius if (sc->sc_carpdev && (cif = sc->sc_carpdev->if_carp)) { 426142215Sglebius CARP_LOCK(cif); 427142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 428142215Sglebius if (!--cif->vhif_nvrs) { 429142914Sglebius callout_drain(&cif->cif_tmo); 430142564Sglebius sc->sc_carpdev->if_carp = NULL; 431142215Sglebius CARP_LOCK_DESTROY(cif); 432142215Sglebius FREE(cif, M_CARP); 433142564Sglebius ifpromisc(sc->sc_carpdev, 0); 434142215Sglebius } else { 435142215Sglebius CARP_UNLOCK(cif); 436142215Sglebius } 437142215Sglebius } 438142215Sglebius 439142911Sglebius mtx_lock(&carp_mtx); 440142911Sglebius LIST_REMOVE(sc, sc_next); 441142911Sglebius mtx_unlock(&carp_mtx); 442142215Sglebius bpfdetach(ifp); 443142215Sglebius if_detach(ifp); 444142215Sglebius free(sc, M_CARP); 445142215Sglebius} 446142215Sglebius 447142215Sglebius/* 448142215Sglebius * process input packet. 449142215Sglebius * we have rearranged checks order compared to the rfc, 450142215Sglebius * but it seems more efficient this way or not possible otherwise. 451142215Sglebius */ 452142215Sglebiusvoid 453142215Sglebiuscarp_input(struct mbuf *m, int hlen) 454142215Sglebius{ 455142215Sglebius struct ip *ip = mtod(m, struct ip *); 456142215Sglebius struct carp_header *ch; 457142215Sglebius int iplen, len; 458142215Sglebius 459142215Sglebius carpstats.carps_ipackets++; 460142215Sglebius 461142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 462142215Sglebius m_freem(m); 463142215Sglebius return; 464142215Sglebius } 465142215Sglebius 466142215Sglebius /* check if received on a valid carp interface */ 467142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 468142215Sglebius carpstats.carps_badif++; 469142452Sglebius CARP_LOG("carp_input: packet received on non-carp " 470142452Sglebius "interface: %s\n", 471142446Sglebius m->m_pkthdr.rcvif->if_xname); 472142215Sglebius m_freem(m); 473142215Sglebius return; 474142215Sglebius } 475142215Sglebius 476142215Sglebius /* verify that the IP TTL is 255. */ 477142215Sglebius if (ip->ip_ttl != CARP_DFLTTL) { 478142215Sglebius carpstats.carps_badttl++; 479142452Sglebius CARP_LOG("carp_input: received ttl %d != 255i on %s\n", 480142446Sglebius ip->ip_ttl, 481142446Sglebius m->m_pkthdr.rcvif->if_xname); 482142215Sglebius m_freem(m); 483142215Sglebius return; 484142215Sglebius } 485142215Sglebius 486142215Sglebius iplen = ip->ip_hl << 2; 487142215Sglebius 488142215Sglebius if (m->m_pkthdr.len < iplen + sizeof(*ch)) { 489142215Sglebius carpstats.carps_badlen++; 490142446Sglebius CARP_LOG("carp_input: received len %zd < " 491142452Sglebius "sizeof(struct carp_header)\n", 492142446Sglebius m->m_len - sizeof(struct ip)); 493142215Sglebius m_freem(m); 494142215Sglebius return; 495142215Sglebius } 496142215Sglebius 497142215Sglebius if (iplen + sizeof(*ch) < m->m_len) { 498142215Sglebius if ((m = m_pullup(m, iplen + sizeof(*ch))) == NULL) { 499142215Sglebius carpstats.carps_hdrops++; 500142452Sglebius CARP_LOG("carp_input: pullup failed\n"); 501142215Sglebius return; 502142215Sglebius } 503142215Sglebius ip = mtod(m, struct ip *); 504142215Sglebius } 505142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 506142215Sglebius 507142215Sglebius /* 508142215Sglebius * verify that the received packet length is 509142215Sglebius * equal to the CARP header 510142215Sglebius */ 511142215Sglebius len = iplen + sizeof(*ch); 512142215Sglebius if (len > m->m_pkthdr.len) { 513142215Sglebius carpstats.carps_badlen++; 514142452Sglebius CARP_LOG("carp_input: packet too short %d on %s\n", 515142446Sglebius m->m_pkthdr.len, 516142446Sglebius m->m_pkthdr.rcvif->if_xname); 517142215Sglebius m_freem(m); 518142215Sglebius return; 519142215Sglebius } 520142215Sglebius 521142215Sglebius if ((m = m_pullup(m, len)) == NULL) { 522142215Sglebius carpstats.carps_hdrops++; 523142215Sglebius return; 524142215Sglebius } 525142215Sglebius ip = mtod(m, struct ip *); 526142215Sglebius ch = (struct carp_header *)((char *)ip + iplen); 527142215Sglebius 528142215Sglebius /* verify the CARP checksum */ 529142215Sglebius m->m_data += iplen; 530142215Sglebius if (carp_cksum(m, len - iplen)) { 531142215Sglebius carpstats.carps_badsum++; 532142452Sglebius CARP_LOG("carp_input: checksum failed on %s\n", 533142446Sglebius m->m_pkthdr.rcvif->if_xname); 534142215Sglebius m_freem(m); 535142215Sglebius return; 536142215Sglebius } 537142215Sglebius m->m_data -= iplen; 538142215Sglebius 539142446Sglebius carp_input_c(m, ch, AF_INET); 540142215Sglebius} 541142215Sglebius 542142215Sglebius#ifdef INET6 543142215Sglebiusint 544142215Sglebiuscarp6_input(struct mbuf **mp, int *offp, int proto) 545142215Sglebius{ 546142215Sglebius struct mbuf *m = *mp; 547142215Sglebius struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 548142215Sglebius struct carp_header *ch; 549142215Sglebius u_int len; 550142215Sglebius 551142215Sglebius carpstats.carps_ipackets6++; 552142215Sglebius 553142215Sglebius if (!carp_opts[CARPCTL_ALLOW]) { 554142215Sglebius m_freem(m); 555142215Sglebius return (IPPROTO_DONE); 556142215Sglebius } 557142215Sglebius 558142215Sglebius /* check if received on a valid carp interface */ 559142215Sglebius if (m->m_pkthdr.rcvif->if_carp == NULL) { 560142215Sglebius carpstats.carps_badif++; 561142446Sglebius CARP_LOG("carp6_input: packet received on non-carp " 562142452Sglebius "interface: %s\n", 563142446Sglebius m->m_pkthdr.rcvif->if_xname); 564142215Sglebius m_freem(m); 565142215Sglebius return (IPPROTO_DONE); 566142215Sglebius } 567142215Sglebius 568142215Sglebius /* verify that the IP TTL is 255 */ 569142215Sglebius if (ip6->ip6_hlim != CARP_DFLTTL) { 570142215Sglebius carpstats.carps_badttl++; 571142452Sglebius CARP_LOG("carp6_input: received ttl %d != 255 on %s\n", 572142446Sglebius ip6->ip6_hlim, 573142446Sglebius m->m_pkthdr.rcvif->if_xname); 574142215Sglebius m_freem(m); 575142215Sglebius return (IPPROTO_DONE); 576142215Sglebius } 577142215Sglebius 578142215Sglebius /* verify that we have a complete carp packet */ 579142215Sglebius len = m->m_len; 580142215Sglebius IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch)); 581142215Sglebius if (ch == NULL) { 582142215Sglebius carpstats.carps_badlen++; 583143804Sglebius CARP_LOG("carp6_input: packet size %u too small\n", len); 584142215Sglebius return (IPPROTO_DONE); 585142215Sglebius } 586142215Sglebius 587142215Sglebius 588142215Sglebius /* verify the CARP checksum */ 589142215Sglebius m->m_data += *offp; 590142215Sglebius if (carp_cksum(m, sizeof(*ch))) { 591142215Sglebius carpstats.carps_badsum++; 592142452Sglebius CARP_LOG("carp6_input: checksum failed, on %s\n", 593142446Sglebius m->m_pkthdr.rcvif->if_xname); 594142215Sglebius m_freem(m); 595142215Sglebius return (IPPROTO_DONE); 596142215Sglebius } 597142215Sglebius m->m_data -= *offp; 598142215Sglebius 599142446Sglebius carp_input_c(m, ch, AF_INET6); 600142215Sglebius return (IPPROTO_DONE); 601142215Sglebius} 602142215Sglebius#endif /* INET6 */ 603142215Sglebius 604142559Sglebiusstatic void 605142446Sglebiuscarp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) 606142215Sglebius{ 607142215Sglebius struct ifnet *ifp = m->m_pkthdr.rcvif; 608142446Sglebius struct carp_softc *sc; 609142215Sglebius u_int64_t tmp_counter; 610142215Sglebius struct timeval sc_tv, ch_tv; 611142215Sglebius 612142215Sglebius /* verify that the VHID is valid on the receiving interface */ 613142215Sglebius CARP_LOCK(ifp->if_carp); 614142215Sglebius TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list) 615142215Sglebius if (sc->sc_vhid == ch->carp_vhid) 616142215Sglebius break; 617142914Sglebius 618142215Sglebius if (!sc || (sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != 619142215Sglebius (IFF_UP|IFF_RUNNING)) { 620142215Sglebius carpstats.carps_badvhid++; 621142914Sglebius CARP_UNLOCK(ifp->if_carp); 622142215Sglebius m_freem(m); 623142215Sglebius return; 624142215Sglebius } 625142215Sglebius 626142215Sglebius getmicrotime(&sc->sc_if.if_lastchange); 627142215Sglebius sc->sc_if.if_ipackets++; 628142215Sglebius sc->sc_if.if_ibytes += m->m_pkthdr.len; 629142215Sglebius 630142215Sglebius if (sc->sc_if.if_bpf) { 631142215Sglebius struct ip *ip = mtod(m, struct ip *); 632142784Sglebius uint32_t af1 = af; 633142215Sglebius 634142215Sglebius /* BPF wants net byte order */ 635142784Sglebius ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2)); 636142784Sglebius ip->ip_off = htons(ip->ip_off); 637142784Sglebius bpf_mtap2(sc->sc_if.if_bpf, &af1, sizeof(af1), m); 638142215Sglebius } 639142215Sglebius 640142215Sglebius /* verify the CARP version. */ 641142215Sglebius if (ch->carp_version != CARP_VERSION) { 642142215Sglebius carpstats.carps_badver++; 643142215Sglebius sc->sc_if.if_ierrors++; 644142914Sglebius CARP_UNLOCK(ifp->if_carp); 645142452Sglebius CARP_LOG("%s; invalid version %d\n", 646142446Sglebius sc->sc_if.if_xname, 647142446Sglebius ch->carp_version); 648142215Sglebius m_freem(m); 649142215Sglebius return; 650142215Sglebius } 651142215Sglebius 652142215Sglebius /* verify the hash */ 653142215Sglebius if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { 654142215Sglebius carpstats.carps_badauth++; 655142215Sglebius sc->sc_if.if_ierrors++; 656142914Sglebius CARP_UNLOCK(ifp->if_carp); 657142452Sglebius CARP_LOG("%s: incorrect hash\n", sc->sc_if.if_xname); 658142215Sglebius m_freem(m); 659142215Sglebius return; 660142215Sglebius } 661142215Sglebius 662142215Sglebius tmp_counter = ntohl(ch->carp_counter[0]); 663142215Sglebius tmp_counter = tmp_counter<<32; 664142215Sglebius tmp_counter += ntohl(ch->carp_counter[1]); 665142215Sglebius 666142215Sglebius /* XXX Replay protection goes here */ 667142215Sglebius 668142215Sglebius sc->sc_init_counter = 0; 669142215Sglebius sc->sc_counter = tmp_counter; 670142215Sglebius 671142215Sglebius sc_tv.tv_sec = sc->sc_advbase; 672142215Sglebius if (carp_suppress_preempt && sc->sc_advskew < 240) 673142215Sglebius sc_tv.tv_usec = 240 * 1000000 / 256; 674142215Sglebius else 675142215Sglebius sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; 676142215Sglebius ch_tv.tv_sec = ch->carp_advbase; 677142215Sglebius ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; 678142215Sglebius 679142215Sglebius switch (sc->sc_state) { 680142215Sglebius case INIT: 681142215Sglebius break; 682142215Sglebius case MASTER: 683142215Sglebius /* 684142215Sglebius * If we receive an advertisement from a master who's going to 685142215Sglebius * be more frequent than us, go into BACKUP state. 686142215Sglebius */ 687142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, >) || 688142215Sglebius timevalcmp(&sc_tv, &ch_tv, ==)) { 689142215Sglebius callout_stop(&sc->sc_ad_tmo); 690142446Sglebius CARP_DEBUG("%s: MASTER -> BACKUP " 691142452Sglebius "(more frequent advertisement received)\n", 692142446Sglebius sc->sc_if.if_xname); 693142215Sglebius carp_set_state(sc, BACKUP); 694142215Sglebius carp_setrun(sc, 0); 695142215Sglebius carp_setroute(sc, RTM_DELETE); 696142215Sglebius } 697142215Sglebius break; 698142215Sglebius case BACKUP: 699142215Sglebius /* 700142215Sglebius * If we're pre-empting masters who advertise slower than us, 701142215Sglebius * and this one claims to be slower, treat him as down. 702142215Sglebius */ 703142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && 704142215Sglebius timevalcmp(&sc_tv, &ch_tv, <)) { 705142446Sglebius CARP_DEBUG("%s: BACKUP -> MASTER " 706142452Sglebius "(preempting a slower master)\n", 707142446Sglebius sc->sc_if.if_xname); 708142914Sglebius carp_master_down_locked(sc); 709142215Sglebius break; 710142215Sglebius } 711142215Sglebius 712142215Sglebius /* 713142215Sglebius * If the master is going to advertise at such a low frequency 714142215Sglebius * that he's guaranteed to time out, we'd might as well just 715142215Sglebius * treat him as timed out now. 716142215Sglebius */ 717142215Sglebius sc_tv.tv_sec = sc->sc_advbase * 3; 718142215Sglebius if (timevalcmp(&sc_tv, &ch_tv, <)) { 719142446Sglebius CARP_DEBUG("%s: BACKUP -> MASTER " 720142452Sglebius "(master timed out)\n", 721142446Sglebius sc->sc_if.if_xname); 722142914Sglebius carp_master_down_locked(sc); 723142215Sglebius break; 724142215Sglebius } 725142215Sglebius 726142215Sglebius /* 727142215Sglebius * Otherwise, we reset the counter and wait for the next 728142215Sglebius * advertisement. 729142215Sglebius */ 730142215Sglebius carp_setrun(sc, af); 731142215Sglebius break; 732142215Sglebius } 733142215Sglebius 734142914Sglebius CARP_UNLOCK(ifp->if_carp); 735142914Sglebius 736142215Sglebius m_freem(m); 737142215Sglebius return; 738142215Sglebius} 739142215Sglebius 740142559Sglebiusstatic void 741142215Sglebiuscarpdetach(struct carp_softc *sc) 742142215Sglebius{ 743142215Sglebius struct ifaddr *ifa; 744142215Sglebius 745142215Sglebius callout_stop(&sc->sc_ad_tmo); 746142215Sglebius callout_stop(&sc->sc_md_tmo); 747142215Sglebius callout_stop(&sc->sc_md6_tmo); 748142215Sglebius 749142215Sglebius while ((ifa = TAILQ_FIRST(&sc->sc_if.if_addrlist)) != NULL) 750142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET) { 751142215Sglebius struct in_ifaddr *ia = ifatoia(ifa); 752142215Sglebius 753142215Sglebius carp_del_addr(sc, &ia->ia_addr); 754142215Sglebius 755142215Sglebius /* ripped screaming from in_control(SIOCDIFADDR) */ 756142215Sglebius in_ifscrub(&sc->sc_if, ia); 757142215Sglebius TAILQ_REMOVE(&sc->sc_if.if_addrlist, ifa, ifa_link); 758142215Sglebius TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); 759142215Sglebius IFAFREE((&ia->ia_ifa)); 760142215Sglebius } 761142215Sglebius} 762142215Sglebius 763142215Sglebius/* Detach an interface from the carp. */ 764142215Sglebiusvoid 765142215Sglebiuscarp_ifdetach(struct ifnet *ifp) 766142215Sglebius{ 767142215Sglebius struct carp_softc *sc; 768142215Sglebius struct carp_if *cif = (struct carp_if *)ifp->if_carp; 769142215Sglebius 770142215Sglebius CARP_LOCK(cif); 771142215Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) 772142215Sglebius carpdetach(sc); 773142215Sglebius CARP_UNLOCK(cif); 774142215Sglebius} 775142215Sglebius 776142559Sglebiusstatic int 777142215Sglebiuscarp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) 778142215Sglebius{ 779142215Sglebius struct m_tag *mtag; 780142215Sglebius struct ifnet *ifp = &sc->sc_if; 781142215Sglebius 782142215Sglebius if (sc->sc_init_counter) { 783142215Sglebius /* this could also be seconds since unix epoch */ 784142215Sglebius sc->sc_counter = arc4random(); 785142215Sglebius sc->sc_counter = sc->sc_counter << 32; 786142215Sglebius sc->sc_counter += arc4random(); 787142215Sglebius } else 788142215Sglebius sc->sc_counter++; 789142215Sglebius 790142215Sglebius ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff); 791142215Sglebius ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff); 792142215Sglebius 793142215Sglebius carp_hmac_generate(sc, ch->carp_counter, ch->carp_md); 794142215Sglebius 795142215Sglebius /* Tag packet for carp_output */ 796142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT); 797142215Sglebius if (mtag == NULL) { 798142215Sglebius m_freem(m); 799142215Sglebius sc->sc_if.if_oerrors++; 800142215Sglebius return (ENOMEM); 801142215Sglebius } 802142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 803142215Sglebius m_tag_prepend(m, mtag); 804142215Sglebius 805142215Sglebius return (0); 806142215Sglebius} 807142215Sglebius 808142559Sglebiusstatic void 809142215Sglebiuscarp_send_ad_all(void) 810142215Sglebius{ 811142911Sglebius struct carp_softc *sc; 812142215Sglebius 813142911Sglebius mtx_lock(&carp_mtx); 814142911Sglebius LIST_FOREACH(sc, &carpif_list, sc_next) { 815142911Sglebius if (sc->sc_carpdev == NULL) 816142215Sglebius continue; 817142911Sglebius CARP_SCLOCK(sc); 818142911Sglebius if ((sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) && 819142911Sglebius sc->sc_state == MASTER) 820142914Sglebius carp_send_ad_locked(sc); 821142911Sglebius CARP_SCUNLOCK(sc); 822142215Sglebius } 823142911Sglebius mtx_unlock(&carp_mtx); 824142215Sglebius} 825142215Sglebius 826142559Sglebiusstatic void 827142215Sglebiuscarp_send_ad(void *v) 828142215Sglebius{ 829142914Sglebius struct carp_softc *sc = v; 830142914Sglebius 831142914Sglebius CARP_SCLOCK(sc); 832142914Sglebius carp_send_ad_locked(sc); 833142914Sglebius CARP_SCUNLOCK(sc); 834142914Sglebius} 835142914Sglebius 836142914Sglebiusstatic void 837142914Sglebiuscarp_send_ad_locked(struct carp_softc *sc) 838142914Sglebius{ 839142215Sglebius struct carp_header ch; 840142215Sglebius struct timeval tv; 841142215Sglebius struct carp_header *ch_ptr; 842142215Sglebius struct mbuf *m; 843142215Sglebius int len, advbase, advskew; 844142215Sglebius 845142914Sglebius CARP_SCLOCK_ASSERT(sc); 846142914Sglebius 847142215Sglebius /* bow out if we've lost our UPness or RUNNINGuiness */ 848142215Sglebius if ((sc->sc_if.if_flags & 849142215Sglebius (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 850142215Sglebius advbase = 255; 851142215Sglebius advskew = 255; 852142215Sglebius } else { 853142215Sglebius advbase = sc->sc_advbase; 854142215Sglebius if (!carp_suppress_preempt || sc->sc_advskew > 240) 855142215Sglebius advskew = sc->sc_advskew; 856142215Sglebius else 857142215Sglebius advskew = 240; 858142215Sglebius tv.tv_sec = advbase; 859142215Sglebius tv.tv_usec = advskew * 1000000 / 256; 860142215Sglebius } 861142215Sglebius 862142215Sglebius ch.carp_version = CARP_VERSION; 863142215Sglebius ch.carp_type = CARP_ADVERTISEMENT; 864142215Sglebius ch.carp_vhid = sc->sc_vhid; 865142215Sglebius ch.carp_advbase = advbase; 866142215Sglebius ch.carp_advskew = advskew; 867142215Sglebius ch.carp_authlen = 7; /* XXX DEFINE */ 868142215Sglebius ch.carp_pad1 = 0; /* must be zero */ 869142215Sglebius ch.carp_cksum = 0; 870142215Sglebius 871142215Sglebius#ifdef INET 872142215Sglebius if (sc->sc_ia) { 873142215Sglebius struct ip *ip; 874142215Sglebius 875142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 876142215Sglebius if (m == NULL) { 877142215Sglebius sc->sc_ac.ac_if.if_oerrors++; 878142215Sglebius carpstats.carps_onomem++; 879142215Sglebius /* XXX maybe less ? */ 880142215Sglebius if (advbase != 255 || advskew != 255) 881142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 882142215Sglebius carp_send_ad, sc); 883142215Sglebius return; 884142215Sglebius } 885142215Sglebius len = sizeof(*ip) + sizeof(ch); 886142215Sglebius m->m_pkthdr.len = len; 887142215Sglebius m->m_pkthdr.rcvif = NULL; 888142215Sglebius m->m_len = len; 889142215Sglebius MH_ALIGN(m, m->m_len); 890142215Sglebius m->m_flags |= M_MCAST; 891142215Sglebius ip = mtod(m, struct ip *); 892142215Sglebius ip->ip_v = IPVERSION; 893142215Sglebius ip->ip_hl = sizeof(*ip) >> 2; 894142215Sglebius ip->ip_tos = IPTOS_LOWDELAY; 895142215Sglebius ip->ip_len = len; 896142215Sglebius ip->ip_id = ip_newid(); 897142215Sglebius ip->ip_off = IP_DF; 898142215Sglebius ip->ip_ttl = CARP_DFLTTL; 899142215Sglebius ip->ip_p = IPPROTO_CARP; 900142215Sglebius ip->ip_sum = 0; 901142215Sglebius ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr; 902142215Sglebius ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP); 903142215Sglebius 904142215Sglebius ch_ptr = (struct carp_header *)(&ip[1]); 905142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 906142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 907142215Sglebius return; 908142215Sglebius 909142215Sglebius m->m_data += sizeof(*ip); 910142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip)); 911142215Sglebius m->m_data -= sizeof(*ip); 912142215Sglebius 913142215Sglebius getmicrotime(&sc->sc_if.if_lastchange); 914142215Sglebius sc->sc_ac.ac_if.if_opackets++; 915142215Sglebius sc->sc_ac.ac_if.if_obytes += len; 916142215Sglebius carpstats.carps_opackets++; 917142215Sglebius 918142215Sglebius if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) { 919142215Sglebius sc->sc_if.if_oerrors++; 920142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 921142215Sglebius sc->sc_sendad_errors++; 922142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 923142215Sglebius carp_suppress_preempt++; 924142914Sglebius if (carp_suppress_preempt == 1) { 925142914Sglebius CARP_SCUNLOCK(sc); 926142215Sglebius carp_send_ad_all(); 927142914Sglebius CARP_SCLOCK(sc); 928142914Sglebius } 929142215Sglebius } 930142215Sglebius sc->sc_sendad_success = 0; 931142215Sglebius } else { 932142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 933142215Sglebius if (++sc->sc_sendad_success >= 934142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 935142215Sglebius carp_suppress_preempt--; 936142215Sglebius sc->sc_sendad_errors = 0; 937142215Sglebius } 938142215Sglebius } else 939142215Sglebius sc->sc_sendad_errors = 0; 940142215Sglebius } 941142215Sglebius } 942142215Sglebius#endif /* INET */ 943142215Sglebius#ifdef INET6 944142215Sglebius if (sc->sc_ia6) { 945142215Sglebius struct ip6_hdr *ip6; 946142215Sglebius 947142215Sglebius MGETHDR(m, M_DONTWAIT, MT_HEADER); 948142215Sglebius if (m == NULL) { 949142215Sglebius sc->sc_ac.ac_if.if_oerrors++; 950142215Sglebius carpstats.carps_onomem++; 951142215Sglebius /* XXX maybe less ? */ 952142215Sglebius if (advbase != 255 || advskew != 255) 953142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 954142215Sglebius carp_send_ad, sc); 955142215Sglebius return; 956142215Sglebius } 957142215Sglebius len = sizeof(*ip6) + sizeof(ch); 958142215Sglebius m->m_pkthdr.len = len; 959142215Sglebius m->m_pkthdr.rcvif = NULL; 960142215Sglebius m->m_len = len; 961142215Sglebius MH_ALIGN(m, m->m_len); 962142215Sglebius m->m_flags |= M_MCAST; 963142215Sglebius ip6 = mtod(m, struct ip6_hdr *); 964142215Sglebius bzero(ip6, sizeof(*ip6)); 965142215Sglebius ip6->ip6_vfc |= IPV6_VERSION; 966142215Sglebius ip6->ip6_hlim = CARP_DFLTTL; 967142215Sglebius ip6->ip6_nxt = IPPROTO_CARP; 968142215Sglebius bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src, 969142215Sglebius sizeof(struct in6_addr)); 970142215Sglebius /* set the multicast destination */ 971142215Sglebius 972142215Sglebius ip6->ip6_dst.s6_addr8[0] = 0xff; 973142215Sglebius ip6->ip6_dst.s6_addr8[1] = 0x02; 974142215Sglebius ip6->ip6_dst.s6_addr8[15] = 0x12; 975142215Sglebius 976142215Sglebius ch_ptr = (struct carp_header *)(&ip6[1]); 977142215Sglebius bcopy(&ch, ch_ptr, sizeof(ch)); 978142215Sglebius if (carp_prepare_ad(m, sc, ch_ptr)) 979142215Sglebius return; 980142215Sglebius 981142215Sglebius m->m_data += sizeof(*ip6); 982142215Sglebius ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6)); 983142215Sglebius m->m_data -= sizeof(*ip6); 984142215Sglebius 985142215Sglebius getmicrotime(&sc->sc_if.if_lastchange); 986142215Sglebius sc->sc_if.if_opackets++; 987142215Sglebius sc->sc_if.if_obytes += len; 988142215Sglebius carpstats.carps_opackets6++; 989142215Sglebius 990142215Sglebius if (ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL)) { 991142215Sglebius sc->sc_if.if_oerrors++; 992142215Sglebius if (sc->sc_sendad_errors < INT_MAX) 993142215Sglebius sc->sc_sendad_errors++; 994142215Sglebius if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { 995142215Sglebius carp_suppress_preempt++; 996142914Sglebius if (carp_suppress_preempt == 1) { 997142914Sglebius CARP_SCUNLOCK(sc); 998142215Sglebius carp_send_ad_all(); 999142914Sglebius CARP_SCLOCK(sc); 1000142914Sglebius } 1001142215Sglebius } 1002142215Sglebius sc->sc_sendad_success = 0; 1003142215Sglebius } else { 1004142215Sglebius if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { 1005142215Sglebius if (++sc->sc_sendad_success >= 1006142215Sglebius CARP_SENDAD_MIN_SUCCESS) { 1007142215Sglebius carp_suppress_preempt--; 1008142215Sglebius sc->sc_sendad_errors = 0; 1009142215Sglebius } 1010142215Sglebius } else 1011142215Sglebius sc->sc_sendad_errors = 0; 1012142215Sglebius } 1013142215Sglebius } 1014142215Sglebius#endif /* INET6 */ 1015142215Sglebius 1016142215Sglebius if (advbase != 255 || advskew != 255) 1017142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1018142215Sglebius carp_send_ad, sc); 1019142215Sglebius 1020142215Sglebius} 1021142215Sglebius 1022142215Sglebius/* 1023142215Sglebius * Broadcast a gratuitous ARP request containing 1024142215Sglebius * the virtual router MAC address for each IP address 1025142215Sglebius * associated with the virtual router. 1026142215Sglebius */ 1027142559Sglebiusstatic void 1028142215Sglebiuscarp_send_arp(struct carp_softc *sc) 1029142215Sglebius{ 1030142215Sglebius struct ifaddr *ifa; 1031142215Sglebius 1032142215Sglebius TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { 1033142215Sglebius 1034142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET) 1035142215Sglebius continue; 1036142215Sglebius 1037142564Sglebius/* arprequest(sc->sc_carpdev, &in, &in, sc->sc_ac.ac_enaddr); */ 1038142564Sglebius arp_ifinit2(sc->sc_carpdev, ifa, sc->sc_ac.ac_enaddr); 1039142215Sglebius 1040142215Sglebius DELAY(1000); /* XXX */ 1041142215Sglebius } 1042142215Sglebius} 1043142215Sglebius 1044142215Sglebius#ifdef INET6 1045142559Sglebiusstatic void 1046142215Sglebiuscarp_send_na(struct carp_softc *sc) 1047142215Sglebius{ 1048142215Sglebius struct ifaddr *ifa; 1049142215Sglebius struct in6_addr *in6; 1050142215Sglebius static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; 1051142215Sglebius 1052142215Sglebius TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { 1053142215Sglebius 1054142215Sglebius if (ifa->ifa_addr->sa_family != AF_INET6) 1055142215Sglebius continue; 1056142215Sglebius 1057142215Sglebius in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; 1058142564Sglebius nd6_na_output(sc->sc_carpdev, &mcast, in6, 1059142215Sglebius ND_NA_FLAG_OVERRIDE, 1, NULL); 1060142215Sglebius DELAY(1000); /* XXX */ 1061142215Sglebius } 1062142215Sglebius} 1063142215Sglebius#endif /* INET6 */ 1064142215Sglebius 1065142559Sglebiusstatic int 1066142215Sglebiuscarp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) 1067142215Sglebius{ 1068142215Sglebius struct carp_softc *vh; 1069142215Sglebius struct ifaddr *ifa; 1070142215Sglebius int count = 0; 1071142215Sglebius 1072142914Sglebius CARP_LOCK_ASSERT(cif); 1073142914Sglebius 1074142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1075142215Sglebius if ((type == CARP_COUNT_RUNNING && 1076142215Sglebius (vh->sc_ac.ac_if.if_flags & (IFF_UP|IFF_RUNNING)) == 1077142215Sglebius (IFF_UP|IFF_RUNNING)) || 1078142215Sglebius (type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) { 1079142215Sglebius TAILQ_FOREACH(ifa, &vh->sc_ac.ac_if.if_addrlist, 1080142215Sglebius ifa_list) { 1081142215Sglebius if (ifa->ifa_addr->sa_family == AF_INET && 1082142215Sglebius ia->ia_addr.sin_addr.s_addr == 1083142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) 1084142215Sglebius count++; 1085142215Sglebius } 1086142215Sglebius } 1087142215Sglebius } 1088142215Sglebius return (count); 1089142215Sglebius} 1090142215Sglebius 1091142215Sglebiusint 1092142215Sglebiuscarp_iamatch(void *v, struct in_ifaddr *ia, 1093142215Sglebius struct in_addr *isaddr, u_int8_t **enaddr) 1094142215Sglebius{ 1095142215Sglebius struct carp_if *cif = v; 1096142215Sglebius struct carp_softc *vh; 1097142215Sglebius int index, count = 0; 1098142215Sglebius struct ifaddr *ifa; 1099142215Sglebius 1100142215Sglebius CARP_LOCK(cif); 1101142215Sglebius 1102142215Sglebius if (carp_opts[CARPCTL_ARPBALANCE]) { 1103142215Sglebius /* 1104142215Sglebius * XXX proof of concept implementation. 1105142215Sglebius * We use the source ip to decide which virtual host should 1106142215Sglebius * handle the request. If we're master of that virtual host, 1107142215Sglebius * then we respond, otherwise, just drop the arp packet on 1108142215Sglebius * the floor. 1109142215Sglebius */ 1110142215Sglebius count = carp_addrcount(cif, ia, CARP_COUNT_RUNNING); 1111142215Sglebius if (count == 0) { 1112142215Sglebius /* should never reach this */ 1113142215Sglebius CARP_UNLOCK(cif); 1114142215Sglebius return (0); 1115142215Sglebius } 1116142215Sglebius 1117142215Sglebius /* this should be a hash, like pf_hash() */ 1118142215Sglebius index = isaddr->s_addr % count; 1119142215Sglebius count = 0; 1120142215Sglebius 1121142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1122142215Sglebius if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == 1123142215Sglebius (IFF_UP|IFF_RUNNING)) { 1124142215Sglebius TAILQ_FOREACH(ifa, &vh->sc_if.if_addrlist, 1125142215Sglebius ifa_list) { 1126142215Sglebius if (ifa->ifa_addr->sa_family == 1127142215Sglebius AF_INET && 1128142215Sglebius ia->ia_addr.sin_addr.s_addr == 1129142215Sglebius ifatoia(ifa)->ia_addr.sin_addr.s_addr) { 1130142215Sglebius if (count == index) { 1131142215Sglebius if (vh->sc_state == 1132142215Sglebius MASTER) { 1133142215Sglebius *enaddr = vh->sc_ac.ac_enaddr; 1134142215Sglebius CARP_UNLOCK(cif); 1135142215Sglebius return (1); 1136142215Sglebius } else { 1137142215Sglebius CARP_UNLOCK(cif); 1138142215Sglebius return (0); 1139142215Sglebius } 1140142215Sglebius } 1141142215Sglebius count++; 1142142215Sglebius } 1143142215Sglebius } 1144142215Sglebius } 1145142215Sglebius } 1146142215Sglebius } else { 1147142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1148142215Sglebius if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == 1149142215Sglebius (IFF_UP|IFF_RUNNING) && ia->ia_ifp == 1150142215Sglebius &vh->sc_if) { 1151142215Sglebius *enaddr = vh->sc_ac.ac_enaddr; 1152142215Sglebius CARP_UNLOCK(cif); 1153142215Sglebius return (1); 1154142215Sglebius } 1155142215Sglebius } 1156142215Sglebius } 1157142215Sglebius CARP_UNLOCK(cif); 1158142215Sglebius return (0); 1159142215Sglebius} 1160142215Sglebius 1161142215Sglebius#ifdef INET6 1162142641Smlaierstruct ifaddr * 1163142215Sglebiuscarp_iamatch6(void *v, struct in6_addr *taddr) 1164142215Sglebius{ 1165142215Sglebius struct carp_if *cif = v; 1166142215Sglebius struct carp_softc *vh; 1167142215Sglebius struct ifaddr *ifa; 1168142215Sglebius 1169142215Sglebius CARP_LOCK(cif); 1170142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { 1171142215Sglebius TAILQ_FOREACH(ifa, &vh->sc_if.if_addrlist, ifa_list) { 1172142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1173142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1174142215Sglebius ((vh->sc_if.if_flags & 1175142215Sglebius (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING))) { 1176142215Sglebius CARP_UNLOCK(cif); 1177142215Sglebius return (ifa); 1178142215Sglebius } 1179142215Sglebius } 1180142215Sglebius } 1181142215Sglebius CARP_UNLOCK(cif); 1182142215Sglebius 1183142215Sglebius return (NULL); 1184142215Sglebius} 1185142215Sglebius 1186142641Smlaiervoid * 1187142215Sglebiuscarp_macmatch6(void *v, struct mbuf *m, const struct in6_addr *taddr) 1188142215Sglebius{ 1189142215Sglebius struct m_tag *mtag; 1190142215Sglebius struct carp_if *cif = v; 1191142215Sglebius struct carp_softc *sc; 1192142215Sglebius struct ifaddr *ifa; 1193142215Sglebius 1194142215Sglebius CARP_LOCK(cif); 1195142215Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { 1196142215Sglebius TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { 1197142215Sglebius if (IN6_ARE_ADDR_EQUAL(taddr, 1198142215Sglebius &ifatoia6(ifa)->ia_addr.sin6_addr) && 1199142215Sglebius ((sc->sc_if.if_flags & 1200142215Sglebius (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING))) { 1201142215Sglebius struct ifnet *ifp = &sc->sc_if; 1202142215Sglebius mtag = m_tag_get(PACKET_TAG_CARP, 1203142215Sglebius sizeof(struct ifnet *), M_NOWAIT); 1204142215Sglebius if (mtag == NULL) { 1205142215Sglebius /* better a bit than nothing */ 1206142215Sglebius CARP_UNLOCK(cif); 1207142215Sglebius return (sc->sc_ac.ac_enaddr); 1208142215Sglebius } 1209142215Sglebius bcopy(&ifp, (caddr_t)(mtag + 1), 1210142215Sglebius sizeof(struct ifnet *)); 1211142215Sglebius m_tag_prepend(m, mtag); 1212142215Sglebius 1213142215Sglebius CARP_UNLOCK(cif); 1214142215Sglebius return (sc->sc_ac.ac_enaddr); 1215142215Sglebius } 1216142215Sglebius } 1217142215Sglebius } 1218142215Sglebius CARP_UNLOCK(cif); 1219142215Sglebius 1220142215Sglebius return (NULL); 1221142215Sglebius} 1222142215Sglebius#endif 1223142215Sglebius 1224142215Sglebiusstruct ifnet * 1225142215Sglebiuscarp_forus(void *v, void *dhost) 1226142215Sglebius{ 1227142215Sglebius struct carp_if *cif = v; 1228142215Sglebius struct carp_softc *vh; 1229142215Sglebius u_int8_t *ena = dhost; 1230142215Sglebius 1231142215Sglebius if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) 1232142215Sglebius return (NULL); 1233142215Sglebius 1234142215Sglebius CARP_LOCK(cif); 1235142215Sglebius TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) 1236142215Sglebius if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == 1237142215Sglebius (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER && 1238142215Sglebius !bcmp(dhost, vh->sc_ac.ac_enaddr, ETHER_ADDR_LEN)) { 1239142215Sglebius CARP_UNLOCK(cif); 1240142215Sglebius return (&vh->sc_if); 1241142215Sglebius } 1242142215Sglebius 1243142215Sglebius CARP_UNLOCK(cif); 1244142215Sglebius return (NULL); 1245142215Sglebius} 1246142215Sglebius 1247142559Sglebiusstatic void 1248142215Sglebiuscarp_master_down(void *v) 1249142215Sglebius{ 1250142215Sglebius struct carp_softc *sc = v; 1251142215Sglebius 1252142914Sglebius CARP_SCLOCK(sc); 1253142914Sglebius carp_master_down_locked(sc); 1254142914Sglebius CARP_SCUNLOCK(sc); 1255142914Sglebius} 1256142914Sglebius 1257142914Sglebiusstatic void 1258142914Sglebiuscarp_master_down_locked(struct carp_softc *sc) 1259142914Sglebius{ 1260142914Sglebius if (sc->sc_carpdev) 1261142914Sglebius CARP_SCLOCK_ASSERT(sc); 1262142914Sglebius 1263142215Sglebius switch (sc->sc_state) { 1264142215Sglebius case INIT: 1265142215Sglebius printf("%s: master_down event in INIT state\n", 1266142215Sglebius sc->sc_if.if_xname); 1267142215Sglebius break; 1268142215Sglebius case MASTER: 1269142215Sglebius break; 1270142215Sglebius case BACKUP: 1271142215Sglebius carp_set_state(sc, MASTER); 1272142914Sglebius carp_send_ad_locked(sc); 1273142215Sglebius carp_send_arp(sc); 1274142215Sglebius#ifdef INET6 1275142215Sglebius carp_send_na(sc); 1276142215Sglebius#endif /* INET6 */ 1277142215Sglebius carp_setrun(sc, 0); 1278142215Sglebius carp_setroute(sc, RTM_ADD); 1279142215Sglebius break; 1280142215Sglebius } 1281142215Sglebius} 1282142215Sglebius 1283142215Sglebius/* 1284142215Sglebius * When in backup state, af indicates whether to reset the master down timer 1285142215Sglebius * for v4 or v6. If it's set to zero, reset the ones which are already pending. 1286142215Sglebius */ 1287142559Sglebiusstatic void 1288142215Sglebiuscarp_setrun(struct carp_softc *sc, sa_family_t af) 1289142215Sglebius{ 1290142215Sglebius struct timeval tv; 1291142215Sglebius 1292142914Sglebius if (sc->sc_carpdev) 1293142914Sglebius CARP_SCLOCK_ASSERT(sc); 1294142914Sglebius 1295142215Sglebius if (sc->sc_if.if_flags & IFF_UP && 1296142215Sglebius sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6)) 1297142215Sglebius sc->sc_if.if_flags |= IFF_RUNNING; 1298142215Sglebius else { 1299142215Sglebius sc->sc_if.if_flags &= ~IFF_RUNNING; 1300142215Sglebius carp_setroute(sc, RTM_DELETE); 1301142215Sglebius return; 1302142215Sglebius } 1303142215Sglebius 1304142215Sglebius switch (sc->sc_state) { 1305142215Sglebius case INIT: 1306142215Sglebius if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) { 1307142914Sglebius carp_send_ad_locked(sc); 1308142215Sglebius carp_send_arp(sc); 1309142215Sglebius#ifdef INET6 1310142215Sglebius carp_send_na(sc); 1311142215Sglebius#endif /* INET6 */ 1312142452Sglebius CARP_DEBUG("%s: INIT -> MASTER (preempting)\n", 1313142446Sglebius sc->sc_if.if_xname); 1314142215Sglebius carp_set_state(sc, MASTER); 1315142215Sglebius carp_setroute(sc, RTM_ADD); 1316142215Sglebius } else { 1317142452Sglebius CARP_DEBUG("%s: INIT -> BACKUP\n", sc->sc_if.if_xname); 1318142215Sglebius carp_set_state(sc, BACKUP); 1319142215Sglebius carp_setroute(sc, RTM_DELETE); 1320142215Sglebius carp_setrun(sc, 0); 1321142215Sglebius } 1322142215Sglebius break; 1323142215Sglebius case BACKUP: 1324142215Sglebius callout_stop(&sc->sc_ad_tmo); 1325142215Sglebius tv.tv_sec = 3 * sc->sc_advbase; 1326142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1327142215Sglebius switch (af) { 1328142215Sglebius#ifdef INET 1329142215Sglebius case AF_INET: 1330142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1331142215Sglebius carp_master_down, sc); 1332142215Sglebius break; 1333142215Sglebius#endif /* INET */ 1334142215Sglebius#ifdef INET6 1335142215Sglebius case AF_INET6: 1336142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1337142215Sglebius carp_master_down, sc); 1338142215Sglebius break; 1339142215Sglebius#endif /* INET6 */ 1340142215Sglebius default: 1341142215Sglebius if (sc->sc_naddrs) 1342142215Sglebius callout_reset(&sc->sc_md_tmo, tvtohz(&tv), 1343142215Sglebius carp_master_down, sc); 1344142215Sglebius if (sc->sc_naddrs6) 1345142215Sglebius callout_reset(&sc->sc_md6_tmo, tvtohz(&tv), 1346142215Sglebius carp_master_down, sc); 1347142215Sglebius break; 1348142215Sglebius } 1349142215Sglebius break; 1350142215Sglebius case MASTER: 1351142215Sglebius tv.tv_sec = sc->sc_advbase; 1352142215Sglebius tv.tv_usec = sc->sc_advskew * 1000000 / 256; 1353142215Sglebius callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), 1354142215Sglebius carp_send_ad, sc); 1355142215Sglebius break; 1356142215Sglebius } 1357142215Sglebius} 1358142215Sglebius 1359142559Sglebiusstatic int 1360142215Sglebiuscarp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1361142215Sglebius{ 1362142215Sglebius struct ifnet *ifp; 1363142215Sglebius struct carp_if *cif; 1364142215Sglebius struct in_ifaddr *ia, *ia_if; 1365142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1366142215Sglebius struct in_addr addr; 1367142215Sglebius u_long iaddr = htonl(sin->sin_addr.s_addr); 1368142215Sglebius int own, error; 1369142215Sglebius 1370142215Sglebius if (sin->sin_addr.s_addr == 0) { 1371142215Sglebius if (!(sc->sc_if.if_flags & IFF_UP)) 1372142215Sglebius carp_set_state(sc, INIT); 1373142215Sglebius if (sc->sc_naddrs) 1374142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1375142215Sglebius carp_setrun(sc, 0); 1376142215Sglebius return (0); 1377142215Sglebius } 1378142215Sglebius 1379142215Sglebius /* we have to do it by hands to check we won't match on us */ 1380142215Sglebius ia_if = NULL; own = 0; 1381142215Sglebius TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { 1382142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1383142215Sglebius if (ia->ia_ifp != &sc->sc_if && 1384142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1385142215Sglebius (iaddr & ia->ia_subnetmask) == ia->ia_subnet) { 1386142215Sglebius if (!ia_if) 1387142215Sglebius ia_if = ia; 1388142215Sglebius if (sin->sin_addr.s_addr == 1389142215Sglebius ia->ia_addr.sin_addr.s_addr) 1390142215Sglebius own++; 1391142215Sglebius } 1392142215Sglebius } 1393142215Sglebius 1394142215Sglebius if (!ia_if) 1395142215Sglebius return (EADDRNOTAVAIL); 1396142215Sglebius 1397142215Sglebius ia = ia_if; 1398142215Sglebius ifp = ia->ia_ifp; 1399142215Sglebius 1400142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1401142215Sglebius (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) 1402142215Sglebius return (EADDRNOTAVAIL); 1403142215Sglebius 1404142215Sglebius if (imo->imo_num_memberships == 0) { 1405142215Sglebius addr.s_addr = htonl(INADDR_CARP_GROUP); 1406142215Sglebius if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == NULL) 1407142215Sglebius return (ENOBUFS); 1408142215Sglebius imo->imo_num_memberships++; 1409142215Sglebius imo->imo_multicast_ifp = ifp; 1410142215Sglebius imo->imo_multicast_ttl = CARP_DFLTTL; 1411142215Sglebius imo->imo_multicast_loop = 0; 1412142215Sglebius } 1413142215Sglebius 1414142215Sglebius if (!ifp->if_carp) { 1415142215Sglebius 1416142215Sglebius MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP, 1417142215Sglebius M_WAITOK|M_ZERO); 1418142215Sglebius if (!cif) { 1419142215Sglebius error = ENOBUFS; 1420142215Sglebius goto cleanup; 1421142215Sglebius } 1422142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1423142215Sglebius FREE(cif, M_CARP); 1424142215Sglebius goto cleanup; 1425142215Sglebius } 1426142215Sglebius 1427142215Sglebius CARP_LOCK_INIT(cif); 1428142215Sglebius CARP_LOCK(cif); 1429142215Sglebius cif->vhif_ifp = ifp; 1430142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1431142914Sglebius callout_init(&cif->cif_tmo, NET_CALLOUT_MPSAFE); 1432142215Sglebius ifp->if_carp = cif; 1433142215Sglebius 1434142215Sglebius } else { 1435142215Sglebius struct carp_softc *vr; 1436142215Sglebius 1437142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1438142215Sglebius CARP_LOCK(cif); 1439142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1440142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1441142215Sglebius CARP_UNLOCK(cif); 1442142215Sglebius error = EINVAL; 1443142215Sglebius goto cleanup; 1444142215Sglebius } 1445142215Sglebius } 1446142215Sglebius sc->sc_ia = ia; 1447142564Sglebius sc->sc_carpdev = ifp; 1448142215Sglebius 1449142215Sglebius { /* XXX prevent endless loop if already in queue */ 1450142215Sglebius struct carp_softc *vr, *after = NULL; 1451142215Sglebius int myself = 0; 1452142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1453142215Sglebius 1454142215Sglebius /* XXX: cif should not change, right? So we still hold the lock */ 1455142215Sglebius CARP_LOCK_ASSERT(cif); 1456142215Sglebius 1457142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1458142215Sglebius if (vr == sc) 1459142215Sglebius myself = 1; 1460142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1461142215Sglebius after = vr; 1462142215Sglebius } 1463142215Sglebius 1464142215Sglebius if (!myself) { 1465142215Sglebius /* We're trying to keep things in order */ 1466142215Sglebius if (after == NULL) { 1467142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1468142215Sglebius } else { 1469142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1470142215Sglebius } 1471142215Sglebius cif->vhif_nvrs++; 1472142215Sglebius } 1473142215Sglebius } 1474142215Sglebius 1475142215Sglebius sc->sc_naddrs++; 1476142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1477142215Sglebius if (own) 1478142215Sglebius sc->sc_advskew = 0; 1479144329Sglebius carp_sc_state_locked(sc); 1480142215Sglebius carp_setrun(sc, 0); 1481142215Sglebius 1482142914Sglebius CARP_UNLOCK(cif); 1483142914Sglebius 1484142215Sglebius return (0); 1485142215Sglebius 1486142215Sglebiuscleanup: 1487142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1488142215Sglebius return (error); 1489142215Sglebius} 1490142215Sglebius 1491142559Sglebiusstatic int 1492142215Sglebiuscarp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin) 1493142215Sglebius{ 1494142215Sglebius int error = 0; 1495142215Sglebius 1496142215Sglebius if (!--sc->sc_naddrs) { 1497142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1498142215Sglebius struct ip_moptions *imo = &sc->sc_imo; 1499142215Sglebius 1500142914Sglebius CARP_LOCK(cif); 1501142215Sglebius callout_stop(&sc->sc_ad_tmo); 1502142215Sglebius sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); 1503142215Sglebius sc->sc_vhid = -1; 1504142215Sglebius in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); 1505142215Sglebius imo->imo_multicast_ifp = NULL; 1506142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1507142215Sglebius if (!--cif->vhif_nvrs) { 1508142914Sglebius callout_drain(&cif->cif_tmo); 1509142564Sglebius sc->sc_carpdev->if_carp = NULL; 1510142215Sglebius CARP_LOCK_DESTROY(cif); 1511142215Sglebius FREE(cif, M_IFADDR); 1512142215Sglebius } else { 1513142215Sglebius CARP_UNLOCK(cif); 1514142215Sglebius } 1515142215Sglebius } 1516142215Sglebius 1517142215Sglebius return (error); 1518142215Sglebius} 1519142215Sglebius 1520142215Sglebius#ifdef INET6 1521142559Sglebiusstatic int 1522142215Sglebiuscarp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1523142215Sglebius{ 1524142215Sglebius struct ifnet *ifp; 1525142215Sglebius struct carp_if *cif; 1526142215Sglebius struct in6_ifaddr *ia, *ia_if; 1527142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1528142215Sglebius struct in6_multi_mship *imm; 1529142215Sglebius struct sockaddr_in6 addr; 1530142215Sglebius int own, error; 1531142215Sglebius 1532142215Sglebius if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 1533142215Sglebius if (!(sc->sc_if.if_flags & IFF_UP)) 1534142215Sglebius carp_set_state(sc, INIT); 1535142215Sglebius if (sc->sc_naddrs6) 1536142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1537142215Sglebius carp_setrun(sc, 0); 1538142215Sglebius return (0); 1539142215Sglebius } 1540142215Sglebius 1541142215Sglebius /* we have to do it by hands to check we won't match on us */ 1542142215Sglebius ia_if = NULL; own = 0; 1543142215Sglebius for (ia = in6_ifaddr; ia; ia = ia->ia_next) { 1544142215Sglebius int i; 1545142215Sglebius 1546142215Sglebius for (i = 0; i < 4; i++) { 1547142215Sglebius if ((sin6->sin6_addr.s6_addr32[i] & 1548142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i]) != 1549142215Sglebius (ia->ia_addr.sin6_addr.s6_addr32[i] & 1550142215Sglebius ia->ia_prefixmask.sin6_addr.s6_addr32[i])) 1551142215Sglebius break; 1552142215Sglebius } 1553142215Sglebius /* and, yeah, we need a multicast-capable iface too */ 1554142215Sglebius if (ia->ia_ifp != &sc->sc_ac.ac_if && 1555142215Sglebius (ia->ia_ifp->if_flags & IFF_MULTICAST) && 1556142215Sglebius (i == 4)) { 1557142215Sglebius if (!ia_if) 1558142215Sglebius ia_if = ia; 1559142215Sglebius if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 1560142215Sglebius &ia->ia_addr.sin6_addr)) 1561142215Sglebius own++; 1562142215Sglebius } 1563142215Sglebius } 1564142215Sglebius 1565142215Sglebius if (!ia_if) 1566142215Sglebius return (EADDRNOTAVAIL); 1567142215Sglebius ia = ia_if; 1568142215Sglebius ifp = ia->ia_ifp; 1569142215Sglebius 1570142215Sglebius if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || 1571142215Sglebius (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) 1572142215Sglebius return (EADDRNOTAVAIL); 1573142215Sglebius 1574142215Sglebius if (!sc->sc_naddrs6) { 1575142215Sglebius im6o->im6o_multicast_ifp = ifp; 1576142215Sglebius 1577142215Sglebius /* join CARP multicast address */ 1578142215Sglebius bzero(&addr, sizeof(addr)); 1579142215Sglebius addr.sin6_family = AF_INET6; 1580142215Sglebius addr.sin6_len = sizeof(addr); 1581142215Sglebius addr.sin6_addr.s6_addr16[0] = htons(0xff02); 1582142215Sglebius addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 1583142215Sglebius addr.sin6_addr.s6_addr8[15] = 0x12; 1584142215Sglebius if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL) 1585142215Sglebius goto cleanup; 1586142215Sglebius LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 1587142215Sglebius 1588142215Sglebius /* join solicited multicast address */ 1589142215Sglebius bzero(&addr.sin6_addr, sizeof(addr.sin6_addr)); 1590142215Sglebius addr.sin6_addr.s6_addr16[0] = htons(0xff02); 1591142215Sglebius addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 1592142215Sglebius addr.sin6_addr.s6_addr32[1] = 0; 1593142215Sglebius addr.sin6_addr.s6_addr32[2] = htonl(1); 1594142215Sglebius addr.sin6_addr.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; 1595142215Sglebius addr.sin6_addr.s6_addr8[12] = 0xff; 1596142215Sglebius if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL) 1597142215Sglebius goto cleanup; 1598142215Sglebius LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 1599142215Sglebius } 1600142215Sglebius 1601142215Sglebius if (!ifp->if_carp) { 1602142215Sglebius MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP, 1603142215Sglebius M_WAITOK|M_ZERO); 1604142215Sglebius if (!cif) { 1605142215Sglebius error = ENOBUFS; 1606142215Sglebius goto cleanup; 1607142215Sglebius } 1608142215Sglebius if ((error = ifpromisc(ifp, 1))) { 1609142215Sglebius FREE(cif, M_CARP); 1610142215Sglebius goto cleanup; 1611142215Sglebius } 1612142215Sglebius 1613142215Sglebius CARP_LOCK_INIT(cif); 1614142215Sglebius CARP_LOCK(cif); 1615142215Sglebius cif->vhif_ifp = ifp; 1616142215Sglebius TAILQ_INIT(&cif->vhif_vrs); 1617142996Sglebius callout_init(&cif->cif_tmo, NET_CALLOUT_MPSAFE); 1618142215Sglebius ifp->if_carp = cif; 1619142215Sglebius 1620142215Sglebius } else { 1621142215Sglebius struct carp_softc *vr; 1622142215Sglebius 1623142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1624142215Sglebius CARP_LOCK(cif); 1625142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1626142215Sglebius if (vr != sc && vr->sc_vhid == sc->sc_vhid) { 1627142215Sglebius CARP_UNLOCK(cif); 1628142215Sglebius error = EINVAL; 1629142215Sglebius goto cleanup; 1630142215Sglebius } 1631142215Sglebius } 1632142215Sglebius sc->sc_ia6 = ia; 1633142564Sglebius sc->sc_carpdev = ifp; 1634142215Sglebius 1635142215Sglebius { /* XXX prevent endless loop if already in queue */ 1636142215Sglebius struct carp_softc *vr, *after = NULL; 1637142215Sglebius int myself = 0; 1638142215Sglebius cif = (struct carp_if *)ifp->if_carp; 1639142215Sglebius CARP_LOCK_ASSERT(cif); 1640142215Sglebius 1641142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { 1642142215Sglebius if (vr == sc) 1643142215Sglebius myself = 1; 1644142215Sglebius if (vr->sc_vhid < sc->sc_vhid) 1645142215Sglebius after = vr; 1646142215Sglebius } 1647142215Sglebius 1648142215Sglebius if (!myself) { 1649142215Sglebius /* We're trying to keep things in order */ 1650142215Sglebius if (after == NULL) { 1651142215Sglebius TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); 1652142215Sglebius } else { 1653142215Sglebius TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list); 1654142215Sglebius } 1655142215Sglebius cif->vhif_nvrs++; 1656142215Sglebius } 1657142215Sglebius } 1658142215Sglebius 1659142215Sglebius sc->sc_naddrs6++; 1660142215Sglebius sc->sc_ac.ac_if.if_flags |= IFF_UP; 1661142215Sglebius if (own) 1662142215Sglebius sc->sc_advskew = 0; 1663144329Sglebius carp_sc_state_locked(sc); 1664142215Sglebius carp_setrun(sc, 0); 1665142215Sglebius 1666142914Sglebius CARP_UNLOCK(cif); 1667142914Sglebius 1668142215Sglebius return (0); 1669142215Sglebius 1670142215Sglebiuscleanup: 1671142215Sglebius /* clean up multicast memberships */ 1672142215Sglebius if (!sc->sc_naddrs6) { 1673142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1674142215Sglebius imm = LIST_FIRST(&im6o->im6o_memberships); 1675142215Sglebius LIST_REMOVE(imm, i6mm_chain); 1676142215Sglebius in6_leavegroup(imm); 1677142215Sglebius } 1678142215Sglebius } 1679142215Sglebius return (error); 1680142215Sglebius} 1681142215Sglebius 1682142559Sglebiusstatic int 1683142215Sglebiuscarp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) 1684142215Sglebius{ 1685142215Sglebius int error = 0; 1686142215Sglebius 1687142215Sglebius if (!--sc->sc_naddrs6) { 1688142564Sglebius struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1689142215Sglebius struct ip6_moptions *im6o = &sc->sc_im6o; 1690142215Sglebius 1691142914Sglebius CARP_LOCK(cif); 1692142215Sglebius callout_stop(&sc->sc_ad_tmo); 1693142215Sglebius sc->sc_ac.ac_if.if_flags &= ~(IFF_UP|IFF_RUNNING); 1694142215Sglebius sc->sc_vhid = -1; 1695142215Sglebius while (!LIST_EMPTY(&im6o->im6o_memberships)) { 1696142215Sglebius struct in6_multi_mship *imm = 1697142215Sglebius LIST_FIRST(&im6o->im6o_memberships); 1698142215Sglebius 1699142215Sglebius LIST_REMOVE(imm, i6mm_chain); 1700142215Sglebius in6_leavegroup(imm); 1701142215Sglebius } 1702142215Sglebius im6o->im6o_multicast_ifp = NULL; 1703142215Sglebius TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); 1704142215Sglebius if (!--cif->vhif_nvrs) { 1705142914Sglebius callout_drain(&cif->cif_tmo); 1706142215Sglebius CARP_LOCK_DESTROY(cif); 1707142564Sglebius sc->sc_carpdev->if_carp = NULL; 1708142215Sglebius FREE(cif, M_IFADDR); 1709142215Sglebius } else 1710142215Sglebius CARP_UNLOCK(cif); 1711142215Sglebius } 1712142215Sglebius 1713142215Sglebius return (error); 1714142215Sglebius} 1715142215Sglebius#endif /* INET6 */ 1716142215Sglebius 1717142559Sglebiusstatic int 1718142215Sglebiuscarp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) 1719142215Sglebius{ 1720142215Sglebius struct carp_softc *sc = ifp->if_softc, *vr; 1721142215Sglebius struct carpreq carpr; 1722142215Sglebius struct ifaddr *ifa; 1723142215Sglebius struct ifreq *ifr; 1724142215Sglebius struct ifaliasreq *ifra; 1725142914Sglebius int locked = 0, error = 0; 1726142215Sglebius 1727142215Sglebius ifa = (struct ifaddr *)addr; 1728142215Sglebius ifra = (struct ifaliasreq *)addr; 1729142215Sglebius ifr = (struct ifreq *)addr; 1730142215Sglebius 1731142215Sglebius switch (cmd) { 1732142215Sglebius case SIOCSIFADDR: 1733142215Sglebius switch (ifa->ifa_addr->sa_family) { 1734142215Sglebius#ifdef INET 1735142215Sglebius case AF_INET: 1736142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1737142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1738142215Sglebius sizeof(struct sockaddr)); 1739142215Sglebius error = carp_set_addr(sc, satosin(ifa->ifa_addr)); 1740142215Sglebius break; 1741142215Sglebius#endif /* INET */ 1742142215Sglebius#ifdef INET6 1743142215Sglebius case AF_INET6: 1744142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1745142215Sglebius error = carp_set_addr6(sc, satosin6(ifa->ifa_addr)); 1746142215Sglebius break; 1747142215Sglebius#endif /* INET6 */ 1748142215Sglebius default: 1749142215Sglebius error = EAFNOSUPPORT; 1750142215Sglebius break; 1751142215Sglebius } 1752142215Sglebius break; 1753142215Sglebius 1754142215Sglebius case SIOCAIFADDR: 1755142215Sglebius switch (ifa->ifa_addr->sa_family) { 1756142215Sglebius#ifdef INET 1757142215Sglebius case AF_INET: 1758142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1759142215Sglebius bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, 1760142215Sglebius sizeof(struct sockaddr)); 1761142215Sglebius error = carp_set_addr(sc, satosin(&ifra->ifra_addr)); 1762142215Sglebius break; 1763142215Sglebius#endif /* INET */ 1764142215Sglebius#ifdef INET6 1765142215Sglebius case AF_INET6: 1766142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1767142215Sglebius error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr)); 1768142215Sglebius break; 1769142215Sglebius#endif /* INET6 */ 1770142215Sglebius default: 1771142215Sglebius error = EAFNOSUPPORT; 1772142215Sglebius break; 1773142215Sglebius } 1774142215Sglebius break; 1775142215Sglebius 1776142215Sglebius case SIOCDIFADDR: 1777142215Sglebius switch (ifa->ifa_addr->sa_family) { 1778142215Sglebius#ifdef INET 1779142215Sglebius case AF_INET: 1780142215Sglebius error = carp_del_addr(sc, satosin(&ifra->ifra_addr)); 1781142215Sglebius break; 1782142215Sglebius#endif /* INET */ 1783142215Sglebius#ifdef INET6 1784142215Sglebius case AF_INET6: 1785142215Sglebius error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr)); 1786142215Sglebius break; 1787142215Sglebius#endif /* INET6 */ 1788142215Sglebius default: 1789142215Sglebius error = EAFNOSUPPORT; 1790142215Sglebius break; 1791142215Sglebius } 1792142215Sglebius break; 1793142215Sglebius 1794142215Sglebius case SIOCSIFFLAGS: 1795142914Sglebius if (sc->sc_carpdev) { 1796142914Sglebius locked = 1; 1797142914Sglebius CARP_SCLOCK(sc); 1798142914Sglebius } 1799142215Sglebius if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { 1800142215Sglebius callout_stop(&sc->sc_ad_tmo); 1801142215Sglebius callout_stop(&sc->sc_md_tmo); 1802142215Sglebius callout_stop(&sc->sc_md6_tmo); 1803142215Sglebius if (sc->sc_state == MASTER) 1804142914Sglebius carp_send_ad_locked(sc); 1805142215Sglebius carp_set_state(sc, INIT); 1806142215Sglebius carp_setrun(sc, 0); 1807142215Sglebius } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { 1808142215Sglebius sc->sc_if.if_flags |= IFF_UP; 1809142215Sglebius carp_setrun(sc, 0); 1810142215Sglebius } 1811142215Sglebius break; 1812142215Sglebius 1813142215Sglebius case SIOCSVH: 1814142215Sglebius if ((error = suser(curthread)) != 0) 1815142215Sglebius break; 1816142215Sglebius if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) 1817142215Sglebius break; 1818142215Sglebius error = 1; 1819142914Sglebius if (sc->sc_carpdev) { 1820142914Sglebius locked = 1; 1821142914Sglebius CARP_SCLOCK(sc); 1822142914Sglebius } 1823142215Sglebius if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { 1824142215Sglebius switch (carpr.carpr_state) { 1825142215Sglebius case BACKUP: 1826142215Sglebius callout_stop(&sc->sc_ad_tmo); 1827142215Sglebius carp_set_state(sc, BACKUP); 1828142215Sglebius carp_setrun(sc, 0); 1829142215Sglebius carp_setroute(sc, RTM_DELETE); 1830142215Sglebius break; 1831142215Sglebius case MASTER: 1832142914Sglebius carp_master_down_locked(sc); 1833142215Sglebius break; 1834142215Sglebius default: 1835142215Sglebius break; 1836142215Sglebius } 1837142215Sglebius } 1838142215Sglebius if (carpr.carpr_vhid > 0) { 1839142215Sglebius if (carpr.carpr_vhid > 255) { 1840142215Sglebius error = EINVAL; 1841142215Sglebius break; 1842142215Sglebius } 1843142564Sglebius if (sc->sc_carpdev) { 1844142215Sglebius struct carp_if *cif; 1845142564Sglebius cif = (struct carp_if *)sc->sc_carpdev->if_carp; 1846142215Sglebius TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) 1847142215Sglebius if (vr != sc && 1848143806Sglebius vr->sc_vhid == carpr.carpr_vhid) 1849143806Sglebius return EEXIST; 1850142215Sglebius } 1851142215Sglebius sc->sc_vhid = carpr.carpr_vhid; 1852142215Sglebius sc->sc_ac.ac_enaddr[0] = 0; 1853142215Sglebius sc->sc_ac.ac_enaddr[1] = 0; 1854142215Sglebius sc->sc_ac.ac_enaddr[2] = 0x5e; 1855142215Sglebius sc->sc_ac.ac_enaddr[3] = 0; 1856142215Sglebius sc->sc_ac.ac_enaddr[4] = 1; 1857142215Sglebius sc->sc_ac.ac_enaddr[5] = sc->sc_vhid; 1858142215Sglebius error--; 1859142215Sglebius } 1860142215Sglebius if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { 1861142215Sglebius if (carpr.carpr_advskew >= 255) { 1862142215Sglebius error = EINVAL; 1863142215Sglebius break; 1864142215Sglebius } 1865142215Sglebius if (carpr.carpr_advbase > 255) { 1866142215Sglebius error = EINVAL; 1867142215Sglebius break; 1868142215Sglebius } 1869142215Sglebius sc->sc_advbase = carpr.carpr_advbase; 1870142215Sglebius sc->sc_advskew = carpr.carpr_advskew; 1871142215Sglebius error--; 1872142215Sglebius } 1873142215Sglebius bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); 1874142215Sglebius if (error > 0) 1875142215Sglebius error = EINVAL; 1876142215Sglebius else { 1877142215Sglebius error = 0; 1878142215Sglebius carp_setrun(sc, 0); 1879142215Sglebius } 1880142215Sglebius break; 1881142215Sglebius 1882142215Sglebius case SIOCGVH: 1883142914Sglebius /* XXX: lockless read */ 1884142215Sglebius bzero(&carpr, sizeof(carpr)); 1885142215Sglebius carpr.carpr_state = sc->sc_state; 1886142215Sglebius carpr.carpr_vhid = sc->sc_vhid; 1887142215Sglebius carpr.carpr_advbase = sc->sc_advbase; 1888142215Sglebius carpr.carpr_advskew = sc->sc_advskew; 1889142215Sglebius if (suser(curthread) == 0) 1890142215Sglebius bcopy(sc->sc_key, carpr.carpr_key, 1891142215Sglebius sizeof(carpr.carpr_key)); 1892142215Sglebius error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); 1893142215Sglebius break; 1894142215Sglebius 1895142215Sglebius default: 1896142215Sglebius error = EINVAL; 1897142215Sglebius } 1898142215Sglebius 1899142914Sglebius if (locked) 1900142914Sglebius CARP_SCUNLOCK(sc); 1901142914Sglebius 1902142215Sglebius carp_hmac_prepare(sc); 1903142914Sglebius 1904142215Sglebius return (error); 1905142215Sglebius} 1906142215Sglebius 1907142215Sglebius/* 1908142215Sglebius * XXX: this is looutput. We should eventually use it from there. 1909142215Sglebius */ 1910142215Sglebiusstatic int 1911142215Sglebiuscarp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 1912142215Sglebius struct rtentry *rt) 1913142215Sglebius{ 1914142215Sglebius M_ASSERTPKTHDR(m); /* check if we have the packet header */ 1915142215Sglebius 1916142215Sglebius if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 1917142215Sglebius m_freem(m); 1918142215Sglebius return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 1919142215Sglebius rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 1920142215Sglebius } 1921142215Sglebius 1922142215Sglebius ifp->if_opackets++; 1923142215Sglebius ifp->if_obytes += m->m_pkthdr.len; 1924142215Sglebius#if 1 /* XXX */ 1925142215Sglebius switch (dst->sa_family) { 1926142215Sglebius case AF_INET: 1927142215Sglebius case AF_INET6: 1928142215Sglebius case AF_IPX: 1929142215Sglebius case AF_APPLETALK: 1930142215Sglebius break; 1931142215Sglebius default: 1932142215Sglebius printf("carp_looutput: af=%d unexpected\n", dst->sa_family); 1933142215Sglebius m_freem(m); 1934142215Sglebius return (EAFNOSUPPORT); 1935142215Sglebius } 1936142215Sglebius#endif 1937142215Sglebius return(if_simloop(ifp, m, dst->sa_family, 0)); 1938142215Sglebius} 1939142215Sglebius 1940142215Sglebius/* 1941142215Sglebius * Start output on carp interface. This function should never be called. 1942142215Sglebius */ 1943142559Sglebiusstatic void 1944142215Sglebiuscarp_start(struct ifnet *ifp) 1945142215Sglebius{ 1946142215Sglebius#ifdef DEBUG 1947142215Sglebius printf("%s: start called\n", ifp->if_xname); 1948142215Sglebius#endif 1949142215Sglebius} 1950142215Sglebius 1951142215Sglebiusint 1952142215Sglebiuscarp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, 1953142215Sglebius struct rtentry *rt) 1954142215Sglebius{ 1955142215Sglebius struct m_tag *mtag; 1956142215Sglebius struct carp_softc *sc; 1957142215Sglebius struct ifnet *carp_ifp; 1958142215Sglebius 1959142215Sglebius if (!sa) 1960142215Sglebius return (0); 1961142215Sglebius 1962142215Sglebius switch (sa->sa_family) { 1963142215Sglebius#ifdef INET 1964142215Sglebius case AF_INET: 1965142215Sglebius break; 1966142215Sglebius#endif /* INET */ 1967142215Sglebius#ifdef INET6 1968142215Sglebius case AF_INET6: 1969142215Sglebius break; 1970142215Sglebius#endif /* INET6 */ 1971142215Sglebius default: 1972142215Sglebius return (0); 1973142215Sglebius } 1974142215Sglebius 1975142215Sglebius mtag = m_tag_find(m, PACKET_TAG_CARP, NULL); 1976142215Sglebius if (mtag == NULL) 1977142215Sglebius return (0); 1978142215Sglebius 1979142215Sglebius bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *)); 1980142215Sglebius sc = carp_ifp->if_softc; 1981142215Sglebius 1982142215Sglebius /* Set the source MAC address to Virtual Router MAC Address */ 1983142215Sglebius switch (ifp->if_type) { 1984142798Syar case IFT_ETHER: 1985142798Syar case IFT_L2VLAN: { 1986142215Sglebius struct ether_header *eh; 1987142215Sglebius 1988142215Sglebius eh = mtod(m, struct ether_header *); 1989142215Sglebius eh->ether_shost[0] = 0; 1990142215Sglebius eh->ether_shost[1] = 0; 1991142215Sglebius eh->ether_shost[2] = 0x5e; 1992142215Sglebius eh->ether_shost[3] = 0; 1993142215Sglebius eh->ether_shost[4] = 1; 1994142215Sglebius eh->ether_shost[5] = sc->sc_vhid; 1995142215Sglebius } 1996142215Sglebius break; 1997142215Sglebius case IFT_FDDI: { 1998142215Sglebius struct fddi_header *fh; 1999142215Sglebius 2000142215Sglebius fh = mtod(m, struct fddi_header *); 2001142215Sglebius fh->fddi_shost[0] = 0; 2002142215Sglebius fh->fddi_shost[1] = 0; 2003142215Sglebius fh->fddi_shost[2] = 0x5e; 2004142215Sglebius fh->fddi_shost[3] = 0; 2005142215Sglebius fh->fddi_shost[4] = 1; 2006142215Sglebius fh->fddi_shost[5] = sc->sc_vhid; 2007142215Sglebius } 2008142215Sglebius break; 2009142215Sglebius case IFT_ISO88025: { 2010142215Sglebius struct iso88025_header *th; 2011142215Sglebius th = mtod(m, struct iso88025_header *); 2012142215Sglebius th->iso88025_shost[0] = 3; 2013142215Sglebius th->iso88025_shost[1] = 0; 2014142215Sglebius th->iso88025_shost[2] = 0x40 >> (sc->sc_vhid - 1); 2015142215Sglebius th->iso88025_shost[3] = 0x40000 >> (sc->sc_vhid - 1); 2016142215Sglebius th->iso88025_shost[4] = 0; 2017142215Sglebius th->iso88025_shost[5] = 0; 2018142215Sglebius } 2019142215Sglebius break; 2020142215Sglebius default: 2021142215Sglebius printf("%s: carp is not supported for this interface type\n", 2022142215Sglebius ifp->if_xname); 2023142215Sglebius return (EOPNOTSUPP); 2024142215Sglebius } 2025142215Sglebius 2026142215Sglebius return (0); 2027142215Sglebius} 2028142215Sglebius 2029142559Sglebiusstatic void 2030142215Sglebiuscarp_set_state(struct carp_softc *sc, int state) 2031142215Sglebius{ 2032142914Sglebius 2033142914Sglebius if (sc->sc_carpdev) 2034142914Sglebius CARP_SCLOCK_ASSERT(sc); 2035142914Sglebius 2036142215Sglebius if (sc->sc_state == state) 2037142215Sglebius return; 2038142215Sglebius 2039142215Sglebius sc->sc_state = state; 2040142215Sglebius switch (state) { 2041142215Sglebius case BACKUP: 2042142215Sglebius sc->sc_ac.ac_if.if_link_state = LINK_STATE_DOWN; 2043142215Sglebius break; 2044142215Sglebius case MASTER: 2045142215Sglebius sc->sc_ac.ac_if.if_link_state = LINK_STATE_UP; 2046142215Sglebius break; 2047142215Sglebius default: 2048142215Sglebius sc->sc_ac.ac_if.if_link_state = LINK_STATE_UNKNOWN; 2049142215Sglebius break; 2050142215Sglebius } 2051142215Sglebius rt_ifmsg(&sc->sc_ac.ac_if); 2052142215Sglebius} 2053142215Sglebius 2054142215Sglebiusvoid 2055142215Sglebiuscarp_carpdev_state(void *v) 2056142215Sglebius{ 2057142215Sglebius struct carp_if *cif = v; 2058142914Sglebius 2059142914Sglebius /* 2060142914Sglebius * We came here from interrupt handler of network 2061142914Sglebius * card. To avoid multiple LORs, we will queue function 2062142914Sglebius * for later. 2063142914Sglebius */ 2064142914Sglebius 2065142914Sglebius callout_reset(&cif->cif_tmo, 1, carp_carpdev_state1, v); 2066142914Sglebius} 2067142914Sglebius 2068142914Sglebiusvoid 2069142914Sglebiuscarp_carpdev_state1(void *v) 2070142914Sglebius{ 2071142914Sglebius struct carp_if *cif = v; 2072142914Sglebius 2073142914Sglebius CARP_LOCK(cif); 2074142914Sglebius carp_carpdev_state_locked(cif); 2075142914Sglebius CARP_UNLOCK(cif); 2076142914Sglebius} 2077142914Sglebius 2078142914Sglebiusstatic void 2079142914Sglebiuscarp_carpdev_state_locked(struct carp_if *cif) 2080142914Sglebius{ 2081142215Sglebius struct carp_softc *sc; 2082142215Sglebius 2083144329Sglebius TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) 2084144329Sglebius carp_sc_state_locked(sc); 2085144329Sglebius} 2086144329Sglebius 2087144329Sglebiusstatic void 2088144329Sglebiuscarp_sc_state_locked(struct carp_softc *sc) 2089144329Sglebius{ 2090144329Sglebius CARP_SCLOCK_ASSERT(sc); 2091144329Sglebius 2092144329Sglebius if (sc->sc_carpdev->if_link_state != LINK_STATE_UP || 2093144329Sglebius !(sc->sc_carpdev->if_flags & IFF_UP)) { 2094144329Sglebius sc->sc_flags_backup = sc->sc_if.if_flags; 2095144329Sglebius sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); 2096144329Sglebius callout_stop(&sc->sc_ad_tmo); 2097144329Sglebius callout_stop(&sc->sc_md_tmo); 2098144329Sglebius callout_stop(&sc->sc_md6_tmo); 2099144329Sglebius carp_set_state(sc, INIT); 2100144329Sglebius carp_setrun(sc, 0); 2101144329Sglebius if (!sc->sc_suppress) { 2102144329Sglebius carp_suppress_preempt++; 2103144329Sglebius if (carp_suppress_preempt == 1) { 2104144329Sglebius CARP_SCUNLOCK(sc); 2105144329Sglebius carp_send_ad_all(); 2106144329Sglebius CARP_SCLOCK(sc); 2107142215Sglebius } 2108142215Sglebius } 2109144329Sglebius sc->sc_suppress = 1; 2110144329Sglebius } else { 2111144329Sglebius sc->sc_if.if_flags |= sc->sc_flags_backup; 2112144329Sglebius carp_set_state(sc, INIT); 2113144329Sglebius carp_setrun(sc, 0); 2114144329Sglebius if (sc->sc_suppress) 2115144329Sglebius carp_suppress_preempt--; 2116144329Sglebius sc->sc_suppress = 0; 2117142215Sglebius } 2118144329Sglebius 2119144329Sglebius return; 2120142215Sglebius} 2121142215Sglebius 2122142215Sglebiusstatic int 2123142215Sglebiuscarp_modevent(module_t mod, int type, void *data) 2124142215Sglebius{ 2125142215Sglebius int error = 0; 2126142215Sglebius 2127142215Sglebius switch (type) { 2128142215Sglebius case MOD_LOAD: 2129142911Sglebius mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); 2130142215Sglebius LIST_INIT(&carpif_list); 2131142215Sglebius if_clone_attach(&carp_cloner); 2132142215Sglebius break; 2133142215Sglebius 2134142215Sglebius case MOD_UNLOAD: 2135142215Sglebius if_clone_detach(&carp_cloner); 2136142215Sglebius while (!LIST_EMPTY(&carpif_list)) 2137142911Sglebius carp_clone_destroy(&LIST_FIRST(&carpif_list)->sc_if); 2138142911Sglebius mtx_destroy(&carp_mtx); 2139142215Sglebius break; 2140142215Sglebius 2141142215Sglebius default: 2142142215Sglebius error = EINVAL; 2143142215Sglebius break; 2144142215Sglebius } 2145142215Sglebius 2146142215Sglebius return error; 2147142215Sglebius} 2148142215Sglebius 2149142215Sglebiusstatic moduledata_t carp_mod = { 2150142215Sglebius "carp", 2151142215Sglebius carp_modevent, 2152142215Sglebius 0 2153142215Sglebius}; 2154142215Sglebius 2155142215SglebiusDECLARE_MODULE(carp, carp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2156