--- /dev/null 2006-01-11 22:05:01.000000000 -0800 +++ sbin/ifconfig/carp.c 2006-01-11 10:17:44.000000000 -0800 @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2002 Michael Shalayeff. All rights reserved. + * Copyright (c) 2003 Ryan McBride. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +void carp_status(void); +void setcarp_advbase(const char *,int); +void setcarp_advskew(const char *, int); +void setcarp_passwd(const char *, int); +void setcarp_vhid(const char *, int); +void setcarp_state(const char *, int); +void setcarpdev(const char *, int); +void unsetcarpdev(const char *, int); + +static const char *carp_states[] = { CARP_STATES }; + +void +carp_status(void) +{ + const char *state; + struct carpreq carpr; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + return; + + if (carpr.carpr_vhid > 0) { + if (carpr.carpr_state > CARP_MAXSTATE) + state = ""; + else + state = carp_states[carpr.carpr_state]; + + printf("\tcarp: %s carpdev %s vhid %d advbase %d advskew %d\n", + state, carpr.carpr_carpdev[0] != '\0' ? + carpr.carpr_carpdev : "none", carpr.carpr_vhid, + carpr.carpr_advbase, carpr.carpr_advskew); + } +} + +/* ARGSUSED */ +void +setcarp_passwd(const char *val, int d) +{ + struct carpreq carpr; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + /* XXX Should hash the password into the key here, perhaps? */ + strlcpy((char *)carpr.carpr_key, val, CARP_KEY_LEN); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} + +/* ARGSUSED */ +void +setcarp_vhid(const char *val, int d) +{ + struct carpreq carpr; + long tmp; + char *ep; + int vhid; + + tmp = strtol(val, &ep, 10); + + errno = 0; + if (*ep != '\0' || tmp < 0 || tmp > 255 + || errno == ERANGE) + errx(EXIT_FAILURE, "advskew: %s: must be between 0 and 255", + val); + + vhid = (int)tmp; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + carpr.carpr_vhid = vhid; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} + +/* ARGSUSED */ +void +setcarp_advskew(const char *val, int d) +{ + struct carpreq carpr; + long tmp; + char *ep; + int advskew; + + tmp = strtol(val, &ep, 10); + + errno = 0; + if (*ep != '\0' || tmp < 0 || tmp > 255 + || errno == ERANGE) + errx(EXIT_FAILURE, "advskew: %s: must be between 0 and 255", + val); + + advskew = (int)tmp; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + carpr.carpr_advskew = advskew; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} + +/* ARGSUSED */ +void +setcarp_advbase(const char *val, int d) +{ + struct carpreq carpr; + long tmp; + char *ep; + int advbase; + + tmp = strtol(val, &ep, 10); + + errno = 0; + if (*ep != '\0' || tmp < 0 || tmp > 255 + || errno == ERANGE) + errx(EXIT_FAILURE, "advskew: %s: must be between 0 and 255", + val); + + advbase = (int)tmp; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + carpr.carpr_advbase = advbase; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} + +/* ARGSUSED */ +void +setcarp_state(const char *val, int d) +{ + struct carpreq carpr; + int i; + + bzero((char *)&carpr, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + for (i = 0; i <= CARP_MAXSTATE; i++) { + if (!strcasecmp(val, carp_states[i])) { + carpr.carpr_state = i; + break; + } + } + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} + +/* ARGSUSED */ +void +setcarpdev(const char *val, int d) +{ + struct carpreq carpr; + + bzero((char *)&carpr, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + strlcpy(carpr.carpr_carpdev, val, sizeof(carpr.carpr_carpdev)); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} + +void +unsetcarpdev(const char *val, int d) +{ + struct carpreq carpr; + + bzero((char *)&carpr, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCGVH"); + + bzero((char *)&carpr.carpr_carpdev, sizeof(carpr.carpr_carpdev)); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(EXIT_FAILURE, "SIOCSVH"); +} --- /dev/null 2006-01-11 22:05:01.000000000 -0800 +++ sbin/ifconfig/carp.h 2006-01-11 10:17:44.000000000 -0800 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +void carp_status(void); +void setcarp_advbase(const char *,int); +void setcarp_advskew(const char *, int); +void setcarp_passwd(const char *, int); +void setcarp_vhid(const char *, int); +void setcarp_state(const char *, int); +void setcarpdev(const char *, int); +void unsetcarpdev(const char *, int); --- /dev/null 2006-01-11 22:05:01.000000000 -0800 +++ sbin/ifconfig/ifcarp.h 2006-01-11 10:17:44.000000000 -0800 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ --- /dev/null 2006-01-11 22:05:01.000000000 -0800 +++ sys/netinet/ip_carp.c 2006-01-11 10:17:44.000000000 -0800 @@ -0,0 +1,2238 @@ +/* $OpenBSD: ip_carp.c,v 1.113 2005/11/04 08:11:54 mcbride Exp $ */ + +/* + * Copyright (c) 2002 Michael Shalayeff. All rights reserved. + * Copyright (c) 2003 Ryan McBride. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * TODO: + * - iface reconfigure + * - support for hardware checksum calculations; + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if NFDDI > 0 +#include +#endif +#if NTOKEN > 0 +#include +#endif + +#ifdef INET +#include +#include +#include +#include +#include + +#include +#endif + +#ifdef INET6 +#include +#include +#include +#include +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include +#endif + +#include + +#include + +struct carp_mc_entry { + LIST_ENTRY(carp_mc_entry) mc_entries; + union { + struct ether_multi *mcu_enm; + } mc_u; + struct sockaddr_storage mc_addr; +}; +#define mc_enm mc_u.mcu_enm + +struct carp_softc { + struct ethercom sc_ac; + u_int8_t ec_enaddr[ETHER_ADDR_LEN]; +#define sc_if sc_ac.ec_if +#define sc_carpdev sc_ac.ec_if.if_carpdev + int ah_cookie; + int lh_cookie; + struct ip_moptions sc_imo; +#ifdef INET6 + struct ip6_moptions sc_im6o; +#endif /* INET6 */ + TAILQ_ENTRY(carp_softc) sc_list; + + enum { INIT = 0, BACKUP, MASTER } sc_state; + + int sc_suppress; + int sc_bow_out; + + int sc_sendad_errors; +#define CARP_SENDAD_MAX_ERRORS 3 + int sc_sendad_success; +#define CARP_SENDAD_MIN_SUCCESS 3 + + int sc_vhid; + int sc_advskew; + int sc_naddrs; + int sc_naddrs6; + int sc_advbase; /* seconds */ + int sc_init_counter; + u_int64_t sc_counter; + + /* authentication */ +#define CARP_HMAC_PAD 64 + unsigned char sc_key[CARP_KEY_LEN]; + unsigned char sc_pad[CARP_HMAC_PAD]; + SHA1_CTX sc_sha1; + u_int32_t sc_hashkey[2]; + + struct callout sc_ad_tmo; /* advertisement timeout */ + struct callout sc_md_tmo; /* master down timeout */ + struct callout sc_md6_tmo; /* master down timeout */ + + LIST_HEAD(__carp_mchead, carp_mc_entry) carp_mc_listhead; +}; + +int carp_suppress_preempt = 0; +int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 0, 0 }; /* XXX for now */ +struct carpstats carpstats; + +struct carp_if { + TAILQ_HEAD(, carp_softc) vhif_vrs; + int vhif_nvrs; + + struct ifnet *vhif_ifp; +}; + +#define CARP_LOG(sc, s) \ + if (carp_opts[CARPCTL_LOG]) { \ + if (sc) \ + log(LOG_INFO, "%s: ", \ + (sc)->sc_if.if_xname); \ + else \ + log(LOG_INFO, "carp: "); \ + addlog s; \ + addlog("\n"); \ + } + +void carp_hmac_prepare(struct carp_softc *); +void carp_hmac_generate(struct carp_softc *, u_int32_t *, + unsigned char *); +int carp_hmac_verify(struct carp_softc *, u_int32_t *, + unsigned char *); +void carp_setroute(struct carp_softc *, int); +void carp_proto_input_c(struct mbuf *, struct carp_header *, sa_family_t); +void carpattach(int); +void carpdetach(struct carp_softc *); +int carp_prepare_ad(struct mbuf *, struct carp_softc *, + struct carp_header *); +void carp_send_ad_all(void); +void carp_send_ad(void *); +void carp_send_arp(struct carp_softc *); +void carp_master_down(void *); +int carp_ioctl(struct ifnet *, u_long, caddr_t); +void carp_start(struct ifnet *); +void carp_setrun(struct carp_softc *, sa_family_t); +void carp_set_state(struct carp_softc *, int); +int carp_addrcount(struct carp_if *, struct in_ifaddr *, int); +enum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING }; + +void carp_multicast_cleanup(struct carp_softc *); +int carp_set_ifp(struct carp_softc *, struct ifnet *); +void carp_set_enaddr(struct carp_softc *); +void carp_addr_updated(void *); +u_int32_t carp_hash(struct carp_softc *, u_char *); +int carp_set_addr(struct carp_softc *, struct sockaddr_in *); +int carp_join_multicast(struct carp_softc *); +#ifdef INET6 +void carp_send_na(struct carp_softc *); +int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); +int carp_join_multicast6(struct carp_softc *); +#endif +int carp_clone_create(struct if_clone *, int); +int carp_clone_destroy(struct ifnet *); +int carp_ether_addmulti(struct carp_softc *, struct ifreq *); +int carp_ether_delmulti(struct carp_softc *, struct ifreq *); +void carp_ether_purgemulti(struct carp_softc *); + +struct if_clone carp_cloner = + IF_CLONE_INITIALIZER("carp", carp_clone_create, carp_clone_destroy); + +static __inline u_int16_t +carp_cksum(struct mbuf *m, int len) +{ + return (in_cksum(m, len)); +} + +void +carp_hmac_prepare(struct carp_softc *sc) +{ + u_int8_t carp_version = CARP_VERSION, type = CARP_ADVERTISEMENT; + u_int8_t vhid = sc->sc_vhid & 0xff; + SHA1_CTX sha1ctx; + u_int32_t kmd[5]; + struct ifaddr *ifa; + int i; +#ifdef INET6 + struct in6_addr in6; +#endif /* INET6 */ + + /* compute ipad from key */ + bzero(sc->sc_pad, sizeof(sc->sc_pad)); + bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key)); + for (i = 0; i < sizeof(sc->sc_pad); i++) + sc->sc_pad[i] ^= 0x36; + + /* precompute first part of inner hash */ + SHA1Init(&sc->sc_sha1); + SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad)); + SHA1Update(&sc->sc_sha1, (void *)&carp_version, sizeof(carp_version)); + SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type)); + + /* generate a key for the arpbalance hash, before the vhid is hashed */ + bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); + SHA1Final((unsigned char *)kmd, &sha1ctx); + sc->sc_hashkey[0] = kmd[0] ^ kmd[1]; + sc->sc_hashkey[1] = kmd[2] ^ kmd[3]; + + /* the rest of the precomputation */ + SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid)); +#ifdef INET + TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family == AF_INET) + SHA1Update(&sc->sc_sha1, + (void *)&ifatoia(ifa)->ia_addr.sin_addr.s_addr, + sizeof(struct in_addr)); + } +#endif /* INET */ +#ifdef INET6 + TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family == AF_INET6) { + in6 = ifatoia6(ifa)->ia_addr.sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL(&in6)) + in6.s6_addr16[1] = 0; + SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(in6)); + } + } +#endif /* INET6 */ + + /* convert ipad to opad */ + for (i = 0; i < sizeof(sc->sc_pad); i++) + sc->sc_pad[i] ^= 0x36 ^ 0x5c; +} + +void +carp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2], + unsigned char md[20]) +{ + SHA1_CTX sha1ctx; + + /* fetch first half of inner hash */ + bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx)); + + SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter)); + SHA1Final(md, &sha1ctx); + + /* outer hash */ + SHA1Init(&sha1ctx); + SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad)); + SHA1Update(&sha1ctx, md, 20); + SHA1Final(md, &sha1ctx); +} + +int +carp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2], + unsigned char md[20]) +{ + unsigned char md2[20]; + + carp_hmac_generate(sc, counter, md2); + + return (bcmp(md, md2, sizeof(md2))); +} + +void +carp_setroute(struct carp_softc *sc, int cmd) +{ + struct ifaddr *ifa; + int s; + + s = splsoftnet(); + TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { + switch (ifa->ifa_addr->sa_family) { + case AF_INET: { + int count = 0; + struct sockaddr sa; + struct rtentry *rt; + struct radix_node_head *rnh = + rt_tables[ifa->ifa_addr->sa_family]; + struct radix_node *rn; + int hr_otherif, nr_ourif; + + /* + * Avoid screwing with the routes if there are other + * carp interfaces which are master and have the same + * address. + */ + if (sc->sc_carpdev != NULL && + sc->sc_carpdev->if_carp != NULL) { + count = carp_addrcount( + (struct carp_if *)sc->sc_carpdev->if_carp, + ifatoia(ifa), CARP_COUNT_MASTER); + if ((cmd == RTM_ADD && count != 1) || + (cmd == RTM_DELETE && count != 0)) + continue; + } + + /* Remove the existing host route, if any */ + rtrequest(RTM_DELETE, ifa->ifa_addr, + ifa->ifa_addr, ifa->ifa_netmask, + RTF_HOST, NULL); + + /* Check for our address on another interface */ + rn = rnh->rnh_matchaddr(ifa->ifa_addr, rnh); + rt = (struct rtentry *)rn; + hr_otherif = (rt && rt->rt_ifp != &sc->sc_if && + rt->rt_flags & (RTF_CLONING|RTF_CLONED)); + + /* Check for a network route on our interface */ + bcopy(ifa->ifa_addr, &sa, sizeof(sa)); + satosin(&sa)->sin_addr.s_addr = satosin(ifa->ifa_netmask + )->sin_addr.s_addr & satosin(&sa)->sin_addr.s_addr; + rn = rnh->rnh_lookup(&sa, ifa->ifa_netmask, rnh); + rt = (struct rtentry *)rn; + nr_ourif = (rt && rt->rt_ifp == &sc->sc_if); + + switch (cmd) { + case RTM_ADD: + if (hr_otherif) { + ifa->ifa_rtrequest = NULL; + ifa->ifa_flags &= ~RTF_CLONING; + + rtrequest(RTM_ADD, ifa->ifa_addr, + ifa->ifa_addr, ifa->ifa_netmask, + RTF_UP | RTF_HOST, NULL); + } + if (!hr_otherif || nr_ourif || !rt) { + if (nr_ourif && !(rt->rt_flags & + RTF_CLONING)) + rtrequest(RTM_DELETE, &sa, + ifa->ifa_addr, + ifa->ifa_netmask, 0, NULL); + + ifa->ifa_rtrequest = arp_rtrequest; + ifa->ifa_flags |= RTF_CLONING; + + if (rtrequest(RTM_ADD, ifa->ifa_addr, + ifa->ifa_addr, ifa->ifa_netmask, 0, + NULL) == 0) + ifa->ifa_flags |= IFA_ROUTE; + } + break; + case RTM_DELETE: + break; + default: + break; + } + break; + } + +#ifdef INET6 + case AF_INET6: + if (cmd == RTM_ADD) + in6_ifaddloop(ifa); + else + in6_ifremloop(ifa); + break; +#endif /* INET6 */ + default: + break; + } + } + splx(s); +} + +/* + * process input packet. + * we have rearranged checks order compared to the rfc, + * but it seems more efficient this way or not possible otherwise. + */ +void +carp_proto_input(struct mbuf *m, ...) +{ + struct ip *ip = mtod(m, struct ip *); + struct carp_softc *sc = NULL; + struct carp_header *ch; + int iplen, len, hlen; + va_list ap; + + va_start(ap, m); + hlen = va_arg(ap, int); + va_end(ap); + + carpstats.carps_ipackets++; + + if (!carp_opts[CARPCTL_ALLOW]) { + m_freem(m); + return; + } + + /* check if received on a valid carp interface */ + if (m->m_pkthdr.rcvif->if_type != IFT_CARP) { + carpstats.carps_badif++; + CARP_LOG(sc, ("packet received on non-carp interface: %s", + m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return; + } + + /* verify that the IP TTL is 255. */ + if (ip->ip_ttl != CARP_DFLTTL) { + carpstats.carps_badttl++; + CARP_LOG(sc, ("received ttl %d != %d on %s", ip->ip_ttl, + CARP_DFLTTL, m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return; + } + + /* + * verify that the received packet length is + * equal to the CARP header + */ + iplen = ip->ip_hl << 2; + len = iplen + sizeof(*ch); + if (len > m->m_pkthdr.len) { + carpstats.carps_badlen++; + CARP_LOG(sc, ("packet too short %d on %s", m->m_pkthdr.len, + m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return; + } + + if ((m = m_pullup(m, len)) == NULL) { + carpstats.carps_hdrops++; + return; + } + ip = mtod(m, struct ip *); + ch = (struct carp_header *)((char *)ip + iplen); + /* verify the CARP checksum */ + m->m_data += iplen; + if (carp_cksum(m, len - iplen)) { + carpstats.carps_badsum++; + CARP_LOG(sc, ("checksum failed on %s", + m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return; + } + m->m_data -= iplen; + + carp_proto_input_c(m, ch, AF_INET); +} + +#ifdef INET6 +int +carp6_proto_input(struct mbuf **mp, int *offp, int proto) +{ + struct mbuf *m = *mp; + struct carp_softc *sc = NULL; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct carp_header *ch; + u_int len; + + carpstats.carps_ipackets6++; + + if (!carp_opts[CARPCTL_ALLOW]) { + m_freem(m); + return (IPPROTO_DONE); + } + + /* check if received on a valid carp interface */ + if (m->m_pkthdr.rcvif->if_type != IFT_CARP) { + carpstats.carps_badif++; + CARP_LOG(sc, ("packet received on non-carp interface: %s", + m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return (IPPROTO_DONE); + } + + /* verify that the IP TTL is 255 */ + if (ip6->ip6_hlim != CARP_DFLTTL) { + carpstats.carps_badttl++; + CARP_LOG(sc, ("received ttl %d != %d on %s", ip6->ip6_hlim, + CARP_DFLTTL, m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return (IPPROTO_DONE); + } + + /* verify that we have a complete carp packet */ + len = m->m_len; + IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch)); + if (ch == NULL) { + carpstats.carps_badlen++; + CARP_LOG(sc, ("packet size %u too small", len)); + return (IPPROTO_DONE); + } + + + /* verify the CARP checksum */ + m->m_data += *offp; + if (carp_cksum(m, sizeof(*ch))) { + carpstats.carps_badsum++; + CARP_LOG(sc, ("checksum failed, on %s", + m->m_pkthdr.rcvif->if_xname)); + m_freem(m); + return (IPPROTO_DONE); + } + m->m_data -= *offp; + + carp_proto_input_c(m, ch, AF_INET6); + return (IPPROTO_DONE); +} +#endif /* INET6 */ + +void +carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) +{ + struct carp_softc *sc; + u_int64_t tmp_counter; + struct timeval sc_tv, ch_tv; + + TAILQ_FOREACH(sc, &((struct carp_if *) + m->m_pkthdr.rcvif->if_carpdev->if_carp)->vhif_vrs, sc_list) + if (sc->sc_vhid == ch->carp_vhid) + break; + + if (!sc || (sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != + (IFF_UP|IFF_RUNNING)) { + carpstats.carps_badvhid++; + m_freem(m); + return; + } + + microtime(&sc->sc_if.if_lastchange); + sc->sc_if.if_ipackets++; + sc->sc_if.if_ibytes += m->m_pkthdr.len; + + /* verify the CARP version. */ + if (ch->carp_version != CARP_VERSION) { + carpstats.carps_badver++; + sc->sc_if.if_ierrors++; + CARP_LOG(sc, ("invalid version %d != %d", + ch->carp_version, CARP_VERSION)); + m_freem(m); + return; + } + + /* verify the hash */ + if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) { + carpstats.carps_badauth++; + sc->sc_if.if_ierrors++; + CARP_LOG(sc, ("incorrect hash")); + m_freem(m); + return; + } + + tmp_counter = ntohl(ch->carp_counter[0]); + tmp_counter = tmp_counter<<32; + tmp_counter += ntohl(ch->carp_counter[1]); + + /* XXX Replay protection goes here */ + + sc->sc_init_counter = 0; + sc->sc_counter = tmp_counter; + + + sc_tv.tv_sec = sc->sc_advbase; + if (carp_suppress_preempt && sc->sc_advskew < 240) + sc_tv.tv_usec = 240 * 1000000 / 256; + else + sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256; + ch_tv.tv_sec = ch->carp_advbase; + ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256; + + switch (sc->sc_state) { + case INIT: + break; + case MASTER: + /* + * If we receive an advertisement from a master who's going to + * be more frequent than us, go into BACKUP state. + */ + if (timercmp(&sc_tv, &ch_tv, >) || + timercmp(&sc_tv, &ch_tv, ==)) { + callout_stop(&sc->sc_ad_tmo); + carp_set_state(sc, BACKUP); + carp_setrun(sc, 0); + carp_setroute(sc, RTM_DELETE); + } + break; + case BACKUP: + /* + * If we're pre-empting masters who advertise slower than us, + * and this one claims to be slower, treat him as down. + */ + if (carp_opts[CARPCTL_PREEMPT] && timercmp(&sc_tv, &ch_tv, <)) { + carp_master_down(sc); + break; + } + + /* + * If the master is going to advertise at such a low frequency + * that he's guaranteed to time out, we'd might as well just + * treat him as timed out now. + */ + sc_tv.tv_sec = sc->sc_advbase * 3; + if (timercmp(&sc_tv, &ch_tv, <)) { + carp_master_down(sc); + break; + } + + /* + * Otherwise, we reset the counter and wait for the next + * advertisement. + */ + carp_setrun(sc, af); + break; + } + + m_freem(m); + return; +} + +#if 0 /* sysctl stuff will need to be reimplemented for NetBSD XXXriz */ +int +carp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, + size_t newlen) +{ + /* All sysctl names at this level are terminal. */ + if (namelen != 1) + return (ENOTDIR); + + if (name[0] <= 0 || name[0] >= CARPCTL_MAXID) + return (ENOPROTOOPT); + + return sysctl_int(oldp, oldlenp, newp, newlen, &carp_opts[name[0]]); +} +#endif + +/* + * Interface side of the CARP implementation. + */ + +/* ARGSUSED */ +void +carpattach(int n) +{ + if_clone_attach(&carp_cloner); +} + +int +carp_clone_create(ifc, unit) + struct if_clone *ifc; + int unit; +{ + extern int ifqmaxlen; + struct carp_softc *sc; + struct ifnet *ifp; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (!sc) + return (ENOMEM); + bzero(sc, sizeof(*sc)); + + sc->sc_suppress = 0; + sc->sc_advbase = CARP_DFLTINTV; + sc->sc_vhid = -1; /* required setting */ + sc->sc_advskew = 0; + sc->sc_init_counter = 1; + sc->sc_naddrs = sc->sc_naddrs6 = 0; +#ifdef INET6 + sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL; +#endif /* INET6 */ + + callout_init(&sc->sc_ad_tmo); + callout_init(&sc->sc_md_tmo); + callout_init(&sc->sc_md6_tmo); + + callout_setfunc(&sc->sc_ad_tmo, carp_send_ad, sc); + callout_setfunc(&sc->sc_md_tmo, carp_master_down, sc); + callout_setfunc(&sc->sc_md6_tmo, carp_master_down, sc); + + LIST_INIT(&sc->carp_mc_listhead); + ifp = &sc->sc_if; + ifp->if_softc = sc; + snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name, + unit); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = carp_ioctl; + ifp->if_start = carp_start; + ifp->if_output = carp_output; + ifp->if_type = IFT_CARP; + ifp->if_addrlen = ETHER_ADDR_LEN; + ifp->if_hdrlen = ETHER_HDR_LEN; + ifp->if_mtu = ETHERMTU; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + IFQ_SET_READY(&ifp->if_snd); + if_attach(ifp); + + if_alloc_sadl(ifp); + carp_set_enaddr(sc); + LIST_INIT(&sc->sc_ac.ec_multiaddrs); +#if NBPFILTER > 0 + bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN); +#endif + return (0); +} + +int +carp_clone_destroy(struct ifnet *ifp) +{ + carpdetach(ifp->if_softc); + ether_ifdetach(ifp); + if_detach(ifp); + free(ifp->if_softc, M_DEVBUF); + + return (0); +} + +void +carpdetach(struct carp_softc *sc) +{ + struct carp_if *cif; + int s; + + callout_stop(&sc->sc_ad_tmo); + callout_stop(&sc->sc_md_tmo); + callout_stop(&sc->sc_md6_tmo); + + if (sc->sc_suppress) + carp_suppress_preempt--; + sc->sc_suppress = 0; + + if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) + carp_suppress_preempt--; + sc->sc_sendad_errors = 0; + + carp_set_state(sc, INIT); + sc->sc_if.if_flags &= ~IFF_UP; + carp_setrun(sc, 0); + carp_multicast_cleanup(sc); + + s = splnet(); + if (sc->sc_carpdev != NULL) { + /* hook_disestablish(sc->sc_carpdev->if_linkstatehooks, + sc->lh_cookie); */ + pfil_remove_hook((void *)carp_carpdev_state, NULL, PFIL_IFNET, + &sc->sc_if.if_pfil); + cif = (struct carp_if *)sc->sc_carpdev->if_carp; + TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); + if (!--cif->vhif_nvrs) { + ifpromisc(sc->sc_carpdev, 0); + sc->sc_carpdev->if_carp = NULL; + FREE(cif, M_IFADDR); + } + } + sc->sc_carpdev = NULL; + splx(s); +} + +/* Detach an interface from the carp. */ +void +carp_ifdetach(struct ifnet *ifp) +{ + struct carp_softc *sc, *nextsc; + struct carp_if *cif = (struct carp_if *)ifp->if_carp; + + for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) { + nextsc = TAILQ_NEXT(sc, sc_list); + carpdetach(sc); + } +} + +int +carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch) +{ + if (sc->sc_init_counter) { + /* this could also be seconds since unix epoch */ + sc->sc_counter = arc4random(); + sc->sc_counter = sc->sc_counter << 32; + sc->sc_counter += arc4random(); + } else + sc->sc_counter++; + + ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff); + ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff); + + carp_hmac_generate(sc, ch->carp_counter, ch->carp_md); + + return (0); +} + +void +carp_send_ad_all(void) +{ + struct ifnet *ifp; + struct carp_if *cif; + struct carp_softc *vh; + + TAILQ_FOREACH(ifp, &ifnet, if_list) { + if (ifp->if_carp == NULL || ifp->if_type == IFT_CARP) + continue; + + cif = (struct carp_if *)ifp->if_carp; + TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == + (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER) + carp_send_ad(vh); + } + } +} + + +void +carp_send_ad(void *v) +{ + struct carp_header ch; + struct timeval tv; + struct carp_softc *sc = v; + struct carp_header *ch_ptr; + struct mbuf *m; + int error, len, advbase, advskew, s; + struct ifaddr *ifa; + struct sockaddr sa; + + s = splsoftnet(); + + advbase = advskew = 0; /* Sssssh compiler */ + if (sc->sc_carpdev == NULL) { + sc->sc_if.if_oerrors++; + goto retry_later; + } + + /* bow out if we've gone to backup (the carp interface is going down) */ + if (sc->sc_bow_out) { + sc->sc_bow_out = 0; + advbase = 255; + advskew = 255; + } else { + advbase = sc->sc_advbase; + if (!carp_suppress_preempt || sc->sc_advskew > 240) + advskew = sc->sc_advskew; + else + advskew = 240; + tv.tv_sec = advbase; + tv.tv_usec = advskew * 1000000 / 256; + } + + ch.carp_version = CARP_VERSION; + ch.carp_type = CARP_ADVERTISEMENT; + ch.carp_vhid = sc->sc_vhid; + ch.carp_advbase = advbase; + ch.carp_advskew = advskew; + ch.carp_authlen = 7; /* XXX DEFINE */ + ch.carp_pad1 = 0; /* must be zero */ + ch.carp_cksum = 0; + + +#ifdef INET + if (sc->sc_naddrs) { + struct ip *ip; + + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m == NULL) { + sc->sc_if.if_oerrors++; + carpstats.carps_onomem++; + /* XXX maybe less ? */ + goto retry_later; + } + len = sizeof(*ip) + sizeof(ch); + m->m_pkthdr.len = len; + m->m_pkthdr.rcvif = NULL; + m->m_len = len; + MH_ALIGN(m, m->m_len); + m->m_flags |= M_MCAST; + ip = mtod(m, struct ip *); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_tos = IPTOS_LOWDELAY; + ip->ip_len = htons(len); + ip->ip_id = htons(ip_randomid()); + ip->ip_off = htons(IP_DF); + ip->ip_ttl = CARP_DFLTTL; + ip->ip_p = IPPROTO_CARP; + ip->ip_sum = 0; + + bzero(&sa, sizeof(sa)); + sa.sa_family = AF_INET; + ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev); + if (ifa == NULL) + ip->ip_src.s_addr = 0; + else + ip->ip_src.s_addr = + ifatoia(ifa)->ia_addr.sin_addr.s_addr; + ip->ip_dst.s_addr = INADDR_CARP_GROUP; + + ch_ptr = (struct carp_header *)(&ip[1]); /* XXXX Check this */ + bcopy(&ch, ch_ptr, sizeof(ch)); + if (carp_prepare_ad(m, sc, ch_ptr)) + goto retry_later; + + m->m_data += sizeof(*ip); + ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip)); + m->m_data -= sizeof(*ip); + + microtime(&sc->sc_if.if_lastchange); + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + carpstats.carps_opackets++; + + error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, + NULL); + if (error) { + if (error == ENOBUFS) + carpstats.carps_onomem++; + else + CARP_LOG(sc, ("ip_output failed: %d", error)); + sc->sc_if.if_oerrors++; + if (sc->sc_sendad_errors < INT_MAX) + sc->sc_sendad_errors++; + if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { + carp_suppress_preempt++; + if (carp_suppress_preempt == 1) + carp_send_ad_all(); + } + sc->sc_sendad_success = 0; + } else { + if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { + if (++sc->sc_sendad_success >= + CARP_SENDAD_MIN_SUCCESS) { + carp_suppress_preempt--; + sc->sc_sendad_errors = 0; + } + } else + sc->sc_sendad_errors = 0; + } + } +#endif /* INET */ +#ifdef INET6 + if (sc->sc_naddrs6) { + struct ip6_hdr *ip6; + + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m == NULL) { + sc->sc_if.if_oerrors++; + carpstats.carps_onomem++; + /* XXX maybe less ? */ + goto retry_later; + } + len = sizeof(*ip6) + sizeof(ch); + m->m_pkthdr.len = len; + m->m_pkthdr.rcvif = NULL; + m->m_len = len; + MH_ALIGN(m, m->m_len); + m->m_flags |= M_MCAST; + ip6 = mtod(m, struct ip6_hdr *); + bzero(ip6, sizeof(*ip6)); + ip6->ip6_vfc |= IPV6_VERSION; + ip6->ip6_hlim = CARP_DFLTTL; + ip6->ip6_nxt = IPPROTO_CARP; + + /* set the source address */ + bzero(&sa, sizeof(sa)); + sa.sa_family = AF_INET6; + ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev); + if (ifa == NULL) /* This should never happen with IPv6 */ + bzero(&ip6->ip6_src, sizeof(struct in6_addr)); + else + bcopy(ifatoia6(ifa)->ia_addr.sin6_addr.s6_addr, + &ip6->ip6_src, sizeof(struct in6_addr)); + /* set the multicast destination */ + + ip6->ip6_dst.s6_addr8[0] = 0xff; + ip6->ip6_dst.s6_addr8[1] = 0x02; + ip6->ip6_dst.s6_addr8[15] = 0x12; + + ch_ptr = (struct carp_header *)(&ip6[1]); /* XXXX Check this */ + bcopy(&ch, ch_ptr, sizeof(ch)); + if (carp_prepare_ad(m, sc, ch_ptr)) + goto retry_later; + + m->m_data += sizeof(*ip6); + ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6)); + m->m_data -= sizeof(*ip6); + + microtime(&sc->sc_if.if_lastchange); + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + carpstats.carps_opackets6++; + + error = ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL); + if (error) { + if (error == ENOBUFS) + carpstats.carps_onomem++; + else + CARP_LOG(sc, ("ip6_output failed: %d", error)); + sc->sc_if.if_oerrors++; + if (sc->sc_sendad_errors < INT_MAX) + sc->sc_sendad_errors++; + if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) { + carp_suppress_preempt++; + if (carp_suppress_preempt == 1) + carp_send_ad_all(); + } + sc->sc_sendad_success = 0; + } else { + if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { + if (++sc->sc_sendad_success >= + CARP_SENDAD_MIN_SUCCESS) { + carp_suppress_preempt--; + sc->sc_sendad_errors = 0; + } + } else + sc->sc_sendad_errors = 0; + } + } +#endif /* INET6 */ + +retry_later: + splx(s); + if (advbase != 255 || advskew != 255) + callout_schedule(&sc->sc_ad_tmo, tvtohz(&tv)); +} + +/* + * Broadcast a gratuitous ARP request containing + * the virtual router MAC address for each IP address + * associated with the virtual router. + */ +void +carp_send_arp(struct carp_softc *sc) +{ + struct ifaddr *ifa; + struct in_addr *in; /* XXXX Check this */ + int s = splsoftnet(); + + TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { + + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + in = &ifatoia(ifa)->ia_addr.sin_addr; + arprequest(sc->sc_carpdev, in, in, sc->ec_enaddr); + DELAY(1000); /* XXX */ + } + splx(s); +} + +#ifdef INET6 +void +carp_send_na(struct carp_softc *sc) +{ + struct ifaddr *ifa; + struct in6_addr *in6; + static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; + int s = splsoftnet(); + + TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; + nd6_na_output(sc->sc_carpdev, &mcast, in6, + ND_NA_FLAG_OVERRIDE, 1, NULL); + DELAY(1000); /* XXX */ + } + splx(s); +} +#endif /* INET6 */ + +/* + * Based on bridge_hash() in if_bridge.c + */ +#define mix(a,b,c) \ + do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ + } while (0) + +u_int32_t +carp_hash(struct carp_softc *sc, u_char *src) +{ + u_int32_t a = 0x9e3779b9, b = sc->sc_hashkey[0], c = sc->sc_hashkey[1]; + + c += sc->sc_key[3] << 24; + c += sc->sc_key[2] << 16; + c += sc->sc_key[1] << 8; + c += sc->sc_key[0]; + b += src[5] << 8; + b += src[4]; + a += src[3] << 24; + a += src[2] << 16; + a += src[1] << 8; + a += src[0]; + + mix(a, b, c); + return (c); +} + +int +carp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) +{ + struct carp_softc *vh; + struct ifaddr *ifa; + int count = 0; + + TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + if ((type == CARP_COUNT_RUNNING && + (vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == + (IFF_UP|IFF_RUNNING)) || + (type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) { + TAILQ_FOREACH(ifa, &vh->sc_if.if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family == AF_INET && + ia->ia_addr.sin_addr.s_addr == + ifatoia(ifa)->ia_addr.sin_addr.s_addr) + count++; + } + } + } + return (count); +} + +int +carp_iamatch(struct in_ifaddr *ia, u_char *src, + u_int32_t *count, u_int32_t index) +{ + struct carp_softc *sc = ia->ia_ifp->if_softc; + + if (carp_opts[CARPCTL_ARPBALANCE]) { + /* + * We use the source ip to decide which virtual host should + * handle the request. If we're master of that virtual host, + * then we respond, otherwise, just drop the arp packet on + * the floor. + */ + + /* Count the elegible carp interfaces with this address */ + if (*count == 0) + *count = carp_addrcount( + (struct carp_if *)ia->ia_ifp->if_carpdev->if_carp, + ia, CARP_COUNT_RUNNING); + + /* This should never happen, but... */ + if (*count == 0) + return (0); + + if (carp_hash(sc, src) % *count == index - 1 && + sc->sc_state == MASTER) { + return (1); + } + } else { + if (sc->sc_state == MASTER) + return (1); + } + + return (0); +} + +#ifdef INET6 +struct ifaddr * +carp_iamatch6(void *v, struct in6_addr *taddr) +{ + struct carp_if *cif = v; + struct carp_softc *vh; + struct ifaddr *ifa; + + TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + TAILQ_FOREACH(ifa, &vh->sc_if.if_addrlist, ifa_list) { + if (IN6_ARE_ADDR_EQUAL(taddr, + &ifatoia6(ifa)->ia_addr.sin6_addr) && + ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == + (IFF_UP|IFF_RUNNING)) && vh->sc_state == MASTER) + return (ifa); + } + } + + return (NULL); +} +#endif /* INET6 */ + +struct ifnet * +carp_ourether(void *v, struct mbuf *m, int src) +{ + struct carp_if *cif = (struct carp_if *)v; + struct carp_softc *vh; + struct ether_header *eh; + u_int8_t *ena; + + eh = mtod(m, struct ether_header *); + + if (src) + ena = (u_int8_t *)&eh->ether_shost; + else + ena = (u_int8_t *)&eh->ether_dhost; + + switch (m->m_pkthdr.rcvif->if_type) { + case IFT_ETHER: + case IFT_FDDI: + if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) + return (NULL); + break; + case IFT_ISO88025: + if (ena[0] != 3 || ena[1] || ena[4] || ena[5]) + return (NULL); + break; + default: + return (NULL); + break; + } + + TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) + if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == + (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER && + !bcmp(ena, vh->ec_enaddr, + ETHER_ADDR_LEN)) + return (&vh->sc_if); + + return (NULL); +} + +int +carp_input(struct mbuf *m) +{ + struct carp_if *cif = (struct carp_if *)m->m_pkthdr.rcvif->if_carp; + struct ifnet *ifp; + + if (m->m_flags & (M_BCAST|M_MCAST)) { + struct carp_softc *vh; + struct mbuf *m0; + + /* + * XXX Should really check the list of multicast addresses + * for each CARP interface _before_ copying. + */ + TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + m0 = m_copym(m, 0, M_COPYALL, M_DONTWAIT); + if (m0 == NULL) + continue; + m0->m_pkthdr.rcvif = &vh->sc_if; + ether_input(&vh->sc_if, m0); + } + return (1); + } + + ifp = carp_ourether(cif, m, 0); + if (ifp == NULL) + return (1); + + m->m_pkthdr.rcvif = ifp; + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); /* Check this */ +#endif + ifp->if_ipackets++; + ether_input(ifp, m); + + return (0); +} + +void +carp_master_down(void *v) +{ + struct carp_softc *sc = v; + + switch (sc->sc_state) { + case INIT: + printf("%s: master_down event in INIT state\n", + sc->sc_if.if_xname); + break; + case MASTER: + break; + case BACKUP: + carp_set_state(sc, MASTER); + carp_send_ad(sc); + carp_send_arp(sc); +#ifdef INET6 + carp_send_na(sc); +#endif /* INET6 */ + carp_setrun(sc, 0); + carp_setroute(sc, RTM_ADD); + break; + } +} + +/* + * When in backup state, af indicates whether to reset the master down timer + * for v4 or v6. If it's set to zero, reset the ones which are already pending. + */ +void +carp_setrun(struct carp_softc *sc, sa_family_t af) +{ + struct timeval tv; + + if (sc->sc_carpdev == NULL) { + sc->sc_if.if_flags &= ~IFF_RUNNING; + carp_set_state(sc, INIT); + return; + } + + if (sc->sc_if.if_flags & IFF_UP && sc->sc_vhid > 0 && + (sc->sc_naddrs || sc->sc_naddrs6) && !sc->sc_suppress) { + sc->sc_if.if_flags |= IFF_RUNNING; + } else { + sc->sc_if.if_flags &= ~IFF_RUNNING; + carp_setroute(sc, RTM_DELETE); + return; + } + + switch (sc->sc_state) { + case INIT: + if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) { + carp_set_state(sc, MASTER); + carp_setroute(sc, RTM_ADD); + carp_send_ad(sc); + carp_send_arp(sc); +#ifdef INET6 + carp_send_na(sc); +#endif /* INET6 */ + } else { + carp_set_state(sc, BACKUP); + carp_setroute(sc, RTM_DELETE); + carp_setrun(sc, 0); + } + break; + case BACKUP: + callout_stop(&sc->sc_ad_tmo); + tv.tv_sec = 3 * sc->sc_advbase; + tv.tv_usec = sc->sc_advskew * 1000000 / 256; + switch (af) { +#ifdef INET + case AF_INET: + callout_schedule(&sc->sc_md_tmo, tvtohz(&tv)); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + callout_schedule(&sc->sc_md6_tmo, tvtohz(&tv)); + break; +#endif /* INET6 */ + default: + if (sc->sc_naddrs) + callout_schedule(&sc->sc_md_tmo, tvtohz(&tv)); + if (sc->sc_naddrs6) + callout_schedule(&sc->sc_md6_tmo, tvtohz(&tv)); + break; + } + break; + case MASTER: + tv.tv_sec = sc->sc_advbase; + tv.tv_usec = sc->sc_advskew * 1000000 / 256; + callout_schedule(&sc->sc_ad_tmo, tvtohz(&tv)); + break; + } +} + +void +carp_multicast_cleanup(struct carp_softc *sc) +{ + struct ip_moptions *imo = &sc->sc_imo; +#ifdef INET6 + struct ip6_moptions *im6o = &sc->sc_im6o; +#endif + u_int16_t n = imo->imo_num_memberships; + + /* Clean up our own multicast memberships */ + while (n-- > 0) { + if (imo->imo_membership[n] != NULL) { + in_delmulti(imo->imo_membership[n]); + imo->imo_membership[n] = NULL; + } + } + imo->imo_num_memberships = 0; + imo->imo_multicast_ifp = NULL; + +#ifdef INET6 + while (!LIST_EMPTY(&im6o->im6o_memberships)) { + struct in6_multi_mship *imm = + LIST_FIRST(&im6o->im6o_memberships); + + LIST_REMOVE(imm, i6mm_chain); + in6_leavegroup(imm); + } + im6o->im6o_multicast_ifp = NULL; +#endif + + /* And any other multicast memberships */ + carp_ether_purgemulti(sc); +} + +int +carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp) +{ + struct carp_if *cif, *ncif = NULL; + struct carp_softc *vr, *after = NULL; + int myself = 0, error = 0; + int s; + + if (ifp == sc->sc_carpdev) + return (0); + + if (ifp != NULL) { + if ((ifp->if_flags & IFF_MULTICAST) == 0) + return (EADDRNOTAVAIL); + + if (ifp->if_type == IFT_CARP) + return (EINVAL); + + if (ifp->if_carp == NULL) { + MALLOC(ncif, struct carp_if *, sizeof(*cif), + M_IFADDR, M_NOWAIT); + if (ncif == NULL) + return (ENOBUFS); + if ((error = ifpromisc(ifp, 1))) { + FREE(ncif, M_IFADDR); + return (error); + } + + ncif->vhif_ifp = ifp; + TAILQ_INIT(&ncif->vhif_vrs); + } else { + cif = (struct carp_if *)ifp->if_carp; + TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) + if (vr != sc && vr->sc_vhid == sc->sc_vhid) + return (EINVAL); + } + + /* detach from old interface */ + if (sc->sc_carpdev != NULL) + carpdetach(sc); + + /* join multicast groups */ + if (sc->sc_naddrs < 0 && + (error = carp_join_multicast(sc)) != 0) { + if (ncif != NULL) + FREE(ncif, M_IFADDR); + return (error); + } + +#ifdef INET6 + if (sc->sc_naddrs6 < 0 && + (error = carp_join_multicast6(sc)) != 0) { + if (ncif != NULL) + FREE(ncif, M_IFADDR); + carp_multicast_cleanup(sc); + return (error); + } +#endif + + /* attach carp interface to physical interface */ + if (ncif != NULL) + ifp->if_carp = (caddr_t)ncif; + sc->sc_carpdev = ifp; + cif = (struct carp_if *)ifp->if_carp; + TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { + if (vr == sc) + myself = 1; + if (vr->sc_vhid < sc->sc_vhid) + after = vr; + } + + if (!myself) { + /* We're trying to keep things in order */ + if (after == NULL) { + TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list); + } else { + TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, + sc, sc_list); + } + cif->vhif_nvrs++; + } + if (sc->sc_naddrs || sc->sc_naddrs6) + sc->sc_if.if_flags |= IFF_UP; + carp_set_enaddr(sc); + s = splnet(); + /* sc->lh_cookie = hook_establish(ifp->if_linkstatehooks, + carp_carpdev_state, ifp); */ + sc->lh_cookie = pfil_add_hook((void *)carp_carpdev_state, NULL, + PFIL_IFNET, &sc->sc_if.if_pfil); + carp_carpdev_state(ifp); + splx(s); + } else { + carpdetach(sc); + sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + } + return (0); +} + +void +carp_set_enaddr(struct carp_softc *sc) +{ + if (sc->sc_carpdev && sc->sc_carpdev->if_type == IFT_ISO88025) { + sc->ec_enaddr[0] = 3; + sc->ec_enaddr[1] = 0; + sc->ec_enaddr[2] = 0x40 >> (sc->sc_vhid - 1); + sc->ec_enaddr[3] = 0x40000 >> (sc->sc_vhid - 1); + sc->ec_enaddr[4] = 0; + sc->ec_enaddr[5] = 0; + } else { + sc->ec_enaddr[0] = 0; + sc->ec_enaddr[1] = 0; + sc->ec_enaddr[2] = 0x5e; + sc->ec_enaddr[3] = 0; + sc->ec_enaddr[4] = 1; + sc->ec_enaddr[5] = sc->sc_vhid; + } + + bcopy(&sc->ec_enaddr, + LLADDR(sc->sc_if.if_sadl), sc->sc_if.if_addrlen); +} + +void +carp_addr_updated(void *v) +{ + struct carp_softc *sc = (struct carp_softc *) v; + struct ifaddr *ifa; + int new_naddrs = 0, new_naddrs6 = 0; + + TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family == AF_INET) + new_naddrs++; + else if (ifa->ifa_addr->sa_family == AF_INET6) + new_naddrs6++; + } + + /* Handle a callback after SIOCDIFADDR */ + if (new_naddrs < sc->sc_naddrs || new_naddrs6 < sc->sc_naddrs6) { + struct in_addr mc_addr; + struct in_multi *inm; + + sc->sc_naddrs = new_naddrs; + sc->sc_naddrs6 = new_naddrs6; + + /* Re-establish multicast membership removed by in_control */ + mc_addr.s_addr = INADDR_CARP_GROUP; + IN_LOOKUP_MULTI(mc_addr, &sc->sc_if, inm); + if (inm == NULL) { + bzero(&sc->sc_imo, sizeof(sc->sc_imo)); + + if (sc->sc_carpdev != NULL && sc->sc_naddrs > 0) + carp_join_multicast(sc); + } + + if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) { + sc->sc_if.if_flags &= ~IFF_UP; + carp_set_state(sc, INIT); + } else + carp_hmac_prepare(sc); + } + + carp_setrun(sc, 0); +} + +int +carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) +{ + struct ifnet *ifp = sc->sc_carpdev; + struct in_ifaddr *ia, *ia_if; + int error = 0; + + if (sin->sin_addr.s_addr == 0) { + if (!(sc->sc_if.if_flags & IFF_UP)) + carp_set_state(sc, INIT); + if (sc->sc_naddrs) + sc->sc_if.if_flags |= IFF_UP; + carp_setrun(sc, 0); + return (0); + } + + /* we have to do this by hand to ensure we don't match on ourselves */ + ia_if = NULL; + for (ia = TAILQ_FIRST(&in_ifaddrhead); ia; + ia = TAILQ_NEXT(ia, ia_list)) { + + /* and, yeah, we need a multicast-capable iface too */ + if (ia->ia_ifp != &sc->sc_if && + ia->ia_ifp->if_type != IFT_CARP && + (ia->ia_ifp->if_flags & IFF_MULTICAST) && + (sin->sin_addr.s_addr & ia->ia_subnetmask) == + ia->ia_subnet) { + if (!ia_if) + ia_if = ia; + } + } + + if (ia_if) { + ia = ia_if; + if (ifp) { + if (ifp != ia->ia_ifp) + return (EADDRNOTAVAIL); + } else { + ifp = ia->ia_ifp; + } + } + + if ((error = carp_set_ifp(sc, ifp))) + return (error); + + if (sc->sc_carpdev == NULL) + return (EADDRNOTAVAIL); + + if (sc->sc_naddrs == 0 && (error = carp_join_multicast(sc)) != 0) + return (error); + + sc->sc_naddrs++; + if (sc->sc_carpdev != NULL) + sc->sc_if.if_flags |= IFF_UP; + + carp_set_state(sc, INIT); + + /* + * Hook if_addrhooks so that we get a callback after in_ifinit has run, + * to correct any inappropriate routes that it inserted. + */ + if (sc->ah_cookie == 0) + /* sc->ah_cookie = hook_establish(sc->sc_if.if_addrhooks, 0, + carp_addr_updated, sc); */ + sc->ah_cookie = pfil_add_hook((void *)carp_addr_updated, NULL, + PFIL_IFNET, &sc->sc_if.if_pfil); + + return (0); +} + +int +carp_join_multicast(struct carp_softc *sc) +{ + struct ip_moptions *imo = &sc->sc_imo, tmpimo; + struct in_addr addr; + + bzero(&tmpimo, sizeof(tmpimo)); + addr.s_addr = INADDR_CARP_GROUP; + if ((tmpimo.imo_membership[0] = + in_addmulti(&addr, &sc->sc_if)) == NULL) { + return (ENOBUFS); + } + + imo->imo_membership[0] = tmpimo.imo_membership[0]; + imo->imo_num_memberships = 1; + imo->imo_multicast_ifp = &sc->sc_if; + imo->imo_multicast_ttl = CARP_DFLTTL; + imo->imo_multicast_loop = 0; + return (0); +} + + +#ifdef INET6 +int +carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) +{ + struct ifnet *ifp = sc->sc_carpdev; + struct in6_ifaddr *ia, *ia_if; + int error = 0; + + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (!(sc->sc_if.if_flags & IFF_UP)) + carp_set_state(sc, INIT); + if (sc->sc_naddrs6) + sc->sc_if.if_flags |= IFF_UP; + carp_setrun(sc, 0); + return (0); + } + + /* we have to do this by hand to ensure we don't match on ourselves */ + ia_if = NULL; + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + int i; + + for (i = 0; i < 4; i++) { + if ((sin6->sin6_addr.s6_addr32[i] & + ia->ia_prefixmask.sin6_addr.s6_addr32[i]) != + (ia->ia_addr.sin6_addr.s6_addr32[i] & + ia->ia_prefixmask.sin6_addr.s6_addr32[i])) + break; + } + /* and, yeah, we need a multicast-capable iface too */ + if (ia->ia_ifp != &sc->sc_if && + ia->ia_ifp->if_type != IFT_CARP && + (ia->ia_ifp->if_flags & IFF_MULTICAST) && + (i == 4)) { + if (!ia_if) + ia_if = ia; + } + } + + if (ia_if) { + ia = ia_if; + if (sc->sc_carpdev) { + if (sc->sc_carpdev != ia->ia_ifp) + return (EADDRNOTAVAIL); + } else { + ifp = ia->ia_ifp; + } + } + + if ((error = carp_set_ifp(sc, ifp))) + return (error); + + if (sc->sc_carpdev == NULL) + return (EADDRNOTAVAIL); + + if (sc->sc_naddrs6 == 0 && (error = carp_join_multicast6(sc)) != 0) + return (error); + + sc->sc_naddrs6++; + if (sc->sc_carpdev != NULL) + sc->sc_if.if_flags |= IFF_UP; + carp_set_state(sc, INIT); + carp_setrun(sc, 0); + + return (0); +} + +int +carp_join_multicast6(struct carp_softc *sc) +{ + struct in6_multi_mship *imm, *imm2; + struct ip6_moptions *im6o = &sc->sc_im6o; + struct sockaddr_in6 addr6; + int error; + + /* Join IPv6 CARP multicast group */ + bzero(&addr6, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_len = sizeof(addr6); + addr6.sin6_addr.s6_addr16[0] = htons(0xff02); + addr6.sin6_addr.s6_addr16[1] = htons(sc->sc_if.if_index); + addr6.sin6_addr.s6_addr8[15] = 0x12; + if ((imm = in6_joingroup(&sc->sc_if, + &addr6.sin6_addr, &error)) == NULL) { + return (error); + } + /* join solicited multicast address */ + bzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr)); + addr6.sin6_addr.s6_addr16[0] = htons(0xff02); + addr6.sin6_addr.s6_addr16[1] = htons(sc->sc_if.if_index); + addr6.sin6_addr.s6_addr32[1] = 0; + addr6.sin6_addr.s6_addr32[2] = htonl(1); + addr6.sin6_addr.s6_addr32[3] = 0; + addr6.sin6_addr.s6_addr8[12] = 0xff; + if ((imm2 = in6_joingroup(&sc->sc_if, + &addr6.sin6_addr, &error)) == NULL) { + in6_leavegroup(imm); + return (error); + } + + /* apply v6 multicast membership */ + im6o->im6o_multicast_ifp = &sc->sc_if; + if (imm) + LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, + i6mm_chain); + if (imm2) + LIST_INSERT_HEAD(&im6o->im6o_memberships, imm2, + i6mm_chain); + + return (0); +} + +#endif /* INET6 */ + +int +carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) +{ + struct proc *p = curlwp->l_proc; /* XXX */ + struct carp_softc *sc = ifp->if_softc, *vr; + struct carpreq carpr; + struct ifaddr *ifa; + struct ifreq *ifr; + struct ifaliasreq *ifra; + struct ifnet *cdev = NULL; + int error = 0; + + ifa = (struct ifaddr *)addr; + ifra = (struct ifaliasreq *)addr; + ifr = (struct ifreq *)addr; + + switch (cmd) { + case SIOCSIFADDR: + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + sc->sc_if.if_flags |= IFF_UP; + bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, + sizeof(struct sockaddr)); + error = carp_set_addr(sc, satosin(ifa->ifa_addr)); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + sc->sc_if.if_flags|= IFF_UP; + error = carp_set_addr6(sc, satosin6(ifa->ifa_addr)); + break; +#endif /* INET6 */ + default: + error = EAFNOSUPPORT; + break; + } + break; + + case SIOCAIFADDR: + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + sc->sc_if.if_flags |= IFF_UP; + bcopy(ifa->ifa_addr, ifa->ifa_dstaddr, + sizeof(struct sockaddr)); + error = carp_set_addr(sc, satosin(&ifra->ifra_addr)); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + sc->sc_if.if_flags |= IFF_UP; + error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr)); + break; +#endif /* INET6 */ + default: + error = EAFNOSUPPORT; + break; + } + break; + + case SIOCSIFFLAGS: + if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) { + callout_stop(&sc->sc_ad_tmo); + callout_stop(&sc->sc_md_tmo); + callout_stop(&sc->sc_md6_tmo); + if (sc->sc_state == MASTER) { + /* we need the interface up to bow out */ + sc->sc_if.if_flags |= IFF_UP; + sc->sc_bow_out = 1; + carp_send_ad(sc); + } + sc->sc_if.if_flags &= ~IFF_UP; + carp_set_state(sc, INIT); + carp_setrun(sc, 0); + } else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) { + sc->sc_if.if_flags |= IFF_UP; + carp_setrun(sc, 0); + } + break; + + case SIOCSVH: + if ((error = suser(p->p_ucred, &(p->p_acflag))) != 0) + break; + if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr))) + break; + error = 1; + if (carpr.carpr_carpdev[0] != '\0' && + (cdev = ifunit(carpr.carpr_carpdev)) == NULL) + return (EINVAL); + if ((error = carp_set_ifp(sc, cdev))) + return (error); + if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { + switch (carpr.carpr_state) { + case BACKUP: + callout_stop(&sc->sc_ad_tmo); + carp_set_state(sc, BACKUP); + carp_setrun(sc, 0); + carp_setroute(sc, RTM_DELETE); + break; + case MASTER: + carp_master_down(sc); + break; + default: + break; + } + } + if (carpr.carpr_vhid > 0) { + if (carpr.carpr_vhid > 255) { + error = EINVAL; + break; + } + if (sc->sc_carpdev) { + struct carp_if *cif; + cif = (struct carp_if *)sc->sc_carpdev->if_carp; + TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) + if (vr != sc && + vr->sc_vhid == carpr.carpr_vhid) + return (EINVAL); + } + sc->sc_vhid = carpr.carpr_vhid; + carp_set_enaddr(sc); + carp_set_state(sc, INIT); + error--; + } + if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { + if (carpr.carpr_advskew >= 255) { + error = EINVAL; + break; + } + if (carpr.carpr_advbase > 255) { + error = EINVAL; + break; + } + sc->sc_advbase = carpr.carpr_advbase; + sc->sc_advskew = carpr.carpr_advskew; + error--; + } + bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key)); + if (error > 0) + error = EINVAL; + else { + error = 0; + carp_setrun(sc, 0); + } + break; + + case SIOCGVH: + bzero(&carpr, sizeof(carpr)); + if (sc->sc_carpdev != NULL) + strlcpy(carpr.carpr_carpdev, sc->sc_carpdev->if_xname, + IFNAMSIZ); + carpr.carpr_state = sc->sc_state; + carpr.carpr_vhid = sc->sc_vhid; + carpr.carpr_advbase = sc->sc_advbase; + carpr.carpr_advskew = sc->sc_advskew; + if (suser(p->p_ucred, &(p->p_acflag)) == 0) + bcopy(sc->sc_key, carpr.carpr_key, + sizeof(carpr.carpr_key)); + error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); + break; + + case SIOCADDMULTI: + error = carp_ether_addmulti(sc, ifr); + break; + + case SIOCDELMULTI: + error = carp_ether_delmulti(sc, ifr); + break; + + default: + error = EINVAL; + } + + carp_hmac_prepare(sc); + return (error); +} + + +/* + * Start output on carp interface. This function should never be called. + */ +void +carp_start(struct ifnet *ifp) +{ +#ifdef DEBUG + printf("%s: start called\n", ifp->if_xname); +#endif +} + +int +carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, + struct rtentry *rt) +{ + struct carp_softc *sc = ((struct carp_softc *)ifp->if_softc); + + if (sc->sc_carpdev != NULL && sc->sc_state == MASTER) + return (sc->sc_carpdev->if_output(ifp, m, sa, rt)); + else { + m_freem(m); + return (ENETUNREACH); + } +} + +void +carp_set_state(struct carp_softc *sc, int state) +{ + if (sc->sc_state == state) + return; + + sc->sc_state = state; + switch (state) { + case BACKUP: + sc->sc_if.if_link_state = LINK_STATE_DOWN; + break; + case MASTER: + sc->sc_if.if_link_state = LINK_STATE_UP; + break; + default: + sc->sc_if.if_link_state = LINK_STATE_UNKNOWN; + break; + } + rt_ifmsg(&sc->sc_if); +} + +void +carp_carpdev_state(void *v) +{ + struct carp_if *cif; + struct carp_softc *sc; + struct ifnet *ifp = v; + + if (ifp->if_type == IFT_CARP) + return; + + cif = (struct carp_if *)ifp->if_carp; + + TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { + int suppressed = sc->sc_suppress; + + if (sc->sc_carpdev->if_link_state == LINK_STATE_DOWN || + !(sc->sc_carpdev->if_flags & IFF_UP)) { + sc->sc_if.if_flags &= ~IFF_RUNNING; + callout_stop(&sc->sc_ad_tmo); + callout_stop(&sc->sc_md_tmo); + callout_stop(&sc->sc_md6_tmo); + carp_set_state(sc, INIT); + sc->sc_suppress = 1; + carp_setrun(sc, 0); + if (!suppressed) { + carp_suppress_preempt++; + if (carp_suppress_preempt == 1) + carp_send_ad_all(); + } + } else { + carp_set_state(sc, INIT); + sc->sc_suppress = 0; + carp_setrun(sc, 0); + if (suppressed) + carp_suppress_preempt--; + } + } +} + +int +carp_ether_addmulti(struct carp_softc *sc, struct ifreq *ifr) +{ + struct ifnet *ifp; + struct carp_mc_entry *mc; + u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; + int error; + + ifp = sc->sc_carpdev; + if (ifp == NULL) + return (EINVAL); + + error = ether_addmulti(ifr, &sc->sc_ac); + if (error != ENETRESET) + return (error); + + /* + * This is new multicast address. We have to tell parent + * about it. Also, remember this multicast address so that + * we can delete them on unconfigure. + */ + MALLOC(mc, struct carp_mc_entry *, sizeof(struct carp_mc_entry), + M_DEVBUF, M_NOWAIT); + if (mc == NULL) { + error = ENOMEM; + goto alloc_failed; + } + + /* + * As ether_addmulti() returns ENETRESET, following two + * statement shouldn't fail. + */ + (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi); + ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, mc->mc_enm); + memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len); + LIST_INSERT_HEAD(&sc->carp_mc_listhead, mc, mc_entries); + + error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr); + if (error != 0) + goto ioctl_failed; + + return (error); + + ioctl_failed: + LIST_REMOVE(mc, mc_entries); + FREE(mc, M_DEVBUF); + alloc_failed: + (void)ether_delmulti(ifr, &sc->sc_ac); + + return (error); +} + +int +carp_ether_delmulti(struct carp_softc *sc, struct ifreq *ifr) +{ + struct ifnet *ifp; + struct ether_multi *enm; + struct carp_mc_entry *mc; + u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; + int error; + + ifp = sc->sc_carpdev; + if (ifp == NULL) + return (EINVAL); + + /* + * Find a key to lookup carp_mc_entry. We have to do this + * before calling ether_delmulti for obvious reason. + */ + if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0) + return (error); + ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, enm); + if (enm == NULL) + return (EINVAL); + + LIST_FOREACH(mc, &sc->carp_mc_listhead, mc_entries) + if (mc->mc_enm == enm) + break; + + /* We won't delete entries we didn't add */ + if (mc == NULL) + return (EINVAL); + + error = ether_delmulti(ifr, &sc->sc_ac); + if (error != ENETRESET) + return (error); + + /* We no longer use this multicast address. Tell parent so. */ + error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr); + if (error == 0) { + /* And forget about this address. */ + LIST_REMOVE(mc, mc_entries); + FREE(mc, M_DEVBUF); + } else + (void)ether_addmulti(ifr, &sc->sc_ac); + return (error); +} + +/* + * Delete any multicast address we have asked to add from parent + * interface. Called when the carp is being unconfigured. + */ +void +carp_ether_purgemulti(struct carp_softc *sc) +{ + struct ifnet *ifp = sc->sc_carpdev; /* Parent. */ + struct carp_mc_entry *mc; + union { + struct ifreq ifreq; + struct { + char ifr_name[IFNAMSIZ]; + struct sockaddr_storage ifr_ss; + } ifreq_storage; + } u; + struct ifreq *ifr = &u.ifreq; + + if (ifp == NULL) + return; + + memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ); + while ((mc = LIST_FIRST(&sc->carp_mc_listhead)) != NULL) { + memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len); + (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr); + LIST_REMOVE(mc, mc_entries); + FREE(mc, M_DEVBUF); + } +} + +/* + * Compute number of hz in the specified amount of time. This + * function is from OpenBSD. + */ +int +tvtohz(struct timeval *tv) +{ + unsigned long ticks; + long sec, usec; + + /* + * If the number of usecs in the whole seconds part of the time + * fits in a long, then the total number of usecs will + * fit in an unsigned long. Compute the total and convert it to + * ticks, rounding up and adding 1 to allow for the current tick + * to expire. Rounding also depends on unsigned long arithmetic + * to avoid overflow. + * + * Otherwise, if the number of ticks in the whole seconds part of + * the time fits in a long, then convert the parts to + * ticks separately and add, using similar rounding methods and + * overflow avoidance. This method would work in the previous + * case but it is slightly slower and assumes that hz is integral. + * + * Otherwise, round the time down to the maximum + * representable value. + * + * If ints have 32 bits, then the maximum value for any timeout in + * 10ms ticks is 248 days. + */ + sec = tv->tv_sec; + usec = tv->tv_usec; + if (sec < 0 || (sec == 0 && usec <= 0)) + ticks = 0; + else if (sec <= LONG_MAX / 1000000) + ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1)) + / tick + 1; + else if (sec <= LONG_MAX / hz) + ticks = sec * hz + + ((unsigned long)usec + (tick - 1)) / tick + 1; + else + ticks = LONG_MAX; + if (ticks > INT_MAX) + ticks = INT_MAX; + return ((int)ticks); +} --- /dev/null 2006-01-11 22:05:01.000000000 -0800 +++ sys/netinet/ip_carp.h 2006-01-11 10:17:44.000000000 -0800 @@ -0,0 +1,169 @@ +/* $OpenBSD: ip_carp.h,v 1.18 2005/04/20 23:00:41 mpf Exp $ */ + +/* + * Copyright (c) 2002 Michael Shalayeff. All rights reserved. + * Copyright (c) 2003 Ryan McBride. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NETINET_IP_CARP_H_ +#define _NETINET_IP_CARP_H_ + +/* + * The CARP header layout is as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| Type | VirtualHostID | AdvSkew | Auth Len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | AdvBase | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Counter (1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Counter (2) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SHA-1 HMAC (1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SHA-1 HMAC (2) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SHA-1 HMAC (3) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SHA-1 HMAC (4) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SHA-1 HMAC (5) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +struct carp_header { +#if BYTE_ORDER == LITTLE_ENDIAN + u_int8_t carp_type:4, + carp_version:4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t carp_version:4, + carp_type:4; +#endif + u_int8_t carp_vhid; /* virtual host id */ + u_int8_t carp_advskew; /* advertisement skew */ + u_int8_t carp_authlen; /* size of counter+md, 32bit chunks */ + u_int8_t carp_pad1; /* reserved */ + u_int8_t carp_advbase; /* advertisement interval */ + u_int16_t carp_cksum; + u_int32_t carp_counter[2]; + unsigned char carp_md[20]; /* SHA1 HMAC */ +} __packed; + +#define CARP_DFLTTL 255 + +/* carp_version */ +#define CARP_VERSION 2 + +/* carp_type */ +#define CARP_ADVERTISEMENT 0x01 + +#define CARP_KEY_LEN 20 /* a sha1 hash of a passphrase */ + +/* carp_advbase */ +#define CARP_DFLTINTV 1 + +/* + * Statistics. + */ +struct carpstats { + u_int64_t carps_ipackets; /* total input packets, IPv4 */ + u_int64_t carps_ipackets6; /* total input packets, IPv6 */ + u_int64_t carps_badif; /* wrong interface */ + u_int64_t carps_badttl; /* TTL is not CARP_DFLTTL */ + u_int64_t carps_hdrops; /* packets shorter than hdr */ + u_int64_t carps_badsum; /* bad checksum */ + u_int64_t carps_badver; /* bad (incl unsupp) version */ + u_int64_t carps_badlen; /* data length does not match */ + u_int64_t carps_badauth; /* bad authentication */ + u_int64_t carps_badvhid; /* bad VHID */ + u_int64_t carps_badaddrs; /* bad address list */ + + u_int64_t carps_opackets; /* total output packets, IPv4 */ + u_int64_t carps_opackets6; /* total output packets, IPv6 */ + u_int64_t carps_onomem; /* no memory for an mbuf */ + u_int64_t carps_ostates; /* total state updates sent */ + + u_int64_t carps_preempt; /* if enabled, preemptions */ +}; + +#define CARPDEVNAMSIZ 16 +#ifdef IFNAMSIZ +#if CARPDEVNAMSIZ != IFNAMSIZ +#error +#endif +#endif + +/* + * Configuration structure for SIOCSVH SIOCGVH + */ +struct carpreq { + int carpr_state; +#define CARP_STATES "INIT", "BACKUP", "MASTER" +#define CARP_MAXSTATE 2 + + char carpr_carpdev[CARPDEVNAMSIZ]; + int carpr_vhid; + int carpr_advskew; + int carpr_advbase; + unsigned char carpr_key[CARP_KEY_LEN]; +}; + +/* + * Names for CARP sysctl objects + */ +#define CARPCTL_ALLOW 1 /* accept incoming CARP packets */ +#define CARPCTL_PREEMPT 2 /* high-pri backup preemption mode */ +#define CARPCTL_LOG 3 /* log bad packets */ +#define CARPCTL_ARPBALANCE 4 /* balance arp responses */ +#define CARPCTL_MAXID 5 + +#define CARPCTL_NAMES { \ + { 0, 0 }, \ + { "allow", CTLTYPE_INT }, \ + { "preempt", CTLTYPE_INT }, \ + { "log", CTLTYPE_INT }, \ + { "arpbalance", CTLTYPE_INT }, \ +} + +#ifdef _KERNEL +void carp_ifdetach (struct ifnet *); +void carp_proto_input (struct mbuf *, ...); +void carp_carpdev_state(void *); +int carp6_proto_input(struct mbuf **, int *, int); +int carp_iamatch(struct in_ifaddr *, u_char *, + u_int32_t *, u_int32_t); +struct ifaddr *carp_iamatch6(void *, struct in6_addr *); +struct ifnet *carp_ourether(void *, struct mbuf *, int); +int carp_input(struct mbuf *); +int carp_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +int carp_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int tvtohz(struct timeval *); +#endif /* _KERNEL */ +#endif /* _NETINET_IP_CARP_H_ */ ? sbin/ifconfig/carp.c ? sbin/ifconfig/carp.h ? sbin/ifconfig/ifcarp.h ? sys/arch/alpha/conf/GENERIC.MP_CARP ? sys/arch/i386/conf/XENU_ASTERISK ? sys/netinet/ip_carp.c ? sys/netinet/ip_carp.h Index: etc/protocols =================================================================== RCS file: /cvsroot/src/etc/protocols,v retrieving revision 1.14 diff -u -r1.14 protocols --- etc/protocols 18 Dec 2005 21:39:52 -0000 1.14 +++ etc/protocols 12 Jan 2006 06:08:24 -0000 @@ -117,7 +117,7 @@ snp 109 SNP # Sitara Networks Protocol compaq-peer 110 Compaq-Peer # Compaq Peer Protocol ipx-in-ip 111 IPX-in-IP # IPX in IP -vrrp 112 VRRP # Virtual Router Redundancy Protocol +carp 112 CARP vrrp # Common Address Redundancy Protocol pgm 113 PGM # PGM Reliable Transport Protocol # 114 # any 0-hop protocol l2tp 115 L2TP # Layer Two Tunneling Protocol Index: sbin/ifconfig/Makefile =================================================================== RCS file: /cvsroot/src/sbin/ifconfig/Makefile,v retrieving revision 1.29 diff -u -r1.29 Makefile --- sbin/ifconfig/Makefile 27 Jun 2005 01:00:05 -0000 1.29 +++ sbin/ifconfig/Makefile 12 Jan 2006 06:12:28 -0000 @@ -30,6 +30,7 @@ .endif SRCS+= af_iso.c SRCS+= af_ns.c +SRCS+= carp.c SRCS+= agr.c SRCS+= ieee80211.c Index: sbin/ifconfig/ifconfig.c =================================================================== RCS file: /cvsroot/src/sbin/ifconfig/ifconfig.c,v retrieving revision 1.169 diff -u -r1.169 ifconfig.c --- sbin/ifconfig/ifconfig.c 11 Aug 2005 20:56:05 -0000 1.169 +++ sbin/ifconfig/ifconfig.c 12 Jan 2006 06:12:33 -0000 @@ -119,6 +119,7 @@ #endif /* INET6 */ #include "agr.h" +#include "carp.h" #include "ieee80211.h" #include "tunnel.h" #include "vlan.h" @@ -228,6 +229,14 @@ { "broadcast", NEXTARG, 0, setifbroadaddr }, { "ipdst", NEXTARG, 0, setifipdst }, { "prefixlen", NEXTARG, 0, setifprefixlen}, + /* CARP */ + { "advbase", NEXTARG, 0, setcarp_advbase }, + { "advskew", NEXTARG, 0, setcarp_advskew }, + { "pass", NEXTARG, 0, setcarp_passwd }, + { "vhid", NEXTARG, 0, setcarp_vhid }, + { "state", NEXTARG, 0, setcarp_state }, + { "carpdev", NEXTARG, 0, setcarpdev }, + { "-carpdev", 1, 0, unsetcarpdev }, #ifdef INET6 { "anycast", IN6_IFF_ANYCAST, 0, setia6flags }, { "-anycast", -IN6_IFF_ANYCAST, 0, setia6flags }, @@ -1314,6 +1323,7 @@ ieee80211_status(); vlan_status(); + carp_status(); tunnel_status(); agr_status(); Index: sys/arch/i386/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC,v retrieving revision 1.718 diff -u -r1.718 GENERIC --- sys/arch/i386/conf/GENERIC 5 Jan 2006 10:24:11 -0000 1.718 +++ sys/arch/i386/conf/GENERIC 12 Jan 2006 06:13:44 -0000 @@ -736,6 +736,8 @@ pseudo-device ataraid ld* at ataraid? vendtype ? unit ? +pseudo-device carp + # ATAPI bus support atapibus* at atapi? Index: sys/arch/i386/conf/GENERIC_LAPTOP =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC_LAPTOP,v retrieving revision 1.165 diff -u -r1.165 GENERIC_LAPTOP --- sys/arch/i386/conf/GENERIC_LAPTOP 5 Jan 2006 10:24:11 -0000 1.165 +++ sys/arch/i386/conf/GENERIC_LAPTOP 12 Jan 2006 06:13:45 -0000 @@ -855,6 +855,8 @@ include "arch/i386/conf/GENERIC.local" +pseudo-device carp + # Pseudo-Devices # disk/mass storage pseudo-devices Index: sys/conf/files =================================================================== RCS file: /cvsroot/src/sys/conf/files,v retrieving revision 1.751 diff -u -r1.751 files --- sys/conf/files 7 Jan 2006 14:19:51 -0000 1.751 +++ sys/conf/files 12 Jan 2006 06:14:39 -0000 @@ -1083,6 +1083,7 @@ defpseudo bridge: ifnet, ether defpseudo bpfilter: ifnet, bpf_filter defpseudo strip: ifnet +defpseudo carp: ifnet, ether defpseudo gre: ifnet defpseudo gif: ifnet defpseudo faith: ifnet @@ -1339,6 +1340,7 @@ file netinet/if_arp.c arp | netatalk needs-flag file netinet/if_atm.c atm file netinet/in_gif.c gif & inet +file netinet/ip_carp.c carp & (inet | inet6) needs-flag file netinet/ip_ecn.c ipsec | fast_ipsec | gif | stf file netinet/ip_encap.c inet | inet6 file netinet/ip_gre.c inet & gre Index: sys/dist/pf/net/pf.c =================================================================== RCS file: /cvsroot/src/sys/dist/pf/net/pf.c,v retrieving revision 1.19 diff -u -r1.19 pf.c --- sys/dist/pf/net/pf.c 11 Dec 2005 12:24:25 -0000 1.19 +++ sys/dist/pf/net/pf.c 12 Jan 2006 06:14:50 -0000 @@ -43,6 +43,7 @@ #else #define NPFSYNC 0 #endif +#include "carp.h" #include #include Index: sys/net/if.c =================================================================== RCS file: /cvsroot/src/sys/net/if.c,v retrieving revision 1.163 diff -u -r1.163 if.c --- sys/net/if.c 11 Dec 2005 23:05:24 -0000 1.163 +++ sys/net/if.c 12 Jan 2006 06:15:04 -0000 @@ -110,6 +110,8 @@ #include "opt_natm.h" #include "opt_pfil_hooks.h" +#include "carp.h" + #include #include #include @@ -146,6 +148,10 @@ #include #endif +#if NCARP > 0 +#include +#endif + #if defined(COMPAT_43) || defined(COMPAT_LINUX) || defined(COMPAT_SVR4) || defined(COMPAT_ULTRIX) || defined(LKM) #define COMPAT_OSOCK #include @@ -575,6 +581,13 @@ altq_detach(&ifp->if_snd); #endif + +#if NCARP > 0 + /* Remove the interface from any carp group it is a part of. */ + if (ifp->if_carp && ifp->if_type != IFT_CARP) + carp_ifdetach(ifp); +#endif + #ifdef PFIL_HOOKS (void)pfil_run_hooks(&if_pfil, (struct mbuf **)PFIL_IFNET_DETACH, ifp, PFIL_IFNET); @@ -1193,6 +1206,10 @@ ifa = TAILQ_NEXT(ifa, ifa_list)) pfctlinput(PRC_IFDOWN, ifa->ifa_addr); IFQ_PURGE(&ifp->if_snd); +#if NCARP > 0 + if (ifp->if_carp) + carp_carpdev_state(ifp); +#endif rt_ifmsg(ifp); } @@ -1216,6 +1233,10 @@ ifa = TAILQ_NEXT(ifa, ifa_list)) pfctlinput(PRC_IFUP, ifa->ifa_addr); #endif +#if NCARP > 0 + if (ifp->if_carp) + carp_carpdev_state(ifp); +#endif rt_ifmsg(ifp); #ifdef INET6 in6_if_up(ifp); Index: sys/net/if.h =================================================================== RCS file: /cvsroot/src/sys/net/if.h,v retrieving revision 1.114 diff -u -r1.114 if.h --- sys/net/if.h 11 Dec 2005 23:05:24 -0000 1.114 +++ sys/net/if.h 12 Jan 2006 06:15:04 -0000 @@ -279,7 +279,12 @@ struct pfil_head if_pfil; /* filtering point */ uint64_t if_capabilities; /* interface capabilities */ uint64_t if_capenable; /* capabilities enabled */ - + union { + caddr_t carp_s; /* carp structure (used by !carp ifs) */ + struct ifnet *carp_d;/* ptr to carpdev (used by carp ifs) */ + } if_carp_ptr; +#define if_carp if_carp_ptr.carp_s +#define if_carpdev if_carp_ptr.carp_d /* * These are pre-computed based on an interfaces enabled * capabilities, for speed elsewhere. @@ -776,6 +781,7 @@ extern struct ifnet *lo0ifp; extern size_t if_indexlim; +void ether_input(struct ifnet *, struct mbuf *); char *ether_sprintf(const u_char *); void if_alloc_sadl(struct ifnet *); Index: sys/net/if_bridge.c =================================================================== RCS file: /cvsroot/src/sys/net/if_bridge.c,v retrieving revision 1.35 diff -u -r1.35 if_bridge.c --- sys/net/if_bridge.c 9 Jan 2006 13:04:38 -0000 1.35 +++ sys/net/if_bridge.c 12 Jan 2006 06:15:06 -0000 @@ -87,6 +87,7 @@ #include "opt_pfil_hooks.h" #include "bpfilter.h" #include "gif.h" +#include "carp.h" #include #include @@ -109,9 +110,11 @@ #include #include +#if NCARP > 0 || (defined(BRIDGE_IPF) && defined(PFIL_HOOKS)) +#include +#endif #if defined(BRIDGE_IPF) && defined(PFIL_HOOKS) /* Used for bridge_ip[6]_checkbasic */ -#include #include #include #include @@ -130,6 +133,11 @@ #define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) +#if NCARP > 0 +#include +#include +#endif + /* * Maximum number of addresses to cache. */ @@ -1531,7 +1539,13 @@ continue; /* It is destined for us. */ if (memcmp(LLADDR(bif->bif_ifp->if_sadl), eh->ether_dhost, - ETHER_ADDR_LEN) == 0) { + ETHER_ADDR_LEN) == 0 +#if NCARP > 0 + || (bif->bif_ifp->if_carp && + carp_ourether(bif->bif_ifp->if_carp, + m, 0) != NULL) +#endif + ) { if (bif->bif_flags & IFBIF_LEARNING) (void) bridge_rtupdate(sc, eh->ether_shost, ifp, 0, IFBAF_DYNAMIC); @@ -1549,7 +1563,13 @@ /* We just received a packet that we sent out. */ if (memcmp(LLADDR(bif->bif_ifp->if_sadl), eh->ether_shost, - ETHER_ADDR_LEN) == 0) { + ETHER_ADDR_LEN) == 0 +#if NCARP > 0 + || (bif->bif_ifp->if_carp && + carp_ourether(bif->bif_ifp->if_carp, + m, 1) != NULL) +#endif + ) { m_freem(m); return (NULL); } Index: sys/net/if_ethersubr.c =================================================================== RCS file: /cvsroot/src/sys/net/if_ethersubr.c,v retrieving revision 1.128 diff -u -r1.128 if_ethersubr.c --- sys/net/if_ethersubr.c 11 Dec 2005 23:05:24 -0000 1.128 +++ sys/net/if_ethersubr.c 12 Jan 2006 06:15:07 -0000 @@ -79,6 +79,7 @@ #include "bpfilter.h" #include "arp.h" #include "agr.h" +#include "carp.h" #include #include @@ -150,6 +151,10 @@ #include #endif +#if NCARP > 0 +#include +#endif + #ifdef IPX #include #include @@ -198,7 +203,7 @@ static int ether_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); -static void ether_input(struct ifnet *, struct mbuf *); +void ether_input(struct ifnet *, struct mbuf *); /* * Ethernet output routine. @@ -206,7 +211,7 @@ * Assumes that ifp is actually pointer to ethercom structure. */ static int -ether_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, +ether_output(struct ifnet *ifp0, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0) { u_int16_t etype = 0; @@ -216,9 +221,10 @@ struct rtentry *rt; struct mbuf *mcopy = (struct mbuf *)0; struct ether_header *eh; + struct ifnet *ifp = ifp0; ALTQ_DECL(struct altq_pktattr pktattr;) #ifdef INET - struct arphdr *ah; + struct arphdr *ah = (struct arphdr *)ifp0; #endif /* INET */ #ifdef NETATALK struct at_ifaddr *aa; @@ -227,6 +233,26 @@ #ifdef MBUFTRACE m_claimm(m, ifp->if_mowner); #endif + +#if NCARP > 0 + if (ifp->if_type == IFT_CARP) { + struct ifaddr *ifa; + + /* loop back if this is going to the carp interface */ + if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP && + (ifa = ifa_ifwithaddr(dst)) != NULL && + ifa->ifa_ifp == ifp0) + return (looutput(ifp0, m, dst, rt0)); + + ifp = ifp->if_carpdev; + ah = (struct arphdr *)ifp; + + if ((ifp0->if_flags & (IFF_UP|IFF_RUNNING)) != + (IFF_UP|IFF_RUNNING)) + senderr(ENETDOWN); + } +#endif /* NCARP > 0 */ + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); if ((rt = rt0) != NULL) { @@ -515,6 +541,13 @@ bcopy(LLADDR(ifp->if_sadl), (caddr_t)eh->ether_shost, sizeof(eh->ether_shost)); +#if NCARP > 0 + if (ifp0 != ifp && ifp0->if_type == IFT_CARP) { + bcopy(LLADDR(ifp->if_sadl), (caddr_t)eh->ether_shost, + sizeof(eh->ether_shost)); + } +#endif + #ifdef PFIL_HOOKS if ((error = pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_OUT)) != 0) return (error); @@ -530,6 +563,11 @@ return (bridge_output(ifp, m, NULL, NULL)); #endif +#if NCARP > 0 + if (ifp != ifp0) + ifp0->if_obytes += m->m_pkthdr.len + ETHER_HDR_LEN; +#endif /* NCARP > 0 */ + #ifdef ALTQ /* * If ALTQ is enabled on the parent interface, do @@ -647,7 +685,7 @@ * the packet is in the mbuf chain m with * the ether header. */ -static void +void ether_input(struct ifnet *ifp, struct mbuf *m) { struct ethercom *ec = (struct ethercom *) ifp; @@ -739,6 +777,12 @@ } else #endif /* NBRIDGE > 0 */ { + +#if NCARP > 0 + if (ifp->if_carp && ifp->if_type != IFT_CARP && + (carp_input(m) == 0)) + return; +#endif /* NCARP > 0 */ if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 && (ifp->if_flags & IFF_PROMISC) != 0 && memcmp(LLADDR(ifp->if_sadl), eh->ether_dhost, Index: sys/net/if_fddisubr.c =================================================================== RCS file: /cvsroot/src/sys/net/if_fddisubr.c,v retrieving revision 1.58 diff -u -r1.58 if_fddisubr.c --- sys/net/if_fddisubr.c 11 Dec 2005 23:05:25 -0000 1.58 +++ sys/net/if_fddisubr.c 12 Jan 2006 06:15:07 -0000 @@ -108,6 +108,7 @@ #include "opt_mbuftrace.h" #include "bpfilter.h" +#include "carp.h" #include #include @@ -159,6 +160,10 @@ #include #endif +#if NCARP > 0 +#include +#endif + #ifdef DECNET #include #endif @@ -214,7 +219,7 @@ * Assumes that ifp is actually pointer to ethercom structure. */ static int -fddi_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, +fddi_output(struct ifnet *ifp0, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0) { u_int16_t etype; @@ -224,9 +229,29 @@ struct rtentry *rt; struct fddi_header *fh; struct mbuf *mcopy = (struct mbuf *)0; + struct ifnet *ifp = ifp0; ALTQ_DECL(struct altq_pktattr pktattr;) MCLAIM(m, ifp->if_mowner); + +#if NCARP > 0 + if (ifp->if_type == IFT_CARP) { + struct ifaddr *ifa; + + /* loop back if this is going to the carp interface */ + if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP && + (ifa = ifa_ifwithaddr(dst)) != NULL && + ifa->ifa_ifp == ifp0) + return (looutput(ifp0, m, dst, rt0)); + + ifp = ifp->if_carpdev; + /* ac = (struct arpcom *)ifp; XXX */ + + if ((ifp0->if_flags & (IFF_UP|IFF_RUNNING)) != + (IFF_UP|IFF_RUNNING)) + senderr(ENETDOWN); + } +#endif /* NCARP > 0 */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); #if !defined(__bsdi__) || _BSDI_VERSION >= 199401 @@ -553,6 +578,13 @@ else memcpy(fh->fddi_shost, FDDIADDR(ifp), sizeof(fh->fddi_shost)); +#if NCARP > 0 + if (ifp0 != ifp && ifp0->if_type == IFT_CARP) + memcpy(FDDIADDR(ifp0), fh->fddi_shost, sizeof(fh->fddi_shost)); + + if (ifp != ifp0) + ifp0->if_obytes += m->m_pkthdr.len; +#endif /* NCARP > 0 */ return ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(&pktattr)); bad: @@ -642,6 +674,12 @@ goto dropanyway; etype = ntohs(l->llc_snap.ether_type); m_adj(m, 8); +#if NCARP > 0 + if (ifp->if_carp && ifp->if_type != IFT_CARP && + (carp_input(m) == 0)) + return; +#endif + switch (etype) { #ifdef INET case ETHERTYPE_IP: Index: sys/net/if_media.h =================================================================== RCS file: /cvsroot/src/sys/net/if_media.h,v retrieving revision 1.43 diff -u -r1.43 if_media.h --- sys/net/if_media.h 10 Dec 2005 23:21:38 -0000 1.43 +++ sys/net/if_media.h 12 Jan 2006 06:15:08 -0000 @@ -237,6 +237,11 @@ #define IFM_IEEE80211_FH 0x00040000 /* 2Ghz, GFSK mode */ /* + * Common Access Redundancy Protocol + */ +#define IFM_CARP 0x000000c0 + +/* * Shared media sub-types */ #define IFM_AUTO 0 /* Autoselect best media */ @@ -332,6 +337,7 @@ { IFM_TOKEN, "token" }, \ { IFM_FDDI, "FDDI" }, \ { IFM_IEEE80211, "IEEE802.11" }, \ + { IFM_CARP, "IEEE802.11" }, \ { 0, NULL }, \ } @@ -575,6 +581,9 @@ { IFM_IEEE80211, IFM_AVALID, IFM_ACTIVE, \ { "no network", "active" } }, \ \ + { IFM_CARP, IFM_AVALID, IFM_ACTIVE, \ + { "backup", "master" } }, \ + \ { 0, 0, 0, \ { NULL, NULL } }, \ } Index: sys/net/if_tokensubr.c =================================================================== RCS file: /cvsroot/src/sys/net/if_tokensubr.c,v retrieving revision 1.34 diff -u -r1.34 if_tokensubr.c --- sys/net/if_tokensubr.c 11 Dec 2005 23:05:25 -0000 1.34 +++ sys/net/if_tokensubr.c 12 Jan 2006 06:15:08 -0000 @@ -110,6 +110,7 @@ #include "opt_gateway.h" #include "bpfilter.h" +#include "carp.h" #include #include @@ -138,6 +139,10 @@ #include #include +#if NCARP > 0 +#include +#endif + #ifdef INET #include #include @@ -206,7 +211,7 @@ * XXX route info has to go into the same mbuf as the header */ static int -token_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, +token_output(struct ifnet *ifp0, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0) { u_int16_t etype; @@ -217,13 +222,33 @@ struct mbuf *mcopy = (struct mbuf *)0; struct token_header *trh; #ifdef INET - struct arphdr *ah = (struct arphdr *)ifp; + struct arphdr *ah = (struct arphdr *)ifp0; #endif /* INET */ struct token_rif *rif = (struct token_rif *)0; struct token_rif bcastrif; + struct ifnet *ifp = ifp0; size_t riflen = 0; ALTQ_DECL(struct altq_pktattr pktattr;) +#if NCARP > 0 + if (ifp->if_type == IFT_CARP) { + struct ifaddr *ifa; + + /* loop back if this is going to the carp interface */ + if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP && + (ifa = ifa_ifwithaddr(dst)) != NULL && + ifa->ifa_ifp == ifp0) + return (looutput(ifp0, m, dst, rt0)); + + ifp = ifp->if_carpdev; + ah = (struct arphdr *)ifp; + + if ((ifp0->if_flags & (IFF_UP|IFF_RUNNING)) != + (IFF_UP|IFF_RUNNING)) + senderr(ENETDOWN); + } +#endif /* NCARP > 0 */ + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); if ((rt = rt0)) { @@ -513,6 +538,13 @@ send: #endif +#if NCARP > 0 + if (ifp0 != ifp && ifp0->if_type == IFT_CARP) { + bcopy((caddr_t)((struct arpcom *)ifp0)->ac_enaddr, + (caddr_t)trh->token_shost, sizeof(trh->token_shost)); + } +#endif + return ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(&pktattr)); bad: if (m) @@ -573,6 +605,12 @@ goto dropanyway; etype = ntohs(l->llc_snap.ether_type); m_adj(m, LLC_SNAPFRAMELEN); +#if NCARP > 0 + if (ifp->if_carp && ifp->if_type != IFT_CARP && + (carp_input(m, (u_int8_t *)&th->token_shost, + (u_int8_t *)&th->token_dhost, l->llc_snap.ether_type) == 0)) + return; +#endif switch (etype) { #ifdef INET case ETHERTYPE_IP: Index: sys/net/if_types.h =================================================================== RCS file: /cvsroot/src/sys/net/if_types.h,v retrieving revision 1.24 diff -u -r1.24 if_types.h --- sys/net/if_types.h 10 Dec 2005 23:21:38 -0000 1.24 +++ sys/net/if_types.h 12 Jan 2006 06:15:09 -0000 @@ -263,5 +263,6 @@ #define IFT_FAITH 0xf2 #define IFT_PFLOG 0xf5 /* Packet filter logging */ #define IFT_PFSYNC 0xf6 /* Packet filter state syncing */ +#define IFT_CARP 0xf8 /* Common Address Redundancy Protocol */ #endif /* !_NET_IF_TYPES_H_ */ Index: sys/netinet/Makefile =================================================================== RCS file: /cvsroot/src/sys/netinet/Makefile,v retrieving revision 1.14 diff -u -r1.14 Makefile --- sys/netinet/Makefile 11 Dec 2005 12:24:57 -0000 1.14 +++ sys/netinet/Makefile 12 Jan 2006 06:15:09 -0000 @@ -4,7 +4,7 @@ INCS= icmp6.h icmp_var.h if_atm.h if_ether.h if_inarp.h igmp.h \ igmp_var.h in.h in_gif.h in_pcb.h in_pcb_hdr.h in_systm.h \ - in_var.h ip.h ip6.h ip_ecn.h ip_encap.h ip_gre.h \ + in_var.h ip.h ip_carp.h ip6.h ip_ecn.h ip_encap.h ip_gre.h \ ip_icmp.h ip_mroute.h ip_var.h pim.h pim_var.h \ tcp.h tcp_debug.h tcp_fsm.h tcp_seq.h tcp_timer.h tcp_var.h \ tcpip.h udp.h udp_var.h Index: sys/netinet/if_arp.c =================================================================== RCS file: /cvsroot/src/sys/netinet/if_arp.c,v retrieving revision 1.108 diff -u -r1.108 if_arp.c --- sys/netinet/if_arp.c 24 Dec 2005 20:45:09 -0000 1.108 +++ sys/netinet/if_arp.c 12 Jan 2006 06:15:10 -0000 @@ -83,6 +83,7 @@ #ifdef INET #include "bridge.h" +#include "carp.h" #include #include @@ -122,6 +123,9 @@ #include #endif #include "token.h" +#if NCARP > 0 +#include +#endif #define SIN(s) ((struct sockaddr_in *)s) #define SDL(s) ((struct sockaddr_dl *)s) @@ -141,8 +145,6 @@ #define rt_expire rt_rmx.rmx_expire #define rt_pksent rt_rmx.rmx_pksent -static void arprequest(struct ifnet *, - struct in_addr *, struct in_addr *, u_int8_t *); static void arptfree(struct llinfo_arp *); static void arptimer(void *); static struct llinfo_arp *arplookup(struct mbuf *, struct in_addr *, @@ -161,10 +163,10 @@ /* revarp state */ -static struct in_addr myip, srv_ip; -static int myip_initialized = 0; -static int revarp_in_progress = 0; -static struct ifnet *myip_ifp = NULL; +struct in_addr myip, srv_ip; +int myip_initialized = 0; +int revarp_in_progress = 0; +struct ifnet *myip_ifp = NULL; #ifdef DDB static void db_print_sa(const struct sockaddr *); @@ -600,7 +602,7 @@ * - arp header target ip address * - arp header source ethernet address */ -static void +void arprequest(struct ifnet *ifp, struct in_addr *sip, struct in_addr *tip, u_int8_t *enaddr) { @@ -730,6 +732,10 @@ arprequest(ifp, &SIN(rt->rt_ifa->ifa_addr)->sin_addr, &SIN(dst)->sin_addr, +#if NCARP > 0 + (rt->rt_ifp->if_type == IFT_CARP) ? + LLADDR(rt->rt_ifp->if_sadl): +#endif LLADDR(ifp->if_sadl)); else { rt->rt_flags |= RTF_REJECT; @@ -824,6 +830,9 @@ #if NBRIDGE > 0 struct in_ifaddr *bridge_ia = NULL; #endif +#if NCARP > 0 + u_int32_t count = 0, index = 0; +#endif struct sockaddr_dl *sdl; struct sockaddr sa; struct in_addr isaddr, itaddr, myaddr; @@ -886,8 +895,20 @@ */ INADDR_TO_IA(itaddr, ia); while (ia != NULL) { - if (ia->ia_ifp == m->m_pkthdr.rcvif) - break; + +#if NCARP > 0 + if (ia->ia_ifp->if_type == IFT_CARP && + ((ia->ia_ifp->if_flags & (IFF_UP|IFF_RUNNING)) == + (IFF_UP|IFF_RUNNING))) { + index++; + if (ia->ia_ifp == m->m_pkthdr.rcvif && + carp_iamatch(ia, ar_sha(ah), /* XXX */ + &count, index)) + break; + } else +#endif + if (ia->ia_ifp == m->m_pkthdr.rcvif) + break; #if NBRIDGE > 0 /* @@ -912,7 +933,7 @@ } #endif - if (ia == NULL) { + if (ia == NULL && m->m_pkthdr.rcvif->if_type != IFT_CARP) { INADDR_TO_IA(isaddr, ia); while ((ia != NULL) && ia->ia_ifp != m->m_pkthdr.rcvif) NEXT_IA_WITH_SAME_ADDR(ia); @@ -1065,6 +1086,9 @@ if (la == 0) goto out; rt = la->la_rt; + if (rt->rt_ifp->if_type == IFT_CARP && + m->m_pkthdr.rcvif->if_type != IFT_CARP) + goto out; if (ar_tha(ah)) bcopy((caddr_t)ar_sha(ah), (caddr_t)ar_tha(ah), ah->ar_hln); Index: sys/netinet/if_inarp.h =================================================================== RCS file: /cvsroot/src/sys/netinet/if_inarp.h,v retrieving revision 1.36 diff -u -r1.36 if_inarp.h --- sys/netinet/if_inarp.h 10 Dec 2005 23:36:23 -0000 1.36 +++ sys/netinet/if_inarp.h 12 Jan 2006 06:15:10 -0000 @@ -66,8 +66,9 @@ int arpresolve(struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *); void arpintr(void); +void arprequest(struct ifnet *, struct in_addr *, struct in_addr *, + u_int8_t *); void arp_drain(void); - int arpioctl(u_long, caddr_t); void arpwhohas(struct ifnet *, struct in_addr *); Index: sys/netinet/in.h =================================================================== RCS file: /cvsroot/src/sys/netinet/in.h,v retrieving revision 1.74 diff -u -r1.74 in.h --- sys/netinet/in.h 24 Dec 2005 20:45:09 -0000 1.74 +++ sys/netinet/in.h 12 Jan 2006 06:15:10 -0000 @@ -102,7 +102,7 @@ #define IPPROTO_PIM 103 /* Protocol indep. multicast */ #define IPPROTO_IPCOMP 108 /* IP Payload Comp. Protocol */ #define IPPROTO_VRRP 112 /* VRRP RFC 2338 */ - +#define IPPROTO_CARP 112 /* Common Address Resolution Protocol */ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 @@ -213,6 +213,7 @@ #define INADDR_UNSPEC_GROUP __IPADDR(0xe0000000) /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP __IPADDR(0xe0000001) /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP __IPADDR(0xe0000002) /* 224.0.0.2 */ +#define INADDR_CARP_GROUP __IPADDR(0xe0000012) /* 224.0.0.18 */ #define INADDR_MAX_LOCAL_GROUP __IPADDR(0xe00000ff) /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ Index: sys/netinet/in_proto.c =================================================================== RCS file: /cvsroot/src/sys/netinet/in_proto.c,v retrieving revision 1.71 diff -u -r1.71 in_proto.c --- sys/netinet/in_proto.c 11 Dec 2005 12:24:57 -0000 1.71 +++ sys/netinet/in_proto.c 12 Jan 2006 06:15:11 -0000 @@ -147,6 +147,11 @@ #include #endif +#include "carp.h" +#if NCARP > 0 +#include +#endif + #include "bridge.h" DOMAIN_DEFINE(inetdomain); /* forward declare and add to link set */ @@ -234,6 +239,13 @@ encap_init, 0, 0, 0, }, #endif +#if NCARP > 0 +{ SOCK_RAW, &inetdomain, IPPROTO_CARP, PR_ATOMIC|PR_ADDR, + carp_proto_input, rip_output, 0, rip_ctloutput, + rip_usrreq, + 0, 0, 0, 0, NULL +}, +#endif #if NGRE > 0 { SOCK_RAW, &inetdomain, IPPROTO_GRE, PR_ATOMIC|PR_ADDR|PR_LASTHDR, gre_input, rip_output, rip_ctlinput, rip_ctloutput, Index: sys/netinet/ip_icmp.c =================================================================== RCS file: /cvsroot/src/sys/netinet/ip_icmp.c,v retrieving revision 1.97 diff -u -r1.97 ip_icmp.c --- sys/netinet/ip_icmp.c 10 Nov 2005 13:40:38 -0000 1.97 +++ sys/netinet/ip_icmp.c 12 Jan 2006 06:15:12 -0000 @@ -333,6 +333,7 @@ icp->icmp_code = code; m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip); + nip = &icp->icmp_ip; /* * Now, copy old ip header (without options) Index: sys/netinet6/in6.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6.c,v retrieving revision 1.94 diff -u -r1.94 in6.c --- sys/netinet6/in6.c 11 Dec 2005 12:25:02 -0000 1.94 +++ sys/netinet6/in6.c 12 Jan 2006 06:15:14 -0000 @@ -224,7 +224,7 @@ * rely on the cloning mechanism from the corresponding interface route * any more. */ -static void +void in6_ifaddloop(struct ifaddr *ifa) { struct rtentry *rt; @@ -242,7 +242,7 @@ * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(), * if it exists. */ -static void +void in6_ifremloop(struct ifaddr *ifa) { struct in6_ifaddr *ia; Index: sys/netinet6/in6_proto.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_proto.c,v retrieving revision 1.61 diff -u -r1.61 in6_proto.c --- sys/netinet6/in6_proto.c 11 Dec 2005 12:25:02 -0000 1.61 +++ sys/netinet6/in6_proto.c 12 Jan 2006 06:15:14 -0000 @@ -68,6 +68,8 @@ #include "opt_ipsec.h" #include "opt_iso.h" +#include "carp.h" + #include #include #include @@ -115,6 +117,10 @@ #include #endif /* IPSEC */ +#if NCARP > 0 +#include +#endif + #include #include @@ -212,6 +218,13 @@ rip6_usrreq, encap_init, 0, 0, 0, }, +#if NCARP > 0 +{ SOCK_RAW, &inet6domain, IPPROTO_CARP, PR_ATOMIC|PR_ADDR, + carp6_proto_input, rip6_output, 0, rip6_ctloutput, + rip6_usrreq, + 0, 0, 0, 0, NULL +}, +#endif #ifdef ISO { SOCK_RAW, &inet6domain, IPPROTO_EON, PR_ATOMIC|PR_ADDR|PR_LASTHDR, encap6_input, rip6_output, encap6_ctlinput, rip6_ctloutput, Index: sys/netinet6/in6_var.h =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_var.h,v retrieving revision 1.40 diff -u -r1.40 in6_var.h --- sys/netinet6/in6_var.h 10 Dec 2005 23:39:56 -0000 1.40 +++ sys/netinet6/in6_var.h 12 Jan 2006 06:15:14 -0000 @@ -589,6 +589,8 @@ void *in6_domifattach __P((struct ifnet *)); void in6_domifdetach __P((struct ifnet *, void *)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); +void in6_ifremloop(struct ifaddr *); +void in6_ifaddloop(struct ifaddr *); void in6_createmkludge __P((struct ifnet *)); void in6_purgemkludge __P((struct ifnet *)); struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); Index: sys/sys/sockio.h =================================================================== RCS file: /cvsroot/src/sys/sys/sockio.h,v retrieving revision 1.23 diff -u -r1.23 sockio.h --- sys/sys/sockio.h 11 Dec 2005 12:25:21 -0000 1.23 +++ sys/sys/sockio.h 12 Jan 2006 06:15:15 -0000 @@ -124,4 +124,7 @@ #define SIOCGIFCAP _IOWR('i', 118, struct ifcapreq)/* get capabilities */ #define SIOCSIFCAP _IOW('i', 117, struct ifcapreq)/* set capabilities */ +#define SIOCSVH _IOWR('i', 245, struct ifreq) /* set carp param */ +#define SIOCGVH _IOWR('i', 246, struct ifreq) /* get carp param */ + #endif /* !_SYS_SOCKIO_H_ */