Annotation of sys/netinet/ip_carp.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ip_carp.c,v 1.147 2007/06/23 16:15:26 reyk Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2002 Michael Shalayeff. All rights reserved.
! 5: * Copyright (c) 2003 Ryan McBride. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 19: * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
! 20: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
! 21: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
! 22: * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 23: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
! 24: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
! 25: * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
! 26: * THE POSSIBILITY OF SUCH DAMAGE.
! 27: */
! 28:
! 29: /*
! 30: * TODO:
! 31: * - iface reconfigure
! 32: * - support for hardware checksum calculations;
! 33: *
! 34: */
! 35:
! 36: #include "ether.h"
! 37:
! 38: #include <sys/param.h>
! 39: #include <sys/proc.h>
! 40: #include <sys/systm.h>
! 41: #include <sys/mbuf.h>
! 42: #include <sys/socket.h>
! 43: #include <sys/socketvar.h>
! 44: #include <sys/ioctl.h>
! 45: #include <sys/errno.h>
! 46: #include <sys/device.h>
! 47: #include <sys/kernel.h>
! 48: #include <sys/sysctl.h>
! 49: #include <sys/syslog.h>
! 50:
! 51: #include <machine/cpu.h>
! 52:
! 53: #include <net/if.h>
! 54: #include <net/if_types.h>
! 55: #include <net/if_llc.h>
! 56: #include <net/route.h>
! 57: #include <net/netisr.h>
! 58:
! 59: /* for arc4random() */
! 60: #include <dev/rndvar.h>
! 61:
! 62: #if NFDDI > 0
! 63: #include <net/if_fddi.h>
! 64: #endif
! 65:
! 66: #include <crypto/sha1.h>
! 67:
! 68: #ifdef INET
! 69: #include <netinet/in.h>
! 70: #include <netinet/in_systm.h>
! 71: #include <netinet/in_var.h>
! 72: #include <netinet/ip.h>
! 73: #include <netinet/ip_var.h>
! 74: #include <netinet/if_ether.h>
! 75: #include <netinet/ip_ipsp.h>
! 76:
! 77: #include <net/if_enc.h>
! 78: #include <net/if_dl.h>
! 79: #endif
! 80:
! 81: #ifdef INET6
! 82: #include <netinet/icmp6.h>
! 83: #include <netinet/ip6.h>
! 84: #include <netinet6/ip6_var.h>
! 85: #include <netinet6/nd6.h>
! 86: #include <netinet6/in6_ifattach.h>
! 87: #endif
! 88:
! 89: #include "bpfilter.h"
! 90: #if NBPFILTER > 0
! 91: #include <net/bpf.h>
! 92: #endif
! 93:
! 94: #include <netinet/ip_carp.h>
! 95:
! 96: struct carp_mc_entry {
! 97: LIST_ENTRY(carp_mc_entry) mc_entries;
! 98: union {
! 99: struct ether_multi *mcu_enm;
! 100: } mc_u;
! 101: struct sockaddr_storage mc_addr;
! 102: };
! 103: #define mc_enm mc_u.mcu_enm
! 104:
! 105: enum { HMAC_ORIG=0, HMAC_NOV6LL=1, HMAC_MAX=2 };
! 106:
! 107: struct carp_softc {
! 108: struct arpcom sc_ac;
! 109: #define sc_if sc_ac.ac_if
! 110: #define sc_carpdev sc_ac.ac_if.if_carpdev
! 111: void *ah_cookie;
! 112: void *lh_cookie;
! 113: struct ip_moptions sc_imo;
! 114: #ifdef INET6
! 115: struct ip6_moptions sc_im6o;
! 116: #endif /* INET6 */
! 117: TAILQ_ENTRY(carp_softc) sc_list;
! 118:
! 119: enum { INIT = 0, BACKUP, MASTER } sc_state;
! 120:
! 121: int sc_suppress;
! 122: int sc_bow_out;
! 123:
! 124: int sc_sendad_errors;
! 125: #define CARP_SENDAD_MAX_ERRORS 3
! 126: int sc_sendad_success;
! 127: #define CARP_SENDAD_MIN_SUCCESS 3
! 128:
! 129: char sc_carplladdr[ETHER_ADDR_LEN];
! 130: char sc_curlladdr[ETHER_ADDR_LEN];
! 131: int sc_vhid;
! 132: int sc_advskew;
! 133: int sc_naddrs;
! 134: int sc_naddrs6;
! 135: int sc_advbase; /* seconds */
! 136: int sc_init_counter;
! 137: u_int64_t sc_counter;
! 138:
! 139: /* authentication */
! 140: #define CARP_HMAC_PAD 64
! 141: unsigned char sc_key[CARP_KEY_LEN];
! 142: unsigned char sc_pad[CARP_HMAC_PAD];
! 143:
! 144: SHA1_CTX sc_sha1[HMAC_MAX];
! 145: u_int32_t sc_hashkey[2];
! 146: u_int32_t sc_lsmask; /* load sharing mask */
! 147: int sc_lscount; /* # load sharing interfaces (max 32) */
! 148:
! 149: struct timeout sc_ad_tmo; /* advertisement timeout */
! 150: struct timeout sc_md_tmo; /* master down timeout */
! 151: struct timeout sc_md6_tmo; /* master down timeout */
! 152: int sc_delayed_arp; /* delayed ARP request countdown */
! 153:
! 154: LIST_HEAD(__carp_mchead, carp_mc_entry) carp_mc_listhead;
! 155: };
! 156:
! 157: int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 0, 0 }; /* XXX for now */
! 158: struct carpstats carpstats;
! 159:
! 160: struct carp_if {
! 161: TAILQ_HEAD(, carp_softc) vhif_vrs;
! 162: int vhif_nvrs;
! 163:
! 164: struct ifnet *vhif_ifp;
! 165: };
! 166:
! 167: #define CARP_LOG(sc, s) \
! 168: if (carp_opts[CARPCTL_LOG]) { \
! 169: if (sc) \
! 170: log(LOG_INFO, "%s: ", \
! 171: (sc)->sc_if.if_xname); \
! 172: else \
! 173: log(LOG_INFO, "carp: "); \
! 174: addlog s; \
! 175: addlog("\n"); \
! 176: }
! 177:
! 178: void carp_hmac_prepare(struct carp_softc *);
! 179: void carp_hmac_prepare_ctx(struct carp_softc *, u_int8_t);
! 180: void carp_hmac_generate(struct carp_softc *, u_int32_t *,
! 181: unsigned char *, u_int8_t);
! 182: int carp_hmac_verify(struct carp_softc *, u_int32_t *,
! 183: unsigned char *);
! 184: void carp_setroute(struct carp_softc *, int);
! 185: void carp_proto_input_c(struct mbuf *, struct carp_header *, sa_family_t);
! 186: void carpattach(int);
! 187: void carpdetach(struct carp_softc *);
! 188: int carp_prepare_ad(struct mbuf *, struct carp_softc *,
! 189: struct carp_header *);
! 190: void carp_send_ad_all(void);
! 191: void carp_send_ad(void *);
! 192: void carp_send_arp(struct carp_softc *);
! 193: void carp_master_down(void *);
! 194: int carp_ioctl(struct ifnet *, u_long, caddr_t);
! 195: void carp_ifgroup_ioctl(struct ifnet *, u_long, caddr_t);
! 196: void carp_ifgattr_ioctl(struct ifnet *, u_long, caddr_t);
! 197: void carp_start(struct ifnet *);
! 198: void carp_setrun(struct carp_softc *, sa_family_t);
! 199: void carp_set_state(struct carp_softc *, int);
! 200: int carp_addrcount(struct carp_if *, struct ifaddr *, int);
! 201: enum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING, CARP_COUNT_LINK0 };
! 202: void carp_multicast_cleanup(struct carp_softc *);
! 203: int carp_set_ifp(struct carp_softc *, struct ifnet *);
! 204: void carp_set_enaddr(struct carp_softc *);
! 205: void carp_addr_updated(void *);
! 206: u_int32_t carp_hash(struct carp_softc *, u_char *);
! 207: int carp_set_addr(struct carp_softc *, struct sockaddr_in *);
! 208: int carp_join_multicast(struct carp_softc *);
! 209: #ifdef INET6
! 210: void carp_send_na(struct carp_softc *);
! 211: int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *);
! 212: int carp_join_multicast6(struct carp_softc *);
! 213: #endif
! 214: int carp_clone_create(struct if_clone *, int);
! 215: int carp_clone_destroy(struct ifnet *);
! 216: int carp_ether_addmulti(struct carp_softc *, struct ifreq *);
! 217: int carp_ether_delmulti(struct carp_softc *, struct ifreq *);
! 218: void carp_ether_purgemulti(struct carp_softc *);
! 219: int carp_group_demote_count(struct carp_softc *);
! 220: void carp_update_lsmask(struct carp_softc *);
! 221:
! 222: struct if_clone carp_cloner =
! 223: IF_CLONE_INITIALIZER("carp", carp_clone_create, carp_clone_destroy);
! 224:
! 225: #define carp_cksum(_m, _l) ((u_int16_t)in_cksum((_m), (_l)))
! 226:
! 227: void
! 228: carp_hmac_prepare(struct carp_softc *sc)
! 229: {
! 230: u_int8_t i;
! 231:
! 232: for (i=0; i < HMAC_MAX; i++)
! 233: carp_hmac_prepare_ctx(sc, i);
! 234: }
! 235:
! 236: void
! 237: carp_hmac_prepare_ctx(struct carp_softc *sc, u_int8_t ctx)
! 238: {
! 239: u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT;
! 240: u_int8_t vhid = sc->sc_vhid & 0xff;
! 241: SHA1_CTX sha1ctx;
! 242: u_int32_t kmd[5];
! 243: struct ifaddr *ifa;
! 244: int i, found;
! 245: struct in_addr last, cur, in;
! 246: #ifdef INET6
! 247: struct in6_addr last6, cur6, in6;
! 248: #endif /* INET6 */
! 249:
! 250: /* compute ipad from key */
! 251: bzero(sc->sc_pad, sizeof(sc->sc_pad));
! 252: bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key));
! 253: for (i = 0; i < sizeof(sc->sc_pad); i++)
! 254: sc->sc_pad[i] ^= 0x36;
! 255:
! 256: /* precompute first part of inner hash */
! 257: SHA1Init(&sc->sc_sha1[ctx]);
! 258: SHA1Update(&sc->sc_sha1[ctx], sc->sc_pad, sizeof(sc->sc_pad));
! 259: SHA1Update(&sc->sc_sha1[ctx], (void *)&version, sizeof(version));
! 260: SHA1Update(&sc->sc_sha1[ctx], (void *)&type, sizeof(type));
! 261:
! 262: /* generate a key for the arpbalance hash, before the vhid is hashed */
! 263: bcopy(&sc->sc_sha1[ctx], &sha1ctx, sizeof(sha1ctx));
! 264: SHA1Final((unsigned char *)kmd, &sha1ctx);
! 265: sc->sc_hashkey[0] = kmd[0] ^ kmd[1];
! 266: sc->sc_hashkey[1] = kmd[2] ^ kmd[3];
! 267:
! 268: /* the rest of the precomputation */
! 269: if (bcmp(sc->sc_ac.ac_enaddr, sc->sc_carplladdr, ETHER_ADDR_LEN) != 0)
! 270: SHA1Update(&sc->sc_sha1[ctx], sc->sc_ac.ac_enaddr,
! 271: ETHER_ADDR_LEN);
! 272:
! 273: SHA1Update(&sc->sc_sha1[ctx], (void *)&vhid, sizeof(vhid));
! 274:
! 275: /* Hash the addresses from smallest to largest, not interface order */
! 276: #ifdef INET
! 277: cur.s_addr = 0;
! 278: do {
! 279: found = 0;
! 280: last = cur;
! 281: cur.s_addr = 0xffffffff;
! 282: TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
! 283: in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
! 284: if (ifa->ifa_addr->sa_family == AF_INET &&
! 285: ntohl(in.s_addr) > ntohl(last.s_addr) &&
! 286: ntohl(in.s_addr) < ntohl(cur.s_addr)) {
! 287: cur.s_addr = in.s_addr;
! 288: found++;
! 289: }
! 290: }
! 291: if (found)
! 292: SHA1Update(&sc->sc_sha1[ctx],
! 293: (void *)&cur, sizeof(cur));
! 294: } while (found);
! 295: #endif /* INET */
! 296: #ifdef INET6
! 297: memset(&cur6, 0x00, sizeof(cur6));
! 298: do {
! 299: found = 0;
! 300: last6 = cur6;
! 301: memset(&cur6, 0xff, sizeof(cur6));
! 302: TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
! 303: in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
! 304: if (IN6_IS_SCOPE_EMBED(&in6)) {
! 305: if (ctx == HMAC_NOV6LL)
! 306: continue;
! 307: in6.s6_addr16[1] = 0;
! 308: }
! 309: if (ifa->ifa_addr->sa_family == AF_INET6 &&
! 310: memcmp(&in6, &last6, sizeof(in6)) > 0 &&
! 311: memcmp(&in6, &cur6, sizeof(in6)) < 0) {
! 312: cur6 = in6;
! 313: found++;
! 314: }
! 315: }
! 316: if (found)
! 317: SHA1Update(&sc->sc_sha1[ctx],
! 318: (void *)&cur6, sizeof(cur6));
! 319: } while (found);
! 320: #endif /* INET6 */
! 321:
! 322: /* convert ipad to opad */
! 323: for (i = 0; i < sizeof(sc->sc_pad); i++)
! 324: sc->sc_pad[i] ^= 0x36 ^ 0x5c;
! 325: }
! 326:
! 327: void
! 328: carp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2],
! 329: unsigned char md[20], u_int8_t ctx)
! 330: {
! 331: SHA1_CTX sha1ctx;
! 332:
! 333: /* fetch first half of inner hash */
! 334: bcopy(&sc->sc_sha1[ctx], &sha1ctx, sizeof(sha1ctx));
! 335:
! 336: SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter));
! 337: SHA1Final(md, &sha1ctx);
! 338:
! 339: /* outer hash */
! 340: SHA1Init(&sha1ctx);
! 341: SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad));
! 342: SHA1Update(&sha1ctx, md, 20);
! 343: SHA1Final(md, &sha1ctx);
! 344: }
! 345:
! 346: int
! 347: carp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2],
! 348: unsigned char md[20])
! 349: {
! 350: unsigned char md2[20];
! 351: u_int8_t i;
! 352:
! 353: for (i=0; i < HMAC_MAX; i++) {
! 354: carp_hmac_generate(sc, counter, md2, i);
! 355: if (!bcmp(md, md2, sizeof(md2)))
! 356: return (0);
! 357: }
! 358: return (1);
! 359: }
! 360:
! 361: void
! 362: carp_setroute(struct carp_softc *sc, int cmd)
! 363: {
! 364: struct ifaddr *ifa;
! 365: int s;
! 366:
! 367: /* XXX this mess needs fixing */
! 368:
! 369: s = splsoftnet();
! 370: TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
! 371: switch (ifa->ifa_addr->sa_family) {
! 372: case AF_INET: {
! 373: int count = 0, error;
! 374: struct sockaddr sa;
! 375: struct rtentry *rt;
! 376: struct radix_node_head *rnh;
! 377: struct radix_node *rn;
! 378: struct rt_addrinfo info;
! 379: int hr_otherif, nr_ourif;
! 380: struct sockaddr_rtlabel sa_rl;
! 381: const char *label;
! 382:
! 383: /*
! 384: * Avoid screwing with the routes if there are other
! 385: * carp interfaces which are master and have the same
! 386: * address.
! 387: */
! 388: if (sc->sc_carpdev != NULL &&
! 389: sc->sc_carpdev->if_carp != NULL) {
! 390: count = carp_addrcount(
! 391: (struct carp_if *)sc->sc_carpdev->if_carp,
! 392: ifa, CARP_COUNT_MASTER);
! 393: if ((cmd == RTM_ADD && count != 1) ||
! 394: (cmd == RTM_DELETE && count != 0))
! 395: continue;
! 396: }
! 397:
! 398: /* Remove the existing host route, if any */
! 399: bzero(&info, sizeof(info));
! 400: info.rti_info[RTAX_DST] = ifa->ifa_addr;
! 401: info.rti_flags = RTF_HOST;
! 402: error = rtrequest1(RTM_DELETE, &info, NULL, 0);
! 403: rt_missmsg(RTM_DELETE, &info, info.rti_flags, NULL,
! 404: error, 0);
! 405:
! 406:
! 407: /* Check for our address on another interface */
! 408: /* XXX cries for proper API */
! 409: rnh = rt_gettable(ifa->ifa_addr->sa_family, 0);
! 410: rn = rnh->rnh_matchaddr(ifa->ifa_addr, rnh);
! 411: rt = (struct rtentry *)rn;
! 412: hr_otherif = (rt && rt->rt_ifp != &sc->sc_if &&
! 413: rt->rt_flags & (RTF_CLONING|RTF_CLONED));
! 414:
! 415: /* Check for a network route on our interface */
! 416: bcopy(ifa->ifa_addr, &sa, sizeof(sa));
! 417: satosin(&sa)->sin_addr.s_addr = satosin(ifa->ifa_netmask
! 418: )->sin_addr.s_addr & satosin(&sa)->sin_addr.s_addr;
! 419: rt = (struct rtentry *)rt_lookup(&sa,
! 420: ifa->ifa_netmask, 0);
! 421: nr_ourif = (rt && rt->rt_ifp == &sc->sc_if);
! 422:
! 423: /* Restore the route label */
! 424: bzero(&sa_rl, sizeof(sa_rl));
! 425: if (rt && rt->rt_labelid) {
! 426: sa_rl.sr_len = sizeof(sa_rl);
! 427: sa_rl.sr_family = AF_UNSPEC;
! 428: label = rtlabel_id2name(rt->rt_labelid);
! 429: if (label != NULL)
! 430: strlcpy(sa_rl.sr_label, label,
! 431: sizeof(sa_rl.sr_label));
! 432: }
! 433:
! 434: switch (cmd) {
! 435: case RTM_ADD:
! 436: if (hr_otherif) {
! 437: ifa->ifa_rtrequest = NULL;
! 438: ifa->ifa_flags &= ~RTF_CLONING;
! 439: bzero(&info, sizeof(info));
! 440: info.rti_info[RTAX_DST] = ifa->ifa_addr;
! 441: info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
! 442: info.rti_flags = RTF_UP | RTF_HOST;
! 443: error = rtrequest1(RTM_ADD, &info, NULL, 0);
! 444: rt_missmsg(RTM_ADD, &info, info.rti_flags, NULL,
! 445: error, 0);
! 446: }
! 447: if (!hr_otherif || nr_ourif || !rt) {
! 448: if (nr_ourif && !(rt->rt_flags &
! 449: RTF_CLONING)) {
! 450: bzero(&info, sizeof(info));
! 451: info.rti_info[RTAX_DST] = &sa;
! 452: info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
! 453: error = rtrequest1(RTM_DELETE, &info, NULL, 0);
! 454: rt_missmsg(RTM_DELETE, &info, info.rti_flags, NULL,
! 455: error, 0);
! 456: }
! 457:
! 458: ifa->ifa_rtrequest = arp_rtrequest;
! 459: ifa->ifa_flags |= RTF_CLONING;
! 460:
! 461: bzero(&info, sizeof(info));
! 462: info.rti_info[RTAX_DST] = &sa;
! 463: info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
! 464: info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
! 465: info.rti_info[RTAX_LABEL] =
! 466: (struct sockaddr *)&sa_rl;
! 467: error = rtrequest1(RTM_ADD, &info, NULL, 0);
! 468: if (error == 0)
! 469: ifa->ifa_flags |= IFA_ROUTE;
! 470: rt_missmsg(RTM_ADD, &info, info.rti_flags, NULL,
! 471: error, 0);
! 472: }
! 473: break;
! 474: case RTM_DELETE:
! 475: break;
! 476: default:
! 477: break;
! 478: }
! 479: break;
! 480: }
! 481:
! 482: #ifdef INET6
! 483: case AF_INET6:
! 484: if (cmd == RTM_ADD)
! 485: in6_ifaddloop(ifa);
! 486: else
! 487: in6_ifremloop(ifa);
! 488: break;
! 489: #endif /* INET6 */
! 490: default:
! 491: break;
! 492: }
! 493: }
! 494: splx(s);
! 495: }
! 496:
! 497: /*
! 498: * process input packet.
! 499: * we have rearranged checks order compared to the rfc,
! 500: * but it seems more efficient this way or not possible otherwise.
! 501: */
! 502: void
! 503: carp_proto_input(struct mbuf *m, ...)
! 504: {
! 505: struct ip *ip = mtod(m, struct ip *);
! 506: struct carp_softc *sc = NULL;
! 507: struct carp_header *ch;
! 508: int iplen, len, hlen;
! 509: va_list ap;
! 510:
! 511: va_start(ap, m);
! 512: hlen = va_arg(ap, int);
! 513: va_end(ap);
! 514:
! 515: carpstats.carps_ipackets++;
! 516:
! 517: if (!carp_opts[CARPCTL_ALLOW]) {
! 518: m_freem(m);
! 519: return;
! 520: }
! 521:
! 522: /* check if received on a valid carp interface */
! 523: if (m->m_pkthdr.rcvif->if_type != IFT_CARP) {
! 524: carpstats.carps_badif++;
! 525: CARP_LOG(sc, ("packet received on non-carp interface: %s",
! 526: m->m_pkthdr.rcvif->if_xname));
! 527: m_freem(m);
! 528: return;
! 529: }
! 530:
! 531: /* verify that the IP TTL is 255. */
! 532: if (ip->ip_ttl != CARP_DFLTTL) {
! 533: carpstats.carps_badttl++;
! 534: CARP_LOG(sc, ("received ttl %d != %d on %s", ip->ip_ttl,
! 535: CARP_DFLTTL, m->m_pkthdr.rcvif->if_xname));
! 536: m_freem(m);
! 537: return;
! 538: }
! 539:
! 540: /*
! 541: * verify that the received packet length is
! 542: * equal to the CARP header
! 543: */
! 544: iplen = ip->ip_hl << 2;
! 545: len = iplen + sizeof(*ch);
! 546: if (len > m->m_pkthdr.len) {
! 547: carpstats.carps_badlen++;
! 548: CARP_LOG(sc, ("packet too short %d on %s", m->m_pkthdr.len,
! 549: m->m_pkthdr.rcvif->if_xname));
! 550: m_freem(m);
! 551: return;
! 552: }
! 553:
! 554: if ((m = m_pullup2(m, len)) == NULL) {
! 555: carpstats.carps_hdrops++;
! 556: return;
! 557: }
! 558: ip = mtod(m, struct ip *);
! 559: ch = (void *)ip + iplen;
! 560:
! 561: /* verify the CARP checksum */
! 562: m->m_data += iplen;
! 563: if (carp_cksum(m, len - iplen)) {
! 564: carpstats.carps_badsum++;
! 565: CARP_LOG(sc, ("checksum failed on %s",
! 566: m->m_pkthdr.rcvif->if_xname));
! 567: m_freem(m);
! 568: return;
! 569: }
! 570: m->m_data -= iplen;
! 571:
! 572: carp_proto_input_c(m, ch, AF_INET);
! 573: }
! 574:
! 575: #ifdef INET6
! 576: int
! 577: carp6_proto_input(struct mbuf **mp, int *offp, int proto)
! 578: {
! 579: struct mbuf *m = *mp;
! 580: struct carp_softc *sc = NULL;
! 581: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 582: struct carp_header *ch;
! 583: u_int len;
! 584:
! 585: carpstats.carps_ipackets6++;
! 586:
! 587: if (!carp_opts[CARPCTL_ALLOW]) {
! 588: m_freem(m);
! 589: return (IPPROTO_DONE);
! 590: }
! 591:
! 592: /* check if received on a valid carp interface */
! 593: if (m->m_pkthdr.rcvif->if_type != IFT_CARP) {
! 594: carpstats.carps_badif++;
! 595: CARP_LOG(sc, ("packet received on non-carp interface: %s",
! 596: m->m_pkthdr.rcvif->if_xname));
! 597: m_freem(m);
! 598: return (IPPROTO_DONE);
! 599: }
! 600:
! 601: /* verify that the IP TTL is 255 */
! 602: if (ip6->ip6_hlim != CARP_DFLTTL) {
! 603: carpstats.carps_badttl++;
! 604: CARP_LOG(sc, ("received ttl %d != %d on %s", ip6->ip6_hlim,
! 605: CARP_DFLTTL, m->m_pkthdr.rcvif->if_xname));
! 606: m_freem(m);
! 607: return (IPPROTO_DONE);
! 608: }
! 609:
! 610: /* verify that we have a complete carp packet */
! 611: len = m->m_len;
! 612: IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch));
! 613: if (ch == NULL) {
! 614: carpstats.carps_badlen++;
! 615: CARP_LOG(sc, ("packet size %u too small", len));
! 616: return (IPPROTO_DONE);
! 617: }
! 618:
! 619:
! 620: /* verify the CARP checksum */
! 621: m->m_data += *offp;
! 622: if (carp_cksum(m, sizeof(*ch))) {
! 623: carpstats.carps_badsum++;
! 624: CARP_LOG(sc, ("checksum failed, on %s",
! 625: m->m_pkthdr.rcvif->if_xname));
! 626: m_freem(m);
! 627: return (IPPROTO_DONE);
! 628: }
! 629: m->m_data -= *offp;
! 630:
! 631: carp_proto_input_c(m, ch, AF_INET6);
! 632: return (IPPROTO_DONE);
! 633: }
! 634: #endif /* INET6 */
! 635:
! 636: void
! 637: carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af)
! 638: {
! 639: struct carp_softc *sc;
! 640: u_int64_t tmp_counter;
! 641: struct timeval sc_tv, ch_tv;
! 642:
! 643: TAILQ_FOREACH(sc, &((struct carp_if *)
! 644: m->m_pkthdr.rcvif->if_carpdev->if_carp)->vhif_vrs, sc_list)
! 645: if (sc->sc_vhid == ch->carp_vhid)
! 646: break;
! 647:
! 648: if (!sc || (sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
! 649: (IFF_UP|IFF_RUNNING)) {
! 650: carpstats.carps_badvhid++;
! 651: m_freem(m);
! 652: return;
! 653: }
! 654:
! 655: /*
! 656: * Check if our own advertisement was duplicated
! 657: * from a non simplex interface.
! 658: * XXX If there is no address on our physical interface
! 659: * there is no way to distinguish our ads from the ones
! 660: * another carp host might have sent us.
! 661: */
! 662: if ((sc->sc_carpdev->if_flags & IFF_SIMPLEX) == 0) {
! 663: struct sockaddr sa;
! 664: struct ifaddr *ifa;
! 665:
! 666: bzero(&sa, sizeof(sa));
! 667: sa.sa_family = af;
! 668: ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev);
! 669:
! 670: if (ifa && af == AF_INET) {
! 671: struct ip *ip = mtod(m, struct ip *);
! 672: if (ip->ip_src.s_addr ==
! 673: ifatoia(ifa)->ia_addr.sin_addr.s_addr) {
! 674: m_freem(m);
! 675: return;
! 676: }
! 677: }
! 678: #ifdef INET6
! 679: if (ifa && af == AF_INET6) {
! 680: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 681: struct in6_addr in6_src, in6_found;
! 682:
! 683: in6_src = ip6->ip6_src;
! 684: in6_found = ifatoia6(ifa)->ia_addr.sin6_addr;
! 685: if (IN6_IS_SCOPE_EMBED(&in6_src))
! 686: in6_src.s6_addr16[1] = 0;
! 687: if (IN6_IS_SCOPE_EMBED(&in6_found))
! 688: in6_found.s6_addr16[1] = 0;
! 689: if (IN6_ARE_ADDR_EQUAL(&in6_src, &in6_found)) {
! 690: m_freem(m);
! 691: return;
! 692: }
! 693: }
! 694: #endif /* INET6 */
! 695: }
! 696:
! 697: getmicrotime(&sc->sc_if.if_lastchange);
! 698: sc->sc_if.if_ipackets++;
! 699: sc->sc_if.if_ibytes += m->m_pkthdr.len;
! 700:
! 701: /* verify the CARP version. */
! 702: if (ch->carp_version != CARP_VERSION) {
! 703: carpstats.carps_badver++;
! 704: sc->sc_if.if_ierrors++;
! 705: CARP_LOG(sc, ("invalid version %d != %d",
! 706: ch->carp_version, CARP_VERSION));
! 707: m_freem(m);
! 708: return;
! 709: }
! 710:
! 711: /* verify the hash */
! 712: if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) {
! 713: carpstats.carps_badauth++;
! 714: sc->sc_if.if_ierrors++;
! 715: CARP_LOG(sc, ("incorrect hash"));
! 716: m_freem(m);
! 717: return;
! 718: }
! 719:
! 720: tmp_counter = ntohl(ch->carp_counter[0]);
! 721: tmp_counter = tmp_counter<<32;
! 722: tmp_counter += ntohl(ch->carp_counter[1]);
! 723:
! 724: /* XXX Replay protection goes here */
! 725:
! 726: sc->sc_init_counter = 0;
! 727: sc->sc_counter = tmp_counter;
! 728:
! 729:
! 730: sc_tv.tv_sec = sc->sc_advbase;
! 731: if (carp_group_demote_count(sc) && sc->sc_advskew < 240)
! 732: sc_tv.tv_usec = 240 * 1000000 / 256;
! 733: else
! 734: sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256;
! 735: ch_tv.tv_sec = ch->carp_advbase;
! 736: ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256;
! 737:
! 738: switch (sc->sc_state) {
! 739: case INIT:
! 740: break;
! 741: case MASTER:
! 742: /*
! 743: * If we receive an advertisement from a master who's going to
! 744: * be more frequent than us, go into BACKUP state.
! 745: */
! 746: if (timercmp(&sc_tv, &ch_tv, >) ||
! 747: (timercmp(&sc_tv, &ch_tv, ==) &&
! 748: ch->carp_demote <=
! 749: (carp_group_demote_count(sc) & 0xff))) {
! 750: timeout_del(&sc->sc_ad_tmo);
! 751: carp_set_state(sc, BACKUP);
! 752: carp_setrun(sc, 0);
! 753: carp_setroute(sc, RTM_DELETE);
! 754: }
! 755: break;
! 756: case BACKUP:
! 757: /*
! 758: * If we're pre-empting masters who advertise slower than us,
! 759: * and this one claims to be slower, treat him as down.
! 760: */
! 761: if (carp_opts[CARPCTL_PREEMPT] && timercmp(&sc_tv, &ch_tv, <)) {
! 762: carp_master_down(sc);
! 763: break;
! 764: }
! 765:
! 766: /*
! 767: * Take over masters advertising with a higher demote count,
! 768: * regardless of CARPCTL_PREEMPT.
! 769: */
! 770: if (ch->carp_demote > (carp_group_demote_count(sc) & 0xff)) {
! 771: carp_master_down(sc);
! 772: break;
! 773: }
! 774:
! 775: /*
! 776: * If the master is going to advertise at such a low frequency
! 777: * that he's guaranteed to time out, we'd might as well just
! 778: * treat him as timed out now.
! 779: */
! 780: sc_tv.tv_sec = sc->sc_advbase * 3;
! 781: if (timercmp(&sc_tv, &ch_tv, <)) {
! 782: carp_master_down(sc);
! 783: break;
! 784: }
! 785:
! 786: /*
! 787: * Otherwise, we reset the counter and wait for the next
! 788: * advertisement.
! 789: */
! 790: carp_setrun(sc, af);
! 791: break;
! 792: }
! 793:
! 794: m_freem(m);
! 795: return;
! 796: }
! 797:
! 798: int
! 799: carp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
! 800: size_t newlen)
! 801: {
! 802: /* All sysctl names at this level are terminal. */
! 803: if (namelen != 1)
! 804: return (ENOTDIR);
! 805:
! 806: if (name[0] <= 0 || name[0] >= CARPCTL_MAXID)
! 807: return (ENOPROTOOPT);
! 808:
! 809: return sysctl_int(oldp, oldlenp, newp, newlen, &carp_opts[name[0]]);
! 810: }
! 811:
! 812: /*
! 813: * Interface side of the CARP implementation.
! 814: */
! 815:
! 816: /* ARGSUSED */
! 817: void
! 818: carpattach(int n)
! 819: {
! 820: struct ifg_group *ifg;
! 821:
! 822: if ((ifg = if_creategroup("carp")) != NULL)
! 823: ifg->ifg_refcnt++; /* keep around even if empty */
! 824: if_clone_attach(&carp_cloner);
! 825: }
! 826:
! 827: int
! 828: carp_clone_create(ifc, unit)
! 829: struct if_clone *ifc;
! 830: int unit;
! 831: {
! 832: struct carp_softc *sc;
! 833: struct ifnet *ifp;
! 834:
! 835: sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT);
! 836: if (!sc)
! 837: return (ENOMEM);
! 838: bzero(sc, sizeof(*sc));
! 839:
! 840: sc->sc_suppress = 0;
! 841: sc->sc_advbase = CARP_DFLTINTV;
! 842: sc->sc_vhid = -1; /* required setting */
! 843: sc->sc_advskew = 0;
! 844: sc->sc_init_counter = 1;
! 845: sc->sc_naddrs = sc->sc_naddrs6 = 0;
! 846: #ifdef INET6
! 847: sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL;
! 848: #endif /* INET6 */
! 849:
! 850: timeout_set(&sc->sc_ad_tmo, carp_send_ad, sc);
! 851: timeout_set(&sc->sc_md_tmo, carp_master_down, sc);
! 852: timeout_set(&sc->sc_md6_tmo, carp_master_down, sc);
! 853:
! 854: LIST_INIT(&sc->carp_mc_listhead);
! 855: ifp = &sc->sc_if;
! 856: ifp->if_softc = sc;
! 857: snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
! 858: unit);
! 859: ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
! 860: ifp->if_ioctl = carp_ioctl;
! 861: ifp->if_start = carp_start;
! 862: ifp->if_output = carp_output;
! 863: ifp->if_type = IFT_CARP;
! 864: ifp->if_addrlen = ETHER_ADDR_LEN;
! 865: ifp->if_hdrlen = ETHER_HDR_LEN;
! 866: ifp->if_mtu = ETHERMTU;
! 867: IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
! 868: IFQ_SET_READY(&ifp->if_snd);
! 869: if_attach(ifp);
! 870:
! 871: if_alloc_sadl(ifp);
! 872: LIST_INIT(&sc->sc_ac.ac_multiaddrs);
! 873: #if NBPFILTER > 0
! 874: bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN);
! 875: #endif
! 876: return (0);
! 877: }
! 878:
! 879: int
! 880: carp_clone_destroy(struct ifnet *ifp)
! 881: {
! 882: carpdetach(ifp->if_softc);
! 883: ether_ifdetach(ifp);
! 884: if_detach(ifp);
! 885: free(ifp->if_softc, M_DEVBUF);
! 886:
! 887: return (0);
! 888: }
! 889:
! 890: void
! 891: carpdetach(struct carp_softc *sc)
! 892: {
! 893: struct carp_if *cif;
! 894: int s;
! 895:
! 896: timeout_del(&sc->sc_ad_tmo);
! 897: timeout_del(&sc->sc_md_tmo);
! 898: timeout_del(&sc->sc_md6_tmo);
! 899:
! 900: if (sc->sc_suppress)
! 901: carp_group_demote_adj(&sc->sc_if, -1);
! 902: sc->sc_suppress = 0;
! 903:
! 904: if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS)
! 905: carp_group_demote_adj(&sc->sc_if, -1);
! 906: sc->sc_sendad_errors = 0;
! 907:
! 908: carp_set_state(sc, INIT);
! 909: sc->sc_if.if_flags &= ~IFF_UP;
! 910: carp_setrun(sc, 0);
! 911: carp_multicast_cleanup(sc);
! 912:
! 913: s = splnet();
! 914: if (sc->sc_carpdev != NULL) {
! 915: if (sc->lh_cookie != NULL)
! 916: hook_disestablish(sc->sc_carpdev->if_linkstatehooks,
! 917: sc->lh_cookie);
! 918: cif = (struct carp_if *)sc->sc_carpdev->if_carp;
! 919: TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
! 920: if (!--cif->vhif_nvrs) {
! 921: ifpromisc(sc->sc_carpdev, 0);
! 922: sc->sc_carpdev->if_carp = NULL;
! 923: FREE(cif, M_IFADDR);
! 924: }
! 925: }
! 926: sc->sc_carpdev = NULL;
! 927: splx(s);
! 928: }
! 929:
! 930: /* Detach an interface from the carp. */
! 931: void
! 932: carp_ifdetach(struct ifnet *ifp)
! 933: {
! 934: struct carp_softc *sc, *nextsc;
! 935: struct carp_if *cif = (struct carp_if *)ifp->if_carp;
! 936:
! 937: for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) {
! 938: nextsc = TAILQ_NEXT(sc, sc_list);
! 939: carpdetach(sc);
! 940: }
! 941: }
! 942:
! 943: int
! 944: carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch)
! 945: {
! 946: if (sc->sc_init_counter) {
! 947: /* this could also be seconds since unix epoch */
! 948: sc->sc_counter = arc4random();
! 949: sc->sc_counter = sc->sc_counter << 32;
! 950: sc->sc_counter += arc4random();
! 951: } else
! 952: sc->sc_counter++;
! 953:
! 954: ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff);
! 955: ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff);
! 956:
! 957: /*
! 958: * For the time being, do not include the IPv6 linklayer addresses
! 959: * in the HMAC.
! 960: */
! 961: carp_hmac_generate(sc, ch->carp_counter, ch->carp_md, HMAC_NOV6LL);
! 962:
! 963: return (0);
! 964: }
! 965:
! 966: void
! 967: carp_send_ad_all(void)
! 968: {
! 969: struct ifnet *ifp;
! 970: struct carp_if *cif;
! 971: struct carp_softc *vh;
! 972:
! 973: TAILQ_FOREACH(ifp, &ifnet, if_list) {
! 974: if (ifp->if_carp == NULL || ifp->if_type == IFT_CARP)
! 975: continue;
! 976:
! 977: cif = (struct carp_if *)ifp->if_carp;
! 978: TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
! 979: if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) ==
! 980: (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER)
! 981: carp_send_ad(vh);
! 982: }
! 983: }
! 984: }
! 985:
! 986:
! 987: void
! 988: carp_send_ad(void *v)
! 989: {
! 990: struct carp_header ch;
! 991: struct timeval tv;
! 992: struct carp_softc *sc = v;
! 993: struct carp_header *ch_ptr;
! 994: struct mbuf *m;
! 995: int error, len, advbase, advskew, s;
! 996: struct ifaddr *ifa;
! 997: struct sockaddr sa;
! 998:
! 999: s = splsoftnet();
! 1000:
! 1001: if (sc->sc_carpdev == NULL) {
! 1002: sc->sc_if.if_oerrors++;
! 1003: goto retry_later;
! 1004: }
! 1005:
! 1006: /* bow out if we've gone to backup (the carp interface is going down) */
! 1007: if (sc->sc_bow_out) {
! 1008: sc->sc_bow_out = 0;
! 1009: advbase = 255;
! 1010: advskew = 255;
! 1011: } else {
! 1012: advbase = sc->sc_advbase;
! 1013: if (!carp_group_demote_count(sc) || sc->sc_advskew > 240)
! 1014: advskew = sc->sc_advskew;
! 1015: else
! 1016: advskew = 240;
! 1017: tv.tv_sec = advbase;
! 1018: tv.tv_usec = advskew * 1000000 / 256;
! 1019: }
! 1020:
! 1021: ch.carp_version = CARP_VERSION;
! 1022: ch.carp_type = CARP_ADVERTISEMENT;
! 1023: ch.carp_vhid = sc->sc_vhid;
! 1024: ch.carp_demote = carp_group_demote_count(sc) & 0xff;
! 1025: ch.carp_advbase = advbase;
! 1026: ch.carp_advskew = advskew;
! 1027: ch.carp_authlen = 7; /* XXX DEFINE */
! 1028: ch.carp_cksum = 0;
! 1029:
! 1030:
! 1031: #ifdef INET
! 1032: if (sc->sc_naddrs) {
! 1033: struct ip *ip;
! 1034:
! 1035: MGETHDR(m, M_DONTWAIT, MT_HEADER);
! 1036: if (m == NULL) {
! 1037: sc->sc_if.if_oerrors++;
! 1038: carpstats.carps_onomem++;
! 1039: /* XXX maybe less ? */
! 1040: goto retry_later;
! 1041: }
! 1042: len = sizeof(*ip) + sizeof(ch);
! 1043: m->m_pkthdr.len = len;
! 1044: m->m_pkthdr.rcvif = NULL;
! 1045: m->m_len = len;
! 1046: MH_ALIGN(m, m->m_len);
! 1047: m->m_flags |= M_MCAST;
! 1048: ip = mtod(m, struct ip *);
! 1049: ip->ip_v = IPVERSION;
! 1050: ip->ip_hl = sizeof(*ip) >> 2;
! 1051: ip->ip_tos = IPTOS_LOWDELAY;
! 1052: ip->ip_len = htons(len);
! 1053: ip->ip_id = htons(ip_randomid());
! 1054: ip->ip_off = htons(IP_DF);
! 1055: ip->ip_ttl = CARP_DFLTTL;
! 1056: ip->ip_p = IPPROTO_CARP;
! 1057: ip->ip_sum = 0;
! 1058:
! 1059: bzero(&sa, sizeof(sa));
! 1060: sa.sa_family = AF_INET;
! 1061: ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev);
! 1062: if (ifa == NULL)
! 1063: ip->ip_src.s_addr = 0;
! 1064: else
! 1065: ip->ip_src.s_addr =
! 1066: ifatoia(ifa)->ia_addr.sin_addr.s_addr;
! 1067: ip->ip_dst.s_addr = INADDR_CARP_GROUP;
! 1068:
! 1069: ch_ptr = (void *)ip + sizeof(*ip);
! 1070: bcopy(&ch, ch_ptr, sizeof(ch));
! 1071: if (carp_prepare_ad(m, sc, ch_ptr))
! 1072: goto retry_later;
! 1073:
! 1074: m->m_data += sizeof(*ip);
! 1075: ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip));
! 1076: m->m_data -= sizeof(*ip);
! 1077:
! 1078: getmicrotime(&sc->sc_if.if_lastchange);
! 1079: sc->sc_if.if_opackets++;
! 1080: sc->sc_if.if_obytes += len;
! 1081: carpstats.carps_opackets++;
! 1082:
! 1083: error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo,
! 1084: NULL);
! 1085: if (error) {
! 1086: if (error == ENOBUFS)
! 1087: carpstats.carps_onomem++;
! 1088: else
! 1089: CARP_LOG(sc, ("ip_output failed: %d", error));
! 1090: sc->sc_if.if_oerrors++;
! 1091: if (sc->sc_sendad_errors < INT_MAX)
! 1092: sc->sc_sendad_errors++;
! 1093: if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS)
! 1094: carp_group_demote_adj(&sc->sc_if, 1);
! 1095: sc->sc_sendad_success = 0;
! 1096: } else {
! 1097: if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) {
! 1098: if (++sc->sc_sendad_success >=
! 1099: CARP_SENDAD_MIN_SUCCESS) {
! 1100: carp_group_demote_adj(&sc->sc_if, -1);
! 1101: sc->sc_sendad_errors = 0;
! 1102: }
! 1103: } else
! 1104: sc->sc_sendad_errors = 0;
! 1105: }
! 1106: if (sc->sc_delayed_arp > 0)
! 1107: sc->sc_delayed_arp--;
! 1108: if (sc->sc_delayed_arp == 0) {
! 1109: carp_send_arp(sc);
! 1110: sc->sc_delayed_arp = -1;
! 1111: }
! 1112: }
! 1113: #endif /* INET */
! 1114: #ifdef INET6
! 1115: if (sc->sc_naddrs6) {
! 1116: struct ip6_hdr *ip6;
! 1117:
! 1118: MGETHDR(m, M_DONTWAIT, MT_HEADER);
! 1119: if (m == NULL) {
! 1120: sc->sc_if.if_oerrors++;
! 1121: carpstats.carps_onomem++;
! 1122: /* XXX maybe less ? */
! 1123: goto retry_later;
! 1124: }
! 1125: len = sizeof(*ip6) + sizeof(ch);
! 1126: m->m_pkthdr.len = len;
! 1127: m->m_pkthdr.rcvif = NULL;
! 1128: m->m_len = len;
! 1129: MH_ALIGN(m, m->m_len);
! 1130: m->m_flags |= M_MCAST;
! 1131: ip6 = mtod(m, struct ip6_hdr *);
! 1132: bzero(ip6, sizeof(*ip6));
! 1133: ip6->ip6_vfc |= IPV6_VERSION;
! 1134: ip6->ip6_hlim = CARP_DFLTTL;
! 1135: ip6->ip6_nxt = IPPROTO_CARP;
! 1136:
! 1137: /* set the source address */
! 1138: bzero(&sa, sizeof(sa));
! 1139: sa.sa_family = AF_INET6;
! 1140: ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev);
! 1141: if (ifa == NULL) /* This should never happen with IPv6 */
! 1142: bzero(&ip6->ip6_src, sizeof(struct in6_addr));
! 1143: else
! 1144: bcopy(ifatoia6(ifa)->ia_addr.sin6_addr.s6_addr,
! 1145: &ip6->ip6_src, sizeof(struct in6_addr));
! 1146: /* set the multicast destination */
! 1147:
! 1148: ip6->ip6_dst.s6_addr8[0] = 0xff;
! 1149: ip6->ip6_dst.s6_addr8[1] = 0x02;
! 1150: ip6->ip6_dst.s6_addr8[15] = 0x12;
! 1151:
! 1152: ch_ptr = (void *)ip6 + sizeof(*ip6);
! 1153: bcopy(&ch, ch_ptr, sizeof(ch));
! 1154: if (carp_prepare_ad(m, sc, ch_ptr))
! 1155: goto retry_later;
! 1156:
! 1157: m->m_data += sizeof(*ip6);
! 1158: ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6));
! 1159: m->m_data -= sizeof(*ip6);
! 1160:
! 1161: getmicrotime(&sc->sc_if.if_lastchange);
! 1162: sc->sc_if.if_opackets++;
! 1163: sc->sc_if.if_obytes += len;
! 1164: carpstats.carps_opackets6++;
! 1165:
! 1166: error = ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL);
! 1167: if (error) {
! 1168: if (error == ENOBUFS)
! 1169: carpstats.carps_onomem++;
! 1170: else
! 1171: CARP_LOG(sc, ("ip6_output failed: %d", error));
! 1172: sc->sc_if.if_oerrors++;
! 1173: if (sc->sc_sendad_errors < INT_MAX)
! 1174: sc->sc_sendad_errors++;
! 1175: if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS)
! 1176: carp_group_demote_adj(&sc->sc_if, 1);
! 1177: sc->sc_sendad_success = 0;
! 1178: } else {
! 1179: if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) {
! 1180: if (++sc->sc_sendad_success >=
! 1181: CARP_SENDAD_MIN_SUCCESS) {
! 1182: carp_group_demote_adj(&sc->sc_if, -1);
! 1183: sc->sc_sendad_errors = 0;
! 1184: }
! 1185: } else
! 1186: sc->sc_sendad_errors = 0;
! 1187: }
! 1188: }
! 1189: #endif /* INET6 */
! 1190:
! 1191: retry_later:
! 1192: splx(s);
! 1193: if (advbase != 255 || advskew != 255)
! 1194: timeout_add(&sc->sc_ad_tmo, tvtohz(&tv));
! 1195: }
! 1196:
! 1197: /*
! 1198: * Broadcast a gratuitous ARP request containing
! 1199: * the virtual router MAC address for each IP address
! 1200: * associated with the virtual router.
! 1201: */
! 1202: void
! 1203: carp_send_arp(struct carp_softc *sc)
! 1204: {
! 1205: struct ifaddr *ifa;
! 1206: in_addr_t in;
! 1207: int s = splsoftnet();
! 1208:
! 1209: TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
! 1210:
! 1211: if (ifa->ifa_addr->sa_family != AF_INET)
! 1212: continue;
! 1213:
! 1214: if (carp_addrcount((struct carp_if *)sc->sc_carpdev->if_carp,
! 1215: ifa, CARP_COUNT_LINK0))
! 1216: continue;
! 1217:
! 1218: in = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
! 1219: arprequest(sc->sc_carpdev, &in, &in, sc->sc_ac.ac_enaddr);
! 1220: DELAY(1000); /* XXX */
! 1221: }
! 1222: splx(s);
! 1223: }
! 1224:
! 1225: #ifdef INET6
! 1226: void
! 1227: carp_send_na(struct carp_softc *sc)
! 1228: {
! 1229: struct ifaddr *ifa;
! 1230: struct in6_addr *in6;
! 1231: static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
! 1232: int s = splsoftnet();
! 1233:
! 1234: TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
! 1235:
! 1236: if (ifa->ifa_addr->sa_family != AF_INET6)
! 1237: continue;
! 1238:
! 1239: in6 = &ifatoia6(ifa)->ia_addr.sin6_addr;
! 1240: nd6_na_output(sc->sc_carpdev, &mcast, in6,
! 1241: ND_NA_FLAG_OVERRIDE, 1, NULL);
! 1242: DELAY(1000); /* XXX */
! 1243: }
! 1244: splx(s);
! 1245: }
! 1246: #endif /* INET6 */
! 1247:
! 1248: /*
! 1249: * Based on bridge_hash() in if_bridge.c
! 1250: */
! 1251: #define mix(a,b,c) \
! 1252: do { \
! 1253: a -= b; a -= c; a ^= (c >> 13); \
! 1254: b -= c; b -= a; b ^= (a << 8); \
! 1255: c -= a; c -= b; c ^= (b >> 13); \
! 1256: a -= b; a -= c; a ^= (c >> 12); \
! 1257: b -= c; b -= a; b ^= (a << 16); \
! 1258: c -= a; c -= b; c ^= (b >> 5); \
! 1259: a -= b; a -= c; a ^= (c >> 3); \
! 1260: b -= c; b -= a; b ^= (a << 10); \
! 1261: c -= a; c -= b; c ^= (b >> 15); \
! 1262: } while (0)
! 1263:
! 1264: u_int32_t
! 1265: carp_hash(struct carp_softc *sc, u_char *src)
! 1266: {
! 1267: u_int32_t a = 0x9e3779b9, b = sc->sc_hashkey[0], c = sc->sc_hashkey[1];
! 1268:
! 1269: c += sc->sc_key[3] << 24;
! 1270: c += sc->sc_key[2] << 16;
! 1271: c += sc->sc_key[1] << 8;
! 1272: c += sc->sc_key[0];
! 1273: b += src[5] << 8;
! 1274: b += src[4];
! 1275: a += src[3] << 24;
! 1276: a += src[2] << 16;
! 1277: a += src[1] << 8;
! 1278: a += src[0];
! 1279:
! 1280: mix(a, b, c);
! 1281: return (c);
! 1282: }
! 1283:
! 1284: int
! 1285: carp_addrcount(struct carp_if *cif, struct ifaddr *ifa0, int type)
! 1286: {
! 1287: struct carp_softc *vh;
! 1288: struct ifaddr *ifa;
! 1289: int count = 0;
! 1290:
! 1291: TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
! 1292: switch (type) {
! 1293: case CARP_COUNT_RUNNING:
! 1294: if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
! 1295: (IFF_UP|IFF_RUNNING))
! 1296: continue;
! 1297: break;
! 1298: case CARP_COUNT_MASTER:
! 1299: if (vh->sc_state != MASTER)
! 1300: continue;
! 1301: break;
! 1302: case CARP_COUNT_LINK0:
! 1303: if (!(vh->sc_if.if_flags & IFF_LINK0) ||
! 1304: (vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
! 1305: (IFF_UP|IFF_RUNNING))
! 1306: continue;
! 1307: break;
! 1308: }
! 1309: TAILQ_FOREACH(ifa, &vh->sc_if.if_addrlist, ifa_list) {
! 1310: if (ifa->ifa_addr->sa_family == AF_INET &&
! 1311: ifa0->ifa_addr->sa_family == AF_INET &&
! 1312: ifatoia(ifa0)->ia_addr.sin_addr.s_addr ==
! 1313: ifatoia(ifa)->ia_addr.sin_addr.s_addr)
! 1314: count++;
! 1315: #ifdef INET6
! 1316: if (ifa->ifa_addr->sa_family == AF_INET6 &&
! 1317: ifa0->ifa_addr->sa_family == AF_INET6 &&
! 1318: IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa0), IFA_IN6(ifa)))
! 1319: count++;
! 1320: #endif
! 1321: }
! 1322: }
! 1323: return (count);
! 1324: }
! 1325:
! 1326: void
! 1327: carp_update_lsmask(struct carp_softc *sc)
! 1328: {
! 1329: struct carp_softc *curvh, *vh, *sc0 = NULL;
! 1330: struct carp_if *cif;
! 1331: struct ifaddr *ifa, *ifa0 = NULL;
! 1332: int cur, last, count, found;
! 1333:
! 1334: if (!sc->sc_carpdev)
! 1335: return;
! 1336: cif = (struct carp_if *)sc->sc_carpdev->if_carp;
! 1337:
! 1338: /*
! 1339: * Take the first IPv4 address from the LINK0 carp interface
! 1340: * to determine the load sharing group.
! 1341: * Fallback on the first IPv6 address.
! 1342: */
! 1343: TAILQ_FOREACH(sc0, &cif->vhif_vrs, sc_list)
! 1344: if (sc0->sc_if.if_flags & IFF_LINK0)
! 1345: break;
! 1346: if (sc0 == NULL)
! 1347: return;
! 1348:
! 1349: TAILQ_FOREACH(ifa0, &sc0->sc_if.if_addrlist, ifa_list)
! 1350: if (ifa0->ifa_addr->sa_family == AF_INET)
! 1351: break;
! 1352: #ifdef INET6
! 1353: if (ifa0 == NULL)
! 1354: TAILQ_FOREACH(ifa0, &sc0->sc_if.if_addrlist, ifa_list)
! 1355: if (ifa0->ifa_addr->sa_family == AF_INET6 &&
! 1356: !IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa0)))
! 1357: break;
! 1358: #endif
! 1359: if (ifa0 == NULL)
! 1360: return;
! 1361: /*
! 1362: * Calculate the load sharing mask w/ all carp interfaces
! 1363: * that share the first address of the LINK0 interface.
! 1364: * Sort by virtual host ID.
! 1365: */
! 1366: sc0->sc_lsmask = 0;
! 1367: cur = 0;
! 1368: curvh = NULL;
! 1369: count = 0;
! 1370: do {
! 1371: found = 0;
! 1372: last = cur;
! 1373: cur = 255;
! 1374: TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
! 1375: if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
! 1376: (IFF_UP|IFF_RUNNING))
! 1377: continue;
! 1378: TAILQ_FOREACH(ifa, &vh->sc_if.if_addrlist, ifa_list) {
! 1379: if (ifa->ifa_addr->sa_family == AF_INET &&
! 1380: ifa0->ifa_addr->sa_family == AF_INET &&
! 1381: ifatoia(ifa0)->ia_addr.sin_addr.s_addr ==
! 1382: ifatoia(ifa)->ia_addr.sin_addr.s_addr)
! 1383: break;
! 1384: #ifdef INET6
! 1385: if (ifa->ifa_addr->sa_family == AF_INET6 &&
! 1386: ifa0->ifa_addr->sa_family == AF_INET6 &&
! 1387: IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa0), IFA_IN6(ifa)))
! 1388: break;
! 1389: #endif
! 1390: }
! 1391: if (ifa && vh->sc_vhid > last && vh->sc_vhid < cur) {
! 1392: cur = vh->sc_vhid;
! 1393: curvh = vh;
! 1394: found++;
! 1395: }
! 1396: }
! 1397: if (found) {
! 1398: if (curvh->sc_state == MASTER &&
! 1399: count < sizeof(sc0->sc_lsmask) * 8)
! 1400: sc0->sc_lsmask |= 1 << count;
! 1401: count++;
! 1402: }
! 1403: } while (found);
! 1404:
! 1405: sc0->sc_lscount = count;
! 1406: if (count == 0)
! 1407: return;
! 1408:
! 1409: CARP_LOG(sc, ("carp_update_lsmask: %x", sc0->sc_lsmask))
! 1410: }
! 1411:
! 1412: int
! 1413: carp_iamatch(struct in_ifaddr *ia, u_char *src,
! 1414: u_int32_t *count, u_int32_t index)
! 1415: {
! 1416: struct carp_softc *sc = ia->ia_ifp->if_softc;
! 1417:
! 1418: /*
! 1419: * If the asked address is found on a LINK0 interface
! 1420: * don't answer the arp reply unless we are MASTER on it.
! 1421: */
! 1422: if (!(sc->sc_if.if_flags & IFF_LINK0) && sc->sc_carpdev &&
! 1423: carp_addrcount((struct carp_if *)sc->sc_carpdev->if_carp,
! 1424: (struct ifaddr *)ia, CARP_COUNT_LINK0))
! 1425: return (0);
! 1426:
! 1427: if (carp_opts[CARPCTL_ARPBALANCE]) {
! 1428: /*
! 1429: * We use the source ip to decide which virtual host should
! 1430: * handle the request. If we're master of that virtual host,
! 1431: * then we respond, otherwise, just drop the arp packet on
! 1432: * the floor.
! 1433: */
! 1434:
! 1435: /* Count the eligible carp interfaces with this address */
! 1436: if (*count == 0)
! 1437: *count = carp_addrcount(
! 1438: (struct carp_if *)ia->ia_ifp->if_carpdev->if_carp,
! 1439: (struct ifaddr *)ia, CARP_COUNT_RUNNING);
! 1440:
! 1441: /* This should never happen, but... */
! 1442: if (*count == 0)
! 1443: return (0);
! 1444:
! 1445: if (carp_hash(sc, src) % *count == index - 1 &&
! 1446: sc->sc_state == MASTER) {
! 1447: return (1);
! 1448: }
! 1449: } else {
! 1450: if (sc->sc_state == MASTER)
! 1451: return (1);
! 1452: }
! 1453:
! 1454: return (0);
! 1455: }
! 1456:
! 1457: #ifdef INET6
! 1458: int
! 1459: carp_iamatch6(struct ifnet *ifp, struct ifaddr *ifa)
! 1460: {
! 1461: struct carp_softc *sc = ifp->if_softc;
! 1462:
! 1463: /*
! 1464: * If the asked address is found on a LINK0 interface
! 1465: * don't answer the arp request unless we are MASTER on it.
! 1466: */
! 1467: if (!(sc->sc_if.if_flags & IFF_LINK0) && sc->sc_carpdev &&
! 1468: carp_addrcount((struct carp_if *)sc->sc_carpdev->if_carp,
! 1469: ifa, CARP_COUNT_LINK0))
! 1470: return (0);
! 1471:
! 1472: if (sc->sc_state == MASTER)
! 1473: return (1);
! 1474:
! 1475: return (0);
! 1476: }
! 1477: #endif /* INET6 */
! 1478:
! 1479: struct ifnet *
! 1480: carp_ourether(void *v, struct ether_header *eh, u_char iftype, int src)
! 1481: {
! 1482: struct carp_if *cif = (struct carp_if *)v;
! 1483: struct carp_softc *vh;
! 1484: u_int8_t *ena;
! 1485:
! 1486: if (src)
! 1487: ena = (u_int8_t *)&eh->ether_shost;
! 1488: else
! 1489: ena = (u_int8_t *)&eh->ether_dhost;
! 1490:
! 1491: TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
! 1492: if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
! 1493: (IFF_UP|IFF_RUNNING))
! 1494: continue;
! 1495: if ((vh->sc_state == MASTER || vh->sc_if.if_flags & IFF_LINK0)
! 1496: && !bcmp(ena, vh->sc_ac.ac_enaddr, ETHER_ADDR_LEN))
! 1497: return (&vh->sc_if);
! 1498: }
! 1499: return (NULL);
! 1500: }
! 1501:
! 1502: int
! 1503: carp_input(struct mbuf *m, u_int8_t *shost, u_int8_t *dhost, u_int16_t etype)
! 1504: {
! 1505: struct ether_header eh;
! 1506: struct carp_if *cif = (struct carp_if *)m->m_pkthdr.rcvif->if_carp;
! 1507: struct ifnet *ifp;
! 1508:
! 1509: bcopy(shost, &eh.ether_shost, sizeof(eh.ether_shost));
! 1510: bcopy(dhost, &eh.ether_dhost, sizeof(eh.ether_dhost));
! 1511: eh.ether_type = etype;
! 1512:
! 1513: if ((ifp = carp_ourether(cif, &eh, m->m_pkthdr.rcvif->if_type, 0)))
! 1514: ;
! 1515: else if (m->m_flags & (M_BCAST|M_MCAST)) {
! 1516: struct carp_softc *vh;
! 1517: struct mbuf *m0;
! 1518:
! 1519: /*
! 1520: * XXX Should really check the list of multicast addresses
! 1521: * for each CARP interface _before_ copying.
! 1522: */
! 1523: TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
! 1524: m0 = m_copym2(m, 0, M_COPYALL, M_DONTWAIT);
! 1525: if (m0 == NULL)
! 1526: continue;
! 1527: m0->m_pkthdr.rcvif = &vh->sc_if;
! 1528: ether_input(&vh->sc_if, &eh, m0);
! 1529: }
! 1530: return (1);
! 1531: }
! 1532:
! 1533: if (ifp == NULL)
! 1534: return (1);
! 1535:
! 1536: m->m_pkthdr.rcvif = ifp;
! 1537:
! 1538: #if NBPFILTER > 0
! 1539: if (ifp->if_bpf)
! 1540: bpf_mtap_hdr(ifp->if_bpf, (char *)&eh, ETHER_HDR_LEN, m,
! 1541: BPF_DIRECTION_IN);
! 1542: #endif
! 1543: ifp->if_ipackets++;
! 1544: ether_input(ifp, &eh, m);
! 1545:
! 1546: return (0);
! 1547: }
! 1548:
! 1549: int
! 1550: carp_lsdrop(struct mbuf *m, sa_family_t af, u_int32_t *src, u_int32_t *dst)
! 1551: {
! 1552: struct carp_softc *sc = m->m_pkthdr.rcvif->if_softc;
! 1553: int match;
! 1554: u_int32_t fold;
! 1555:
! 1556: /*
! 1557: * Never drop carp advertisements.
! 1558: * XXX Bad idea to pass all broadcast / multicast traffic?
! 1559: */
! 1560: if (m->m_flags & (M_BCAST|M_MCAST))
! 1561: return (0);
! 1562:
! 1563: fold = src[0] ^ dst[0];
! 1564: #ifdef INET6
! 1565: if (af == AF_INET6) {
! 1566: int i;
! 1567: for (i = 1; i < 4; i++)
! 1568: fold ^= src[i] ^ dst[i];
! 1569: }
! 1570: #endif
! 1571: if (sc->sc_lscount == 0) /* just to be safe */
! 1572: return (1);
! 1573: match = (1 << (ntohl(fold) % sc->sc_lscount)) & sc->sc_lsmask;
! 1574:
! 1575: return (!match);
! 1576: }
! 1577:
! 1578: void
! 1579: carp_master_down(void *v)
! 1580: {
! 1581: struct carp_softc *sc = v;
! 1582:
! 1583: switch (sc->sc_state) {
! 1584: case INIT:
! 1585: printf("%s: master_down event in INIT state\n",
! 1586: sc->sc_if.if_xname);
! 1587: break;
! 1588: case MASTER:
! 1589: break;
! 1590: case BACKUP:
! 1591: carp_set_state(sc, MASTER);
! 1592: carp_send_ad(sc);
! 1593: carp_send_arp(sc);
! 1594: /* Schedule a delayed ARP request to deal w/ some L3 switches */
! 1595: sc->sc_delayed_arp = 2;
! 1596: #ifdef INET6
! 1597: carp_send_na(sc);
! 1598: #endif /* INET6 */
! 1599: carp_setrun(sc, 0);
! 1600: carp_setroute(sc, RTM_ADD);
! 1601: break;
! 1602: }
! 1603: }
! 1604:
! 1605: /*
! 1606: * When in backup state, af indicates whether to reset the master down timer
! 1607: * for v4 or v6. If it's set to zero, reset the ones which are already pending.
! 1608: */
! 1609: void
! 1610: carp_setrun(struct carp_softc *sc, sa_family_t af)
! 1611: {
! 1612: struct timeval tv;
! 1613:
! 1614: if (sc->sc_carpdev == NULL) {
! 1615: sc->sc_if.if_flags &= ~IFF_RUNNING;
! 1616: carp_set_state(sc, INIT);
! 1617: return;
! 1618: }
! 1619:
! 1620: if (sc->sc_if.if_flags & IFF_UP && sc->sc_vhid > 0 &&
! 1621: (sc->sc_naddrs || sc->sc_naddrs6) && !sc->sc_suppress) {
! 1622: sc->sc_if.if_flags |= IFF_RUNNING;
! 1623: } else {
! 1624: sc->sc_if.if_flags &= ~IFF_RUNNING;
! 1625: carp_setroute(sc, RTM_DELETE);
! 1626: return;
! 1627: }
! 1628:
! 1629: switch (sc->sc_state) {
! 1630: case INIT:
! 1631: carp_set_state(sc, BACKUP);
! 1632: carp_setroute(sc, RTM_DELETE);
! 1633: carp_setrun(sc, 0);
! 1634: break;
! 1635: case BACKUP:
! 1636: timeout_del(&sc->sc_ad_tmo);
! 1637: tv.tv_sec = 3 * sc->sc_advbase;
! 1638: tv.tv_usec = sc->sc_advskew * 1000000 / 256;
! 1639: sc->sc_delayed_arp = -1;
! 1640: switch (af) {
! 1641: #ifdef INET
! 1642: case AF_INET:
! 1643: timeout_add(&sc->sc_md_tmo, tvtohz(&tv));
! 1644: break;
! 1645: #endif /* INET */
! 1646: #ifdef INET6
! 1647: case AF_INET6:
! 1648: timeout_add(&sc->sc_md6_tmo, tvtohz(&tv));
! 1649: break;
! 1650: #endif /* INET6 */
! 1651: default:
! 1652: if (sc->sc_naddrs)
! 1653: timeout_add(&sc->sc_md_tmo, tvtohz(&tv));
! 1654: if (sc->sc_naddrs6)
! 1655: timeout_add(&sc->sc_md6_tmo, tvtohz(&tv));
! 1656: break;
! 1657: }
! 1658: break;
! 1659: case MASTER:
! 1660: tv.tv_sec = sc->sc_advbase;
! 1661: tv.tv_usec = sc->sc_advskew * 1000000 / 256;
! 1662: timeout_add(&sc->sc_ad_tmo, tvtohz(&tv));
! 1663: break;
! 1664: }
! 1665: }
! 1666:
! 1667: void
! 1668: carp_multicast_cleanup(struct carp_softc *sc)
! 1669: {
! 1670: struct ip_moptions *imo = &sc->sc_imo;
! 1671: #ifdef INET6
! 1672: struct ip6_moptions *im6o = &sc->sc_im6o;
! 1673: #endif
! 1674: u_int16_t n = imo->imo_num_memberships;
! 1675:
! 1676: /* Clean up our own multicast memberships */
! 1677: while (n-- > 0) {
! 1678: if (imo->imo_membership[n] != NULL) {
! 1679: in_delmulti(imo->imo_membership[n]);
! 1680: imo->imo_membership[n] = NULL;
! 1681: }
! 1682: }
! 1683: imo->imo_num_memberships = 0;
! 1684: imo->imo_multicast_ifp = NULL;
! 1685:
! 1686: #ifdef INET6
! 1687: while (!LIST_EMPTY(&im6o->im6o_memberships)) {
! 1688: struct in6_multi_mship *imm =
! 1689: LIST_FIRST(&im6o->im6o_memberships);
! 1690:
! 1691: LIST_REMOVE(imm, i6mm_chain);
! 1692: in6_leavegroup(imm);
! 1693: }
! 1694: im6o->im6o_multicast_ifp = NULL;
! 1695: #endif
! 1696:
! 1697: /* And any other multicast memberships */
! 1698: carp_ether_purgemulti(sc);
! 1699: }
! 1700:
! 1701: int
! 1702: carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp)
! 1703: {
! 1704: struct carp_if *cif, *ncif = NULL;
! 1705: struct carp_softc *vr, *after = NULL;
! 1706: int myself = 0, error = 0;
! 1707: int s;
! 1708:
! 1709: if (ifp == sc->sc_carpdev)
! 1710: return (0);
! 1711:
! 1712: if (ifp != NULL) {
! 1713: if ((ifp->if_flags & IFF_MULTICAST) == 0)
! 1714: return (EADDRNOTAVAIL);
! 1715:
! 1716: if (ifp->if_type == IFT_CARP)
! 1717: return (EINVAL);
! 1718:
! 1719: if (ifp->if_carp == NULL) {
! 1720: MALLOC(ncif, struct carp_if *, sizeof(*cif),
! 1721: M_IFADDR, M_NOWAIT);
! 1722: if (ncif == NULL)
! 1723: return (ENOBUFS);
! 1724: if ((error = ifpromisc(ifp, 1))) {
! 1725: FREE(ncif, M_IFADDR);
! 1726: return (error);
! 1727: }
! 1728:
! 1729: ncif->vhif_ifp = ifp;
! 1730: TAILQ_INIT(&ncif->vhif_vrs);
! 1731: } else {
! 1732: cif = (struct carp_if *)ifp->if_carp;
! 1733: TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
! 1734: if (vr != sc && vr->sc_vhid == sc->sc_vhid)
! 1735: return (EINVAL);
! 1736: }
! 1737:
! 1738: /* detach from old interface */
! 1739: if (sc->sc_carpdev != NULL)
! 1740: carpdetach(sc);
! 1741:
! 1742: /* join multicast groups */
! 1743: if (sc->sc_naddrs < 0 &&
! 1744: (error = carp_join_multicast(sc)) != 0) {
! 1745: if (ncif != NULL)
! 1746: FREE(ncif, M_IFADDR);
! 1747: return (error);
! 1748: }
! 1749:
! 1750: #ifdef INET6
! 1751: if (sc->sc_naddrs6 < 0 &&
! 1752: (error = carp_join_multicast6(sc)) != 0) {
! 1753: if (ncif != NULL)
! 1754: FREE(ncif, M_IFADDR);
! 1755: carp_multicast_cleanup(sc);
! 1756: return (error);
! 1757: }
! 1758: #endif
! 1759:
! 1760: /* attach carp interface to physical interface */
! 1761: if (ncif != NULL)
! 1762: ifp->if_carp = (caddr_t)ncif;
! 1763: sc->sc_carpdev = ifp;
! 1764: cif = (struct carp_if *)ifp->if_carp;
! 1765: TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) {
! 1766: if (vr == sc)
! 1767: myself = 1;
! 1768: if (vr->sc_vhid < sc->sc_vhid)
! 1769: after = vr;
! 1770: }
! 1771:
! 1772: if (!myself) {
! 1773: /* We're trying to keep things in order */
! 1774: if (after == NULL) {
! 1775: TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list);
! 1776: } else {
! 1777: TAILQ_INSERT_AFTER(&cif->vhif_vrs, after,
! 1778: sc, sc_list);
! 1779: }
! 1780: cif->vhif_nvrs++;
! 1781: }
! 1782: if (sc->sc_naddrs || sc->sc_naddrs6)
! 1783: sc->sc_if.if_flags |= IFF_UP;
! 1784: carp_set_enaddr(sc);
! 1785: s = splnet();
! 1786: sc->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1,
! 1787: carp_carpdev_state, ifp);
! 1788: carp_carpdev_state(ifp);
! 1789: splx(s);
! 1790: } else {
! 1791: carpdetach(sc);
! 1792: sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
! 1793: }
! 1794: return (0);
! 1795: }
! 1796:
! 1797: void
! 1798: carp_set_enaddr(struct carp_softc *sc)
! 1799: {
! 1800: if (sc->sc_vhid != -1 && sc->sc_carpdev) {
! 1801: /* XXX detach ipv6 link-local address? */
! 1802: if (sc->sc_if.if_flags & IFF_LINK2)
! 1803: sc->sc_carplladdr[0] = 1;
! 1804: else
! 1805: sc->sc_carplladdr[0] = 0;
! 1806: sc->sc_carplladdr[1] = 0;
! 1807: sc->sc_carplladdr[2] = 0x5e;
! 1808: sc->sc_carplladdr[3] = 0;
! 1809: sc->sc_carplladdr[4] = 1;
! 1810: sc->sc_carplladdr[5] = sc->sc_vhid;
! 1811: } else
! 1812: bzero(sc->sc_carplladdr, ETHER_ADDR_LEN);
! 1813:
! 1814: /*
! 1815: * Use the carp lladdr if the running one isn't manually set.
! 1816: * Only compare static parts of the lladdr.
! 1817: */
! 1818: if ((bcmp(sc->sc_ac.ac_enaddr + 1, sc->sc_carplladdr + 1,
! 1819: ETHER_ADDR_LEN - 2) == 0) ||
! 1820: (!sc->sc_ac.ac_enaddr[0] && !sc->sc_ac.ac_enaddr[1] &&
! 1821: !sc->sc_ac.ac_enaddr[2] && !sc->sc_ac.ac_enaddr[3] &&
! 1822: !sc->sc_ac.ac_enaddr[4] && !sc->sc_ac.ac_enaddr[5]))
! 1823: bcopy(sc->sc_carplladdr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
! 1824:
! 1825: /* Make sure the enaddr has changed before further twiddling. */
! 1826: if (bcmp(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN) != 0) {
! 1827: bcopy(sc->sc_ac.ac_enaddr, LLADDR(sc->sc_if.if_sadl),
! 1828: ETHER_ADDR_LEN);
! 1829: bcopy(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN);
! 1830: #ifdef INET6
! 1831: /*
! 1832: * (re)attach a link-local address which matches
! 1833: * our new MAC address.
! 1834: */
! 1835: in6_ifattach_linklocal(&sc->sc_if, NULL);
! 1836: #endif
! 1837: carp_set_state(sc, INIT);
! 1838: carp_setrun(sc, 0);
! 1839: }
! 1840: }
! 1841:
! 1842: void
! 1843: carp_addr_updated(void *v)
! 1844: {
! 1845: struct carp_softc *sc = (struct carp_softc *) v;
! 1846: struct ifaddr *ifa;
! 1847: int new_naddrs = 0, new_naddrs6 = 0;
! 1848:
! 1849: TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
! 1850: if (ifa->ifa_addr->sa_family == AF_INET)
! 1851: new_naddrs++;
! 1852: else if (ifa->ifa_addr->sa_family == AF_INET6 &&
! 1853: !IN6_IS_ADDR_LINKLOCAL(&ifatoia6(ifa)->ia_addr.sin6_addr))
! 1854: new_naddrs6++;
! 1855: }
! 1856:
! 1857: /* Handle a callback after SIOCDIFADDR */
! 1858: if (new_naddrs < sc->sc_naddrs || new_naddrs6 < sc->sc_naddrs6) {
! 1859: struct in_addr mc_addr;
! 1860: struct in_multi *inm;
! 1861:
! 1862: sc->sc_naddrs = new_naddrs;
! 1863: sc->sc_naddrs6 = new_naddrs6;
! 1864:
! 1865: /* Re-establish multicast membership removed by in_control */
! 1866: mc_addr.s_addr = INADDR_CARP_GROUP;
! 1867: IN_LOOKUP_MULTI(mc_addr, &sc->sc_if, inm);
! 1868: if (inm == NULL) {
! 1869: bzero(&sc->sc_imo, sizeof(sc->sc_imo));
! 1870:
! 1871: if (sc->sc_carpdev != NULL && sc->sc_naddrs > 0)
! 1872: carp_join_multicast(sc);
! 1873: }
! 1874:
! 1875: if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) {
! 1876: sc->sc_if.if_flags &= ~IFF_UP;
! 1877: carp_set_state(sc, INIT);
! 1878: } else
! 1879: carp_hmac_prepare(sc);
! 1880: }
! 1881:
! 1882: carp_setrun(sc, 0);
! 1883: }
! 1884:
! 1885: int
! 1886: carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin)
! 1887: {
! 1888: struct ifnet *ifp = sc->sc_carpdev;
! 1889: struct in_ifaddr *ia, *ia_if;
! 1890: int error = 0;
! 1891:
! 1892: if (sin->sin_addr.s_addr == 0) {
! 1893: if (!(sc->sc_if.if_flags & IFF_UP))
! 1894: carp_set_state(sc, INIT);
! 1895: if (sc->sc_naddrs)
! 1896: sc->sc_if.if_flags |= IFF_UP;
! 1897: carp_setrun(sc, 0);
! 1898: return (0);
! 1899: }
! 1900:
! 1901: /* we have to do this by hand to ensure we don't match on ourselves */
! 1902: ia_if = NULL;
! 1903: for (ia = TAILQ_FIRST(&in_ifaddr); ia;
! 1904: ia = TAILQ_NEXT(ia, ia_list)) {
! 1905:
! 1906: /* and, yeah, we need a multicast-capable iface too */
! 1907: if (ia->ia_ifp != &sc->sc_if &&
! 1908: ia->ia_ifp->if_type != IFT_CARP &&
! 1909: (ia->ia_ifp->if_flags & IFF_MULTICAST) &&
! 1910: (sin->sin_addr.s_addr & ia->ia_subnetmask) ==
! 1911: ia->ia_subnet) {
! 1912: if (!ia_if)
! 1913: ia_if = ia;
! 1914: }
! 1915: }
! 1916:
! 1917: if (ia_if) {
! 1918: ia = ia_if;
! 1919: if (ifp) {
! 1920: if (ifp != ia->ia_ifp)
! 1921: return (EADDRNOTAVAIL);
! 1922: } else {
! 1923: ifp = ia->ia_ifp;
! 1924: }
! 1925: }
! 1926:
! 1927: if ((error = carp_set_ifp(sc, ifp)))
! 1928: return (error);
! 1929:
! 1930: if (sc->sc_carpdev == NULL)
! 1931: return (EADDRNOTAVAIL);
! 1932:
! 1933: if (sc->sc_naddrs == 0 && (error = carp_join_multicast(sc)) != 0)
! 1934: return (error);
! 1935:
! 1936: sc->sc_naddrs++;
! 1937: if (sc->sc_carpdev != NULL)
! 1938: sc->sc_if.if_flags |= IFF_UP;
! 1939:
! 1940: carp_set_state(sc, INIT);
! 1941:
! 1942: /*
! 1943: * Hook if_addrhooks so that we get a callback after in_ifinit has run,
! 1944: * to correct any inappropriate routes that it inserted.
! 1945: */
! 1946: if (sc->ah_cookie == NULL)
! 1947: sc->ah_cookie = hook_establish(sc->sc_if.if_addrhooks, 0,
! 1948: carp_addr_updated, sc);
! 1949:
! 1950: return (0);
! 1951: }
! 1952:
! 1953: int
! 1954: carp_join_multicast(struct carp_softc *sc)
! 1955: {
! 1956: struct ip_moptions *imo = &sc->sc_imo, tmpimo;
! 1957: struct in_addr addr;
! 1958:
! 1959: bzero(&tmpimo, sizeof(tmpimo));
! 1960: addr.s_addr = INADDR_CARP_GROUP;
! 1961: if ((tmpimo.imo_membership[0] =
! 1962: in_addmulti(&addr, &sc->sc_if)) == NULL) {
! 1963: return (ENOBUFS);
! 1964: }
! 1965:
! 1966: imo->imo_membership[0] = tmpimo.imo_membership[0];
! 1967: imo->imo_num_memberships = 1;
! 1968: imo->imo_multicast_ifp = &sc->sc_if;
! 1969: imo->imo_multicast_ttl = CARP_DFLTTL;
! 1970: imo->imo_multicast_loop = 0;
! 1971: return (0);
! 1972: }
! 1973:
! 1974:
! 1975: #ifdef INET6
! 1976: int
! 1977: carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
! 1978: {
! 1979: struct ifnet *ifp = sc->sc_carpdev;
! 1980: struct in6_ifaddr *ia, *ia_if;
! 1981: int error = 0;
! 1982:
! 1983: if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
! 1984: if (!(sc->sc_if.if_flags & IFF_UP))
! 1985: carp_set_state(sc, INIT);
! 1986: if (sc->sc_naddrs6)
! 1987: sc->sc_if.if_flags |= IFF_UP;
! 1988: carp_setrun(sc, 0);
! 1989: return (0);
! 1990: }
! 1991:
! 1992: /* we have to do this by hand to ensure we don't match on ourselves */
! 1993: ia_if = NULL;
! 1994: for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
! 1995: int i;
! 1996:
! 1997: for (i = 0; i < 4; i++) {
! 1998: if ((sin6->sin6_addr.s6_addr32[i] &
! 1999: ia->ia_prefixmask.sin6_addr.s6_addr32[i]) !=
! 2000: (ia->ia_addr.sin6_addr.s6_addr32[i] &
! 2001: ia->ia_prefixmask.sin6_addr.s6_addr32[i]))
! 2002: break;
! 2003: }
! 2004: /* and, yeah, we need a multicast-capable iface too */
! 2005: if (ia->ia_ifp != &sc->sc_if &&
! 2006: ia->ia_ifp->if_type != IFT_CARP &&
! 2007: (ia->ia_ifp->if_flags & IFF_MULTICAST) &&
! 2008: (i == 4)) {
! 2009: if (!ia_if)
! 2010: ia_if = ia;
! 2011: }
! 2012: }
! 2013:
! 2014: if (ia_if) {
! 2015: ia = ia_if;
! 2016: if (sc->sc_carpdev) {
! 2017: if (sc->sc_carpdev != ia->ia_ifp)
! 2018: return (EADDRNOTAVAIL);
! 2019: } else {
! 2020: ifp = ia->ia_ifp;
! 2021: }
! 2022: }
! 2023:
! 2024: if ((error = carp_set_ifp(sc, ifp)))
! 2025: return (error);
! 2026:
! 2027: if (sc->sc_carpdev == NULL)
! 2028: return (EADDRNOTAVAIL);
! 2029:
! 2030: if (sc->sc_naddrs6 == 0 && (error = carp_join_multicast6(sc)) != 0)
! 2031: return (error);
! 2032:
! 2033: if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
! 2034: sc->sc_naddrs6++;
! 2035: if (sc->sc_carpdev != NULL && sc->sc_naddrs6)
! 2036: sc->sc_if.if_flags |= IFF_UP;
! 2037: carp_set_state(sc, INIT);
! 2038: carp_setrun(sc, 0);
! 2039:
! 2040: return (0);
! 2041: }
! 2042:
! 2043: int
! 2044: carp_join_multicast6(struct carp_softc *sc)
! 2045: {
! 2046: struct in6_multi_mship *imm, *imm2;
! 2047: struct ip6_moptions *im6o = &sc->sc_im6o;
! 2048: struct sockaddr_in6 addr6;
! 2049: int error;
! 2050:
! 2051: /* Join IPv6 CARP multicast group */
! 2052: bzero(&addr6, sizeof(addr6));
! 2053: addr6.sin6_family = AF_INET6;
! 2054: addr6.sin6_len = sizeof(addr6);
! 2055: addr6.sin6_addr.s6_addr16[0] = htons(0xff02);
! 2056: addr6.sin6_addr.s6_addr16[1] = htons(sc->sc_if.if_index);
! 2057: addr6.sin6_addr.s6_addr8[15] = 0x12;
! 2058: if ((imm = in6_joingroup(&sc->sc_if,
! 2059: &addr6.sin6_addr, &error)) == NULL) {
! 2060: return (error);
! 2061: }
! 2062: /* join solicited multicast address */
! 2063: bzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr));
! 2064: addr6.sin6_addr.s6_addr16[0] = htons(0xff02);
! 2065: addr6.sin6_addr.s6_addr16[1] = htons(sc->sc_if.if_index);
! 2066: addr6.sin6_addr.s6_addr32[1] = 0;
! 2067: addr6.sin6_addr.s6_addr32[2] = htonl(1);
! 2068: addr6.sin6_addr.s6_addr32[3] = 0;
! 2069: addr6.sin6_addr.s6_addr8[12] = 0xff;
! 2070: if ((imm2 = in6_joingroup(&sc->sc_if,
! 2071: &addr6.sin6_addr, &error)) == NULL) {
! 2072: in6_leavegroup(imm);
! 2073: return (error);
! 2074: }
! 2075:
! 2076: /* apply v6 multicast membership */
! 2077: im6o->im6o_multicast_ifp = &sc->sc_if;
! 2078: if (imm)
! 2079: LIST_INSERT_HEAD(&im6o->im6o_memberships, imm,
! 2080: i6mm_chain);
! 2081: if (imm2)
! 2082: LIST_INSERT_HEAD(&im6o->im6o_memberships, imm2,
! 2083: i6mm_chain);
! 2084:
! 2085: return (0);
! 2086: }
! 2087:
! 2088: #endif /* INET6 */
! 2089:
! 2090: int
! 2091: carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
! 2092: {
! 2093: struct proc *p = curproc; /* XXX */
! 2094: struct carp_softc *sc = ifp->if_softc, *vr;
! 2095: struct carpreq carpr;
! 2096: struct ifaddr *ifa = (struct ifaddr *)addr;
! 2097: struct ifreq *ifr = (struct ifreq *)addr;
! 2098: struct ifnet *cdev = NULL;
! 2099: int error = 0;
! 2100:
! 2101: switch (cmd) {
! 2102: case SIOCSIFADDR:
! 2103: switch (ifa->ifa_addr->sa_family) {
! 2104: #ifdef INET
! 2105: case AF_INET:
! 2106: sc->sc_if.if_flags |= IFF_UP;
! 2107: bcopy(ifa->ifa_addr, ifa->ifa_dstaddr,
! 2108: sizeof(struct sockaddr));
! 2109: error = carp_set_addr(sc, satosin(ifa->ifa_addr));
! 2110: break;
! 2111: #endif /* INET */
! 2112: #ifdef INET6
! 2113: case AF_INET6:
! 2114: sc->sc_if.if_flags |= IFF_UP;
! 2115: error = carp_set_addr6(sc, satosin6(ifa->ifa_addr));
! 2116: break;
! 2117: #endif /* INET6 */
! 2118: default:
! 2119: error = EAFNOSUPPORT;
! 2120: break;
! 2121: }
! 2122: break;
! 2123:
! 2124: case SIOCSIFFLAGS:
! 2125: if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) {
! 2126: timeout_del(&sc->sc_ad_tmo);
! 2127: timeout_del(&sc->sc_md_tmo);
! 2128: timeout_del(&sc->sc_md6_tmo);
! 2129: if (sc->sc_state == MASTER) {
! 2130: /* we need the interface up to bow out */
! 2131: sc->sc_if.if_flags |= IFF_UP;
! 2132: sc->sc_bow_out = 1;
! 2133: carp_send_ad(sc);
! 2134: }
! 2135: sc->sc_if.if_flags &= ~IFF_UP;
! 2136: carp_set_state(sc, INIT);
! 2137: carp_setrun(sc, 0);
! 2138: } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) {
! 2139: sc->sc_if.if_flags |= IFF_UP;
! 2140: carp_setrun(sc, 0);
! 2141: }
! 2142: carp_set_enaddr(sc); /* for changes on LINK2 */
! 2143: if (ifr->ifr_flags & IFF_LINK0)
! 2144: carp_update_lsmask(sc);
! 2145: break;
! 2146:
! 2147: case SIOCSVH:
! 2148: if ((error = suser(p, p->p_acflag)) != 0)
! 2149: break;
! 2150: if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr)))
! 2151: break;
! 2152: error = 1;
! 2153: if (carpr.carpr_carpdev[0] != '\0' &&
! 2154: (cdev = ifunit(carpr.carpr_carpdev)) == NULL)
! 2155: return (EINVAL);
! 2156: if ((error = carp_set_ifp(sc, cdev)))
! 2157: return (error);
! 2158: if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) {
! 2159: switch (carpr.carpr_state) {
! 2160: case BACKUP:
! 2161: timeout_del(&sc->sc_ad_tmo);
! 2162: carp_set_state(sc, BACKUP);
! 2163: carp_setrun(sc, 0);
! 2164: carp_setroute(sc, RTM_DELETE);
! 2165: break;
! 2166: case MASTER:
! 2167: carp_master_down(sc);
! 2168: break;
! 2169: default:
! 2170: break;
! 2171: }
! 2172: }
! 2173: if (carpr.carpr_vhid > 0 && carpr.carpr_vhid != sc->sc_vhid) {
! 2174: if (carpr.carpr_vhid > 255) {
! 2175: error = EINVAL;
! 2176: break;
! 2177: }
! 2178: if (sc->sc_carpdev) {
! 2179: struct carp_if *cif;
! 2180: cif = (struct carp_if *)sc->sc_carpdev->if_carp;
! 2181: TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
! 2182: if (vr != sc &&
! 2183: vr->sc_vhid == carpr.carpr_vhid)
! 2184: return (EINVAL);
! 2185: }
! 2186: if (carpr.carpr_vhid != sc->sc_vhid) {
! 2187: sc->sc_vhid = carpr.carpr_vhid;
! 2188: carp_set_enaddr(sc);
! 2189: carp_set_state(sc, INIT);
! 2190: }
! 2191: error--;
! 2192: }
! 2193: if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) {
! 2194: if (carpr.carpr_advskew >= 255) {
! 2195: error = EINVAL;
! 2196: break;
! 2197: }
! 2198: if (carpr.carpr_advbase > 255) {
! 2199: error = EINVAL;
! 2200: break;
! 2201: }
! 2202: sc->sc_advbase = carpr.carpr_advbase;
! 2203: sc->sc_advskew = carpr.carpr_advskew;
! 2204: error--;
! 2205: }
! 2206: bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key));
! 2207: if (error > 0)
! 2208: error = EINVAL;
! 2209: else {
! 2210: error = 0;
! 2211: carp_setrun(sc, 0);
! 2212: }
! 2213: break;
! 2214:
! 2215: case SIOCGVH:
! 2216: bzero(&carpr, sizeof(carpr));
! 2217: if (sc->sc_carpdev != NULL)
! 2218: strlcpy(carpr.carpr_carpdev, sc->sc_carpdev->if_xname,
! 2219: IFNAMSIZ);
! 2220: carpr.carpr_state = sc->sc_state;
! 2221: carpr.carpr_vhid = sc->sc_vhid;
! 2222: carpr.carpr_advbase = sc->sc_advbase;
! 2223: carpr.carpr_advskew = sc->sc_advskew;
! 2224: if (suser(p, p->p_acflag) == 0)
! 2225: bcopy(sc->sc_key, carpr.carpr_key,
! 2226: sizeof(carpr.carpr_key));
! 2227: error = copyout(&carpr, ifr->ifr_data, sizeof(carpr));
! 2228: break;
! 2229:
! 2230: case SIOCADDMULTI:
! 2231: error = carp_ether_addmulti(sc, ifr);
! 2232: break;
! 2233:
! 2234: case SIOCDELMULTI:
! 2235: error = carp_ether_delmulti(sc, ifr);
! 2236: break;
! 2237: case SIOCAIFGROUP:
! 2238: case SIOCDIFGROUP:
! 2239: if (sc->sc_suppress)
! 2240: carp_ifgroup_ioctl(ifp, cmd, addr);
! 2241: break;
! 2242: case SIOCSIFGATTR:
! 2243: carp_ifgattr_ioctl(ifp, cmd, addr);
! 2244: break;
! 2245: default:
! 2246: error = EINVAL;
! 2247: }
! 2248:
! 2249: if (bcmp(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN) != 0)
! 2250: carp_set_enaddr(sc);
! 2251: carp_hmac_prepare(sc);
! 2252: return (error);
! 2253: }
! 2254:
! 2255: void
! 2256: carp_ifgroup_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
! 2257: {
! 2258: struct ifgroupreq *ifgr = (struct ifgroupreq *)addr;
! 2259: struct ifg_list *ifgl;
! 2260:
! 2261: if (!strcmp(ifgr->ifgr_group, IFG_ALL))
! 2262: return;
! 2263: TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
! 2264: if (!strcmp(ifgl->ifgl_group->ifg_group, ifgr->ifgr_group)) {
! 2265: if (cmd == SIOCAIFGROUP)
! 2266: ifgl->ifgl_group->ifg_carp_demoted++;
! 2267: else if (cmd == SIOCDIFGROUP &&
! 2268: ifgl->ifgl_group->ifg_carp_demoted)
! 2269: ifgl->ifgl_group->ifg_carp_demoted--;
! 2270: }
! 2271: }
! 2272:
! 2273: void
! 2274: carp_ifgattr_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
! 2275: {
! 2276: struct ifgroupreq *ifgr = (struct ifgroupreq *)addr;
! 2277: struct carp_softc *sc = ifp->if_softc;
! 2278:
! 2279: if (ifgr->ifgr_attrib.ifg_carp_demoted > 0 && (sc->sc_if.if_flags &
! 2280: (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) &&
! 2281: sc->sc_state == MASTER)
! 2282: carp_send_ad(sc);
! 2283: }
! 2284:
! 2285: /*
! 2286: * Start output on carp interface. This function should never be called.
! 2287: */
! 2288: void
! 2289: carp_start(struct ifnet *ifp)
! 2290: {
! 2291: #ifdef DEBUG
! 2292: printf("%s: start called\n", ifp->if_xname);
! 2293: #endif
! 2294: }
! 2295:
! 2296: int
! 2297: carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
! 2298: struct rtentry *rt)
! 2299: {
! 2300: struct carp_softc *sc = ((struct carp_softc *)ifp->if_softc);
! 2301:
! 2302: if (sc->sc_carpdev != NULL && sc->sc_state == MASTER)
! 2303: return (sc->sc_carpdev->if_output(ifp, m, sa, rt));
! 2304: else {
! 2305: m_freem(m);
! 2306: return (ENETUNREACH);
! 2307: }
! 2308: }
! 2309:
! 2310: void
! 2311: carp_set_state(struct carp_softc *sc, int state)
! 2312: {
! 2313: if (sc->sc_state == state)
! 2314: return;
! 2315:
! 2316: sc->sc_state = state;
! 2317: carp_update_lsmask(sc);
! 2318:
! 2319: switch (state) {
! 2320: case BACKUP:
! 2321: sc->sc_if.if_link_state = LINK_STATE_DOWN;
! 2322: break;
! 2323: case MASTER:
! 2324: sc->sc_if.if_link_state = LINK_STATE_UP;
! 2325: break;
! 2326: default:
! 2327: sc->sc_if.if_link_state = LINK_STATE_UNKNOWN;
! 2328: break;
! 2329: }
! 2330: if_link_state_change(&sc->sc_if);
! 2331: }
! 2332:
! 2333: void
! 2334: carp_group_demote_adj(struct ifnet *ifp, int adj)
! 2335: {
! 2336: struct ifg_list *ifgl;
! 2337: int *dm;
! 2338: struct carp_softc *nil = NULL;
! 2339:
! 2340: TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
! 2341: if (!strcmp(ifgl->ifgl_group->ifg_group, IFG_ALL))
! 2342: continue;
! 2343: dm = &ifgl->ifgl_group->ifg_carp_demoted;
! 2344:
! 2345: if (*dm + adj >= 0)
! 2346: *dm += adj;
! 2347: else
! 2348: *dm = 0;
! 2349:
! 2350: if (adj > 0 && *dm == 1)
! 2351: carp_send_ad_all();
! 2352: CARP_LOG(nil, ("%s demoted group %s to %d", ifp->if_xname,
! 2353: ifgl->ifgl_group->ifg_group, *dm));
! 2354: }
! 2355: }
! 2356:
! 2357: int
! 2358: carp_group_demote_count(struct carp_softc *sc)
! 2359: {
! 2360: struct ifg_list *ifgl;
! 2361: int count = 0;
! 2362:
! 2363: TAILQ_FOREACH(ifgl, &sc->sc_if.if_groups, ifgl_next)
! 2364: count += ifgl->ifgl_group->ifg_carp_demoted;
! 2365:
! 2366: return (count);
! 2367: }
! 2368:
! 2369: void
! 2370: carp_carpdev_state(void *v)
! 2371: {
! 2372: struct carp_if *cif;
! 2373: struct carp_softc *sc;
! 2374: struct ifnet *ifp = v;
! 2375:
! 2376: if (ifp->if_type == IFT_CARP)
! 2377: return;
! 2378:
! 2379: cif = (struct carp_if *)ifp->if_carp;
! 2380:
! 2381: TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) {
! 2382: int suppressed = sc->sc_suppress;
! 2383:
! 2384: if (sc->sc_carpdev->if_link_state == LINK_STATE_DOWN ||
! 2385: !(sc->sc_carpdev->if_flags & IFF_UP)) {
! 2386: sc->sc_if.if_flags &= ~IFF_RUNNING;
! 2387: timeout_del(&sc->sc_ad_tmo);
! 2388: timeout_del(&sc->sc_md_tmo);
! 2389: timeout_del(&sc->sc_md6_tmo);
! 2390: carp_set_state(sc, INIT);
! 2391: sc->sc_suppress = 1;
! 2392: carp_setrun(sc, 0);
! 2393: if (!suppressed)
! 2394: carp_group_demote_adj(&sc->sc_if, 1);
! 2395: } else {
! 2396: carp_set_state(sc, INIT);
! 2397: sc->sc_suppress = 0;
! 2398: carp_setrun(sc, 0);
! 2399: if (suppressed)
! 2400: carp_group_demote_adj(&sc->sc_if, -1);
! 2401: }
! 2402: }
! 2403: }
! 2404:
! 2405: int
! 2406: carp_ether_addmulti(struct carp_softc *sc, struct ifreq *ifr)
! 2407: {
! 2408: struct ifnet *ifp;
! 2409: struct carp_mc_entry *mc;
! 2410: u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
! 2411: int error;
! 2412:
! 2413: ifp = sc->sc_carpdev;
! 2414: if (ifp == NULL)
! 2415: return (EINVAL);
! 2416:
! 2417: error = ether_addmulti(ifr, (struct arpcom *)&sc->sc_ac);
! 2418: if (error != ENETRESET)
! 2419: return (error);
! 2420:
! 2421: /*
! 2422: * This is new multicast address. We have to tell parent
! 2423: * about it. Also, remember this multicast address so that
! 2424: * we can delete them on unconfigure.
! 2425: */
! 2426: MALLOC(mc, struct carp_mc_entry *, sizeof(struct carp_mc_entry),
! 2427: M_DEVBUF, M_NOWAIT);
! 2428: if (mc == NULL) {
! 2429: error = ENOMEM;
! 2430: goto alloc_failed;
! 2431: }
! 2432:
! 2433: /*
! 2434: * As ether_addmulti() returns ENETRESET, following two
! 2435: * statement shouldn't fail.
! 2436: */
! 2437: (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
! 2438: ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, mc->mc_enm);
! 2439: memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
! 2440: LIST_INSERT_HEAD(&sc->carp_mc_listhead, mc, mc_entries);
! 2441:
! 2442: error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr);
! 2443: if (error != 0)
! 2444: goto ioctl_failed;
! 2445:
! 2446: return (error);
! 2447:
! 2448: ioctl_failed:
! 2449: LIST_REMOVE(mc, mc_entries);
! 2450: FREE(mc, M_DEVBUF);
! 2451: alloc_failed:
! 2452: (void)ether_delmulti(ifr, (struct arpcom *)&sc->sc_ac);
! 2453:
! 2454: return (error);
! 2455: }
! 2456:
! 2457: int
! 2458: carp_ether_delmulti(struct carp_softc *sc, struct ifreq *ifr)
! 2459: {
! 2460: struct ifnet *ifp;
! 2461: struct ether_multi *enm;
! 2462: struct carp_mc_entry *mc;
! 2463: u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
! 2464: int error;
! 2465:
! 2466: ifp = sc->sc_carpdev;
! 2467: if (ifp == NULL)
! 2468: return (EINVAL);
! 2469:
! 2470: /*
! 2471: * Find a key to lookup carp_mc_entry. We have to do this
! 2472: * before calling ether_delmulti for obvious reason.
! 2473: */
! 2474: if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
! 2475: return (error);
! 2476: ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, enm);
! 2477: if (enm == NULL)
! 2478: return (EINVAL);
! 2479:
! 2480: LIST_FOREACH(mc, &sc->carp_mc_listhead, mc_entries)
! 2481: if (mc->mc_enm == enm)
! 2482: break;
! 2483:
! 2484: /* We won't delete entries we didn't add */
! 2485: if (mc == NULL)
! 2486: return (EINVAL);
! 2487:
! 2488: error = ether_delmulti(ifr, (struct arpcom *)&sc->sc_ac);
! 2489: if (error != ENETRESET)
! 2490: return (error);
! 2491:
! 2492: /* We no longer use this multicast address. Tell parent so. */
! 2493: error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
! 2494: if (error == 0) {
! 2495: /* And forget about this address. */
! 2496: LIST_REMOVE(mc, mc_entries);
! 2497: FREE(mc, M_DEVBUF);
! 2498: } else
! 2499: (void)ether_addmulti(ifr, (struct arpcom *)&sc->sc_ac);
! 2500: return (error);
! 2501: }
! 2502:
! 2503: /*
! 2504: * Delete any multicast address we have asked to add from parent
! 2505: * interface. Called when the carp is being unconfigured.
! 2506: */
! 2507: void
! 2508: carp_ether_purgemulti(struct carp_softc *sc)
! 2509: {
! 2510: struct ifnet *ifp = sc->sc_carpdev; /* Parent. */
! 2511: struct carp_mc_entry *mc;
! 2512: union {
! 2513: struct ifreq ifreq;
! 2514: struct {
! 2515: char ifr_name[IFNAMSIZ];
! 2516: struct sockaddr_storage ifr_ss;
! 2517: } ifreq_storage;
! 2518: } u;
! 2519: struct ifreq *ifr = &u.ifreq;
! 2520:
! 2521: if (ifp == NULL)
! 2522: return;
! 2523:
! 2524: memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
! 2525: while ((mc = LIST_FIRST(&sc->carp_mc_listhead)) != NULL) {
! 2526: memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
! 2527: (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
! 2528: LIST_REMOVE(mc, mc_entries);
! 2529: FREE(mc, M_DEVBUF);
! 2530: }
! 2531: }
CVSweb