vBulletin Search Engine Optimization
| |||||||
| Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| Here is an updated version of the diff which fixes problems with 'make build' and 'make release'. - in ip_carp.h, define CARPDEVNAMSIZ instead of using IFNAMSIZ, so that random applications don't have to include if.h - get rid of carp_sendif(), store the pointer to carpdev directly in struct ifnet, so random kernel code doesn't have to include ip_carp.h - some files missing from the diff. - other misc. cleanup. As an aside, I've not recieved a single reports from a user about the above problems, so I have to imagine that nobody cares enough to test it! IF YOU USE CARP, YOU SHOULD TEST THIS CODE! On Mon, Dec 06, 2004 at 02:10:10AM +0000, Ryan McBride wrote: > Here's the long-promised diff which allows you to specify a physical > interface 'carpdev' which the carp interface will bind to, and that > interface does not have to have an IP address on the same subnet (or at > all, in fact). > > $ sudo ifconfig carp3 192.168.10.10 vhid 10 carpdev hme0 > > If you don't specify it, the attachment behaves as the current code, ie. > it figures out the correct carpdev on the address. This means that > existing configurations should work correctly with this diff. > > Pleas test this - it is important as it allows CARP to work on small > subnets or other situations with address shortage. I'm interested in > hearing reports of this code running on existing configurations as well > as the new functionality that it provides. Index: sys/net/if_ethersubr.c ================================================== ================= RCS file: /cvs/src/sys/net/if_ethersubr.c,v retrieving revision 1.81 diff -u -r1.81 if_ethersubr.c --- sys/net/if_ethersubr.c 28 Nov 2004 23:39:45 -0000 1.81 +++ sys/net/if_ethersubr.c 6 Dec 2004 20:35:59 -0000 @@ -238,8 +238,8 @@ * Assumes that ifp is actually pointer to arpcom structure. */ int -ether_output(ifp, m0, dst, rt0) - struct ifnet *ifp; +ether_output(ifp0, m0, dst, rt0) + struct ifnet *ifp0; struct mbuf *m0; struct sockaddr *dst; struct rtentry *rt0; @@ -251,8 +251,28 @@ struct rtentry *rt; struct mbuf *mcopy = (struct mbuf *)0; struct ether_header *eh; - struct arpcom *ac = (struct arpcom *)ifp; + struct arpcom *ac = (struct arpcom *)ifp0; short mflags; + struct ifnet *ifp = ifp0; + +#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; + + 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); @@ -513,12 +533,12 @@ #endif #if NCARP > 0 - if (ifp->if_carp) { - int error; - error = carp_output(ifp, m, dst, NULL); - if (error) - goto bad; - } + if (ifp->if_carp) { + int error; + error = carp_output(ifp0, m, dst, NULL); + if (error) + goto bad; + } #endif mflags = m->m_flags; @@ -535,6 +555,10 @@ return (error); } ifp->if_obytes += len + ETHER_HDR_LEN; +#if NCARP > 0 + if (ifp != ifp0) + ifp0->if_obytes += len + ETHER_HDR_LEN; +#endif /* NCARP > 0 */ if (mflags & M_MCAST) ifp->if_omcasts++; if ((ifp->if_flags & IFF_OACTIVE) == 0) @@ -650,9 +674,9 @@ #endif /* NVLAN > 0 */ #if NCARP > 0 - if (ifp->if_carp && - carp_forus(ifp->if_carp, eh->ether_dhost)) - goto decapsulate; + if (ifp->if_carp && ifp->if_type != IFT_CARP && + (carp_input(eh, m) == 0)) + return; #endif /* NCARP > 0 */ ac = (struct arpcom *)ifp; Index: sys/netinet/if_ether.c ================================================== ================= RCS file: /cvs/src/sys/netinet/if_ether.c,v retrieving revision 1.54 diff -u -r1.54 if_ether.c --- sys/netinet/if_ether.c 21 Jun 2004 23:50:37 -0000 1.54 +++ sys/netinet/if_ether.c 6 Dec 2004 21:03:45 -0000 @@ -557,7 +557,7 @@ #endif #if NCARP > 0 - if (ac->ac_if.if_carp) { + if (ac->ac_if.if_carp && ac->ac_if.if_type != IFT_CARP) { if (carp_iamatch(ac->ac_if.if_carp, ia, &isaddr, &enaddr)) break; @@ -645,7 +645,11 @@ } } } else if (rt->rt_ifp != &ac->ac_if && !(ac->ac_if.if_bridge && - (rt->rt_ifp->if_bridge == ac->ac_if.if_bridge))) { + (rt->rt_ifp->if_bridge == ac->ac_if.if_bridge)) && + !(rt->rt_ifp->if_type == IFT_CARP && + rt->rt_ifp->if_carpdev == &ac->ac_if) && + !(ac->ac_if.if_type == IFT_CARP && + ac->ac_if.if_carpdev == rt->rt_ifp)) { log(LOG_WARNING, "arp: attempt to add entry for %s " "on %s by %s on %s\n", Index: sys/netinet/ip_carp.c ================================================== ================= RCS file: /cvs/src/sys/netinet/ip_carp.c,v retrieving revision 1.72 diff -u -r1.72 ip_carp.c --- sys/netinet/ip_carp.c 30 Nov 2004 00:17:18 -0000 1.72 +++ sys/netinet/ip_carp.c 6 Dec 2004 23:17:38 -0000 @@ -29,7 +29,6 @@ /* * TODO: * - iface reconfigure - * - track iface ip address changes; * - support for hardware checksum calculations; * */ @@ -41,6 +40,7 @@ #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/socket.h> +#include <sys/socketvar.h> #include <sys/ioctl.h> #include <sys/errno.h> #include <sys/device.h> @@ -92,10 +92,19 @@ #include <netinet/ip_carp.h> +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 arpcom sc_ac; +#define sc_carpdev sc_ac.ac_if.if_carpdev int if_flags; /* current flags to treat UP/DOWN */ - struct ifnet *sc_ifp; struct in_ifaddr *sc_ia; /* primary iface address */ struct ip_moptions sc_imo; #ifdef INET6 @@ -132,6 +141,7 @@ struct timeout sc_md_tmo; /* master down timeout */ struct timeout sc_md6_tmo; /* master down timeout */ + LIST_HEAD(__carp_mchead, carp_mc_entry) carp_mc_listhead; }; int carp_suppress_preempt = 0; @@ -161,7 +171,7 @@ int carp_hmac_verify(struct carp_softc *, u_int32_t *, unsigned char *); void carp_setroute(struct carp_softc *, int); -void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); +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 *, @@ -177,15 +187,20 @@ 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 *); int carp_set_addr(struct carp_softc *, struct sockaddr_in *); -int carp_del_addr(struct carp_softc *, struct sockaddr_in *); +int carp_join_multicast(struct carp_softc *, struct ifnet *); #ifdef INET6 void carp_send_na(struct carp_softc *); int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *); -int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *); +int carp_join_multicast6(struct carp_softc *, struct ifnet *); #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); @@ -279,25 +294,91 @@ struct ifaddr *ifa; int s; - s = splnet(); + s = splsoftnet(); TAILQ_FOREACH(ifa, &sc->sc_ac.ac_if.if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family == AF_INET && sc->sc_ifp != NULL) { - int count = carp_addrcount( - (struct carp_if *)sc->sc_ifp->if_carp, - ifatoia(ifa), CARP_COUNT_MASTER); - - if ((cmd == RTM_ADD && count == 1) || - (cmd == RTM_DELETE && count == 0)) - rtinit(ifa, cmd, RTF_UP | RTF_HOST); + 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; + + /* + * 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 a route on a physical interface */ + rn = rnh->rnh_matchaddr(ifa->ifa_addr, rnh); + rt = (struct rtentry *)rn; + + switch (cmd) { + case RTM_ADD: + if (rt && rt->rt_ifp != &sc->sc_ac.ac_if && + + rt->rt_flags & (RTF_CLONING|RTF_CLONED)) { + rtrequest(RTM_ADD, ifa->ifa_addr, + ifa->ifa_addr, ifa->ifa_netmask, + RTF_UP | RTF_HOST, NULL); + } else { + ifa->ifa_rtrequest = arp_rtrequest; + ifa->ifa_flags |= RTF_CLONING; + + rtrequest(RTM_ADD, ifa->ifa_addr, + ifa->ifa_addr, ifa->ifa_netmask, + 0, NULL); + } + break; + case RTM_DELETE: + ifa->ifa_rtrequest = NULL; + ifa->ifa_flags &= ~RTF_CLONING; + + if (!(rt && rt->rt_ifp != &sc->sc_ac.ac_if && + rt->rt_flags & (RTF_CLONING|RTF_CLONED))) { + 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; + + rtrequest(cmd, &sa, ifa->ifa_addr, + ifa->ifa_netmask, 0, NULL); + } + break; + default: + break; + } + break; } + #ifdef INET6 - if (ifa->ifa_addr->sa_family == AF_INET6) { + case AF_INET6: if (cmd == RTM_ADD) in6_ifaddloop(ifa); else in6_ifremloop(ifa); - } + break; #endif /* INET6 */ + default: + break; + } } splx(s); } @@ -308,7 +389,7 @@ * but it seems more efficient this way or not possible otherwise. */ void -carp_input(struct mbuf *m, ...) +carp_proto_input(struct mbuf *m, ...) { struct ip *ip = mtod(m, struct ip *); struct carp_softc *sc = NULL; @@ -328,7 +409,7 @@ } /* check if received on a valid carp interface */ - if (m->m_pkthdr.rcvif->if_carp == NULL) { + 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)); @@ -396,12 +477,12 @@ } m->m_data -= iplen; - carp_input_c(m, ch, AF_INET); + carp_proto_input_c(m, ch, AF_INET); } #ifdef INET6 int -carp6_input(struct mbuf **mp, int *offp, int proto) +carp6_proto_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp; struct carp_softc *sc = NULL; @@ -417,7 +498,7 @@ } /* check if received on a valid carp interface */ - if (m->m_pkthdr.rcvif->if_carp == NULL) { + 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)); @@ -456,23 +537,23 @@ } m->m_data -= *offp; - carp_input_c(m, ch, AF_INET6); + carp_proto_input_c(m, ch, AF_INET6); return (IPPROTO_DONE); } #endif /* INET6 */ void -carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) +carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) { struct carp_softc *sc; - struct ifnet *ifp = m->m_pkthdr.rcvif; u_int64_t tmp_counter; struct timeval sc_tv, ch_tv; - /* verify that the VHID is valid on the receiving interface */ - TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list) + 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_ac.ac_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { carpstats.carps_badvhid++; @@ -484,25 +565,6 @@ sc->sc_ac.ac_if.if_ipackets++; sc->sc_ac.ac_if.if_ibytes += m->m_pkthdr.len; -#if NBPFILTER > 0 - if (sc->sc_ac.ac_if.if_bpf) { - /* - * We need to prepend the address family as - * a four byte field. Cons up a dummy header - * to pacify bpf. This is safe because bpf - * will only read from the mbuf (i.e., it won't - * try to free it or keep a pointer to it). - */ - struct mbuf m0; - u_int32_t af0 = htonl(af); - - m0.m_next = m; - m0.m_len = sizeof(af0); - m0.m_data = (char *)&af0; - bpf_mtap(sc->sc_ac.ac_if.if_bpf, &m0); - } -#endif - /* verify the CARP version. */ if (ch->carp_version != CARP_VERSION) { carpstats.carps_badver++; @@ -642,22 +704,28 @@ timeout_set(&sc->sc_md_tmo, carp_master_down, sc); timeout_set(&sc->sc_md6_tmo, carp_master_down, sc); + LIST_INIT(&sc->carp_mc_listhead); ifp = &sc->sc_ac.ac_if; ifp->if_softc = sc; snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name, unit); - ifp->if_mtu = ETHERMTU; - ifp->if_flags = 0; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = carp_ioctl; - ifp->if_output = looutput; + ifp->if_output = ether_output; ifp->if_start = carp_start; ifp->if_type = IFT_CARP; - ifp->if_snd.ifq_maxlen = ifqmaxlen; - ifp->if_hdrlen = 0; - if_attach(ifp); + 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_attachhead(ifp); + if_alloc_sadl(ifp); + bcopy(&sc->sc_ac.ac_enaddr, LLADDR(ifp->if_sadl), ifp->if_addrlen); + LIST_INIT(&sc->sc_ac.ac_multiaddrs); #if NBPFILTER > 0 - bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t)); + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN); #endif return (0); } @@ -667,44 +735,22 @@ { struct carp_softc *sc = ifp->if_softc; struct carp_if *cif; - struct ip_moptions *imo = &sc->sc_imo; -#ifdef INET6 - struct ip6_moptions *im6o = &sc->sc_im6o; -#endif timeout_del(&sc->sc_ad_tmo); timeout_del(&sc->sc_md_tmo); timeout_del(&sc->sc_md6_tmo); - if (sc->sc_ifp != NULL) { - cif = (struct carp_if *)sc->sc_ifp->if_carp; + if (sc->sc_carpdev != NULL) { + cif = (struct carp_if *)sc->sc_carpdev->if_carp; TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); if (cif->vhif_nvrs) { - ifpromisc(sc->sc_ifp, 0); - /* Clear IPv4 multicast */ - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); - imo->imo_multicast_ifp = NULL; - - /* Clear IPv6 multicast */ -#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); - } -#endif - - sc->sc_ifp->if_carp = NULL; + sc->sc_carpdev->if_carp = NULL; FREE(cif, M_IFADDR); } } -#if NBPFILTER > 0 - bpfdetach(ifp); -#endif + ether_ifdetach(ifp); if_detach(ifp); free(sc, M_DEVBUF); @@ -714,25 +760,27 @@ void carpdetach(struct carp_softc *sc) { - struct ifaddr *ifa; + struct carp_if *cif; timeout_del(&sc->sc_ad_tmo); timeout_del(&sc->sc_md_tmo); timeout_del(&sc->sc_md6_tmo); - while ((ifa = TAILQ_FIRST(&sc->sc_ac.ac_if.if_addrlist)) != NULL) - if (ifa->ifa_addr->sa_family == AF_INET) { - struct in_ifaddr *ia = ifatoia(ifa); - - carp_del_addr(sc, &ia->ia_addr); - - /* ripped screaming from in_control(SIOCDIFADDR) */ - in_ifscrub(&sc->sc_ac.ac_if, ia); - TAILQ_REMOVE(&sc->sc_ac.ac_if.if_addrlist, - ifa, ifa_list); - TAILQ_REMOVE(&in_ifaddr, ia, ia_list); - IFAFREE((&ia->ia_ifa)); + carp_set_state(sc, INIT); + sc->sc_ac.ac_if.if_flags &= ~IFF_UP; + carp_setrun(sc, 0); + carp_multicast_cleanup(sc); + + if (sc->sc_carpdev != NULL) { + 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; } /* Detach an interface from the carp. */ @@ -786,7 +834,7 @@ struct carp_softc *vh; TAILQ_FOREACH(ifp, &ifnet, if_list) { - if (ifp->if_carp == NULL) + if (ifp->if_carp == NULL || ifp->if_type == IFT_CARP) continue; cif = (struct carp_if *)ifp->if_carp; @@ -808,9 +856,16 @@ struct carp_header *ch_ptr; struct mbuf *m; int error, len, advbase, advskew, s; + struct ifaddr *ifa; + struct sockaddr sa; s = splsoftnet(); + if (sc->sc_carpdev == NULL) { + sc->sc_ac.ac_if.if_oerrors++; + goto retry_later; + } + /* bow out if we've lost our UPness or RUNNINGuiness */ if ((sc->sc_ac.ac_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { @@ -837,7 +892,7 @@ #ifdef INET - if (sc->sc_ia) { + if (sc->sc_naddrs) { struct ip *ip; MGETHDR(m, M_DONTWAIT, MT_HEADER); @@ -863,7 +918,15 @@ ip->ip_ttl = CARP_DFLTTL; ip->ip_p = IPPROTO_CARP; ip->ip_sum = 0; - ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr; + + 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 = (void *)ip + sizeof(*ip); @@ -909,7 +972,7 @@ } #endif /* INET */ #ifdef INET6 - if (sc->sc_ia6) { + if (sc->sc_naddrs6) { struct ip6_hdr *ip6; MGETHDR(m, M_DONTWAIT, MT_HEADER); @@ -930,8 +993,16 @@ ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_hlim = CARP_DFLTTL; ip6->ip6_nxt = IPPROTO_CARP; - bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src, - sizeof(struct in6_addr)); + + /* 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; @@ -1004,7 +1075,7 @@ continue; in = ifatoia(ifa)->ia_addr.sin_addr.s_addr; - arprequest(sc->sc_ifp, &in, &in, sc->sc_ac.ac_enaddr); + arprequest(sc->sc_carpdev, &in, &in, sc->sc_ac.ac_enaddr); DELAY(1000); /* XXX */ } splx(s); @@ -1025,7 +1096,7 @@ continue; in6 = &ifatoia6(ifa)->ia_addr.sin6_addr; - nd6_na_output(sc->sc_ifp, &mcast, in6, + nd6_na_output(sc->sc_carpdev, &mcast, in6, ND_NA_FLAG_OVERRIDE, 1, NULL); DELAY(1000); /* XXX */ } @@ -1178,23 +1249,67 @@ } #endif /* INET6 */ -struct ifnet * -carp_forus(void *v, void *dhost) +int +carp_input(struct ether_header *eh, struct mbuf *m) { - struct carp_if *cif = v; + struct carp_if *cif = (struct carp_if *)m->m_pkthdr.rcvif->if_carp; struct carp_softc *vh; - u_int8_t *ena = dhost; + u_int8_t *ena = (u_int8_t *)&eh->ether_dhost; + struct ifnet *ifp; + + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + struct mbuf *m0; + + /* + * XXX Should reall check the list of multicast addresses + * for each CARP interface _before_ copying. + */ + TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { + m0 = m_copym2(m, 0, M_COPYALL, M_DONTWAIT); + if (m0 == NULL) + continue; + m0->m_pkthdr.rcvif = &vh->sc_ac.ac_if; + ether_input(&vh->sc_ac.ac_if, eh, m0); + } + return (1); + } if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) - return (NULL); + return (1); TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) if ((vh->sc_ac.ac_if.if_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER && - !bcmp(dhost, vh->sc_ac.ac_enaddr, ETHER_ADDR_LEN)) - return (&vh->sc_ac.ac_if); + !bcmp(ena, vh->sc_ac.ac_enaddr, + ETHER_ADDR_LEN)) + break; - return (NULL); + if (vh == NULL) + return (1); + + m->m_pkthdr.rcvif = ifp = &vh->sc_ac.ac_if; + +#if NBPFILTER > 0 + if (ifp->if_bpf) { + /* + * Do the usual BPF fakery. Note that we don't support + * promiscuous mode here, since it would require the + * drivers to know about CARP and we're not ready for + * that yet. + */ + struct mbuf m0; + + m0.m_flags = 0; + m0.m_next = m; + m0.m_len = ETHER_HDR_LEN; + m0.m_data = (char *)eh; + bpf_mtap(ifp->if_bpf, &m0); + } +#endif + ifp->if_ipackets++; + ether_input(ifp, eh, m); + + return (0); } void @@ -1231,10 +1346,16 @@ { struct timeval tv; + if (sc->sc_carpdev == NULL) { + sc->sc_ac.ac_if.if_flags &= ~IFF_RUNNING; + carp_set_state(sc, INIT); + return; + } + if (sc->sc_ac.ac_if.if_flags & IFF_UP && - sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6)) + sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6)) { sc->sc_ac.ac_if.if_flags |= IFF_RUNNING; - else { + } else { sc->sc_ac.ac_if.if_flags &= ~IFF_RUNNING; carp_setroute(sc, RTM_DELETE); return; @@ -1287,15 +1408,120 @@ } } +void +carp_multicast_cleanup(struct carp_softc *sc) { + struct ip_moptions *imo = &sc->sc_imo; + struct ip6_moptions *im6o = &sc->sc_im6o; + + /* Clean up our own multicast memberships */ + while (imo->imo_num_memberships > 0) { + if (imo->imo_membership[--imo->imo_num_memberships] == NULL) + in_delmulti(imo->imo_membership[imo->imo_num_memberships]); + imo->imo_membership[imo->imo_num_memberships] = NULL; + } + imo->imo_multicast_ifp = NULL; + + 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; + + /* 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; + + if (ifp == sc->sc_carpdev) + return (0); + + if (ifp != NULL) { + if ((ifp->if_flags & IFF_MULTICAST) == 0) + return (EADDRNOTAVAIL); + + if (ifp->if_carp == NULL) { + MALLOC(ncif, struct carp_if *, sizeof(*cif), + M_IFADDR, M_WAITOK); + if (!ncif) + 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, ifp)) != 0) { + FREE(ncif, M_IFADDR); + return (error); + } + + if (sc->sc_naddrs6 < 0 && + (error = carp_join_multicast(sc, ifp)) != 0) { + FREE(ncif, M_IFADDR); + return (error); + } + + /* 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_ac.ac_if.if_flags |= IFF_UP; + } else { + carpdetach(sc); + sc->sc_ac.ac_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + } + return (0); + +} + int carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) { - struct ifnet *ifp; - struct carp_if *cif; + struct ifnet *ifp = sc->sc_carpdev; struct in_ifaddr *ia, *ia_if; - struct ip_moptions *imo = &sc->sc_imo; - struct in_addr addr; - int own, error; + int own, error = 0; if (sin->sin_addr.s_addr == 0) { if (!(sc->sc_ac.ac_if.if_flags & IFF_UP)) @@ -1308,7 +1534,8 @@ /* we have to do it by hands to check we won't match on us */ ia_if = NULL; own = 0; - for (ia = TAILQ_FIRST(&in_ifaddr); ia; ia = TAILQ_NEXT(ia, ia_list)) { + for (ia = TAILQ_FIRST(&in_ifaddr); ia; + ia = TAILQ_NEXT(ia, ia_list)) { /* and, yeah, we need a multicast-capable iface too */ if (ia->ia_ifp != &sc->sc_ac.ac_if && @@ -1322,127 +1549,66 @@ } } - if (!ia_if) - return (EADDRNOTAVAIL); - ia = ia_if; - ifp = ia->ia_ifp; - - if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || - (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) - return (EADDRNOTAVAIL); - - if (imo->imo_num_memberships == 0) { - addr.s_addr = INADDR_CARP_GROUP; - if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == NULL) - return (ENOBUFS); - imo->imo_num_memberships++; - imo->imo_multicast_ifp = ifp; - imo->imo_multicast_ttl = CARP_DFLTTL; - imo->imo_multicast_loop = 0; - } - - if (!ifp->if_carp) { - - MALLOC(cif, struct carp_if *, sizeof(*cif), M_IFADDR, M_WAITOK); - if (!cif) { - error = ENOBUFS; - goto cleanup; - } - if ((error = ifpromisc(ifp, 1))) { - FREE(cif, M_IFADDR); - goto cleanup; - } - - bzero(cif, sizeof(*cif)); - cif->vhif_ifp = ifp; - TAILQ_INIT(&cif->vhif_vrs); - ifp->if_carp = (caddr_t)cif; - - } else { - struct carp_softc *vr; + if (ia_if) { + ia = ia_if; + if (ifp) { + if (ifp != ia->ia_ifp) + return (EADDRNOTAVAIL); + } else { + ifp = ia->ia_ifp; + } + } - cif = (struct carp_if *)ifp->if_carp; - TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) - if (vr != sc && vr->sc_vhid == sc->sc_vhid) { - error = EINVAL; - goto cleanup; - } - } - sc->sc_ia = ia; - sc->sc_ifp = ifp; + if ((error = carp_set_ifp(sc, ifp))) + return (error); - { /* XXX prevent endless loop if already in queue */ - struct carp_softc *vr, *after = NULL; - int myself = 0; - 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 (sc->sc_carpdev == NULL) + return (EADDRNOTAVAIL); - 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 == 0 && (error = carp_join_multicast(sc, ifp)) != 0) + return (error); sc->sc_naddrs++; - sc->sc_ac.ac_if.if_flags |= IFF_UP; + if (sc->sc_carpdev != NULL) + sc->sc_ac.ac_if.if_flags |= IFF_UP; + if (own) sc->sc_advskew = 0; carp_set_state(sc, INIT); carp_setrun(sc, 0); return (0); - -cleanup: - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); - return (error); } int -carp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin) +carp_join_multicast(struct carp_softc *sc, struct ifnet *ifp) { - int error = 0; - - if (!--sc->sc_naddrs) { - struct carp_if *cif = (struct carp_if *)sc->sc_ifp->if_carp; - struct ip_moptions *imo = &sc->sc_imo; + struct ip_moptions *imo = &sc->sc_imo, tmpimo; + struct in_addr addr; - timeout_del(&sc->sc_ad_tmo); - sc->sc_ac.ac_if.if_flags &= ~(IFF_UP|IFF_RUNNING); - sc->sc_vhid = -1; - in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); - imo->imo_multicast_ifp = NULL; - TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); - if (!--cif->vhif_nvrs) { - sc->sc_ifp->if_carp = NULL; - FREE(cif, M_IFADDR); - } + bzero(&tmpimo, sizeof(tmpimo)); + addr.s_addr = INADDR_CARP_GROUP; + if ((tmpimo.imo_membership[0] = + in_addmulti(&addr, &sc->sc_ac.ac_if)) == NULL) { + return (ENOBUFS); } - return (error); + imo->imo_membership[0] = tmpimo.imo_membership[0]; + imo->imo_num_memberships = 1; + imo->imo_multicast_ifp = &sc->sc_ac.ac_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; - struct carp_if *cif; + struct ifnet *ifp = sc->sc_carpdev; struct in6_ifaddr *ia, *ia_if; - struct ip6_moptions *im6o = &sc->sc_im6o; - struct in6_multi_mship *imm; - struct sockaddr_in6 addr; - int own, error; + int own, error = 0; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { if (!(sc->sc_ac.ac_if.if_flags & IFF_UP)) @@ -1477,143 +1643,84 @@ } } - if (!ia_if) - return (EADDRNOTAVAIL); - ia = ia_if; - ifp = ia->ia_ifp; - - if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 || - (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) - return (EADDRNOTAVAIL); - - if (!sc->sc_naddrs6) { - im6o->im6o_multicast_ifp = ifp; - - /* join CARP multicast address */ - bzero(&addr, sizeof(addr)); - addr.sin6_family = AF_INET6; - addr.sin6_len = sizeof(addr); - addr.sin6_addr.s6_addr16[0] = htons(0xff02); - addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - addr.sin6_addr.s6_addr8[15] = 0x12; - if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL) - goto cleanup; - LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); - - /* join solicited multicast address */ - bzero(&addr.sin6_addr, sizeof(addr.sin6_addr)); - addr.sin6_addr.s6_addr16[0] = htons(0xff02); - addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - addr.sin6_addr.s6_addr32[1] = 0; - addr.sin6_addr.s6_addr32[2] = htonl(1); - addr.sin6_addr.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; - addr.sin6_addr.s6_addr8[12] = 0xff; - if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL) - goto cleanup; - LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); - } - - if (!ifp->if_carp) { - MALLOC(cif, struct carp_if *, sizeof(*cif), M_IFADDR, M_WAITOK); - if (!cif) { - error = ENOBUFS; - goto cleanup; - } - if ((error = ifpromisc(ifp, 1))) { - FREE(cif, M_IFADDR); - goto cleanup; + if (ia_if) { + ia = ia_if; + if (sc->sc_carpdev) { + if (sc->sc_carpdev != ia->ia_ifp) + return (EADDRNOTAVAIL); + } else { + ifp = ia->ia_ifp; } - - bzero(cif, sizeof(*cif)); - cif->vhif_ifp = ifp; - TAILQ_INIT(&cif->vhif_vrs); - ifp->if_carp = (caddr_t)cif; - - } else { - struct carp_softc *vr; - - cif = (struct carp_if *)ifp->if_carp; - TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) - if (vr != sc && vr->sc_vhid == sc->sc_vhid) { - error = EINVAL; - goto cleanup; - } } - sc->sc_ia6 = ia; - sc->sc_ifp = ifp; - { /* XXX prevent endless loop if already in queue */ - struct carp_softc *vr, *after = NULL; - int myself = 0; - cif = (struct carp_if *)ifp->if_carp; + if ((error = carp_set_ifp(sc, ifp))) + return (error); - TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) { - if (vr == sc) - myself = 1; - if (vr->sc_vhid < sc->sc_vhid) - after = vr; - } + if (sc->sc_carpdev == NULL) + return (EADDRNOTAVAIL); - 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_naddrs6 == 0 && (error = carp_join_multicast6(sc, ifp)) != 0) + return (error); sc->sc_naddrs6++; - sc->sc_ac.ac_if.if_flags |= IFF_UP; - if (own) - sc->sc_advskew = 0; + if (sc->sc_carpdev != NULL) + sc->sc_ac.ac_if.if_flags |= IFF_UP; carp_set_state(sc, INIT); carp_setrun(sc, 0); return (0); - -cleanup: - /* clean up multicast memberships */ - if (!sc->sc_naddrs6) { - while (!LIST_EMPTY(&im6o->im6o_memberships)) { - imm = LIST_FIRST(&im6o->im6o_memberships); - LIST_REMOVE(imm, i6mm_chain); - in6_leavegroup(imm); - } - } - return (error); } int -carp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) +carp_join_multicast6(struct carp_softc *sc, struct ifnet *ifp) { - int error = 0; - - if (!--sc->sc_naddrs6) { - struct carp_if *cif = (struct carp_if *)sc->sc_ifp->if_carp; - struct ip6_moptions *im6o = &sc->sc_im6o; + struct in6_multi_mship *imm, *imm2; + struct ip6_moptions *im6o = &sc->sc_im6o; + struct sockaddr_in6 addr6; + int error; + + /* + * For IPv6, we can attach to the physical interface, as + * there will be a link-local address there. + * This way we don't need a link-local address on the + * CARP interface. + */ - timeout_del(&sc->sc_ad_tmo); - sc->sc_ac.ac_if.if_flags &= ~(IFF_UP|IFF_RUNNING); - sc->sc_vhid = -1; - while (!LIST_EMPTY(&im6o->im6o_memberships)) { - struct in6_multi_mship *imm = - LIST_FIRST(&im6o->im6o_memberships); + /* 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(ifp->if_index); + addr6.sin6_addr.s6_addr8[15] = 0x12; + if ((imm = in6_joingroup(ifp, + &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(ifp->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(ifp, + &addr6.sin6_addr, &error)) == NULL) { + in6_leavegroup(imm); + return (error); + } + + /* apply v6 multicast membership */ + im6o->im6o_multicast_ifp = sc->sc_carpdev; + if (imm) + LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, + i6mm_chain); + if (imm2) + LIST_INSERT_HEAD(&im6o->im6o_memberships, imm2, + i6mm_chain); - LIST_REMOVE(imm, i6mm_chain); - in6_leavegroup(imm); - } - im6o->im6o_multicast_ifp = NULL; - TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list); - if (!--cif->vhif_nvrs) { - sc->sc_ifp->if_carp = NULL; - FREE(cif, M_IFADDR); - } - } - - return (error); + return (0); } #endif /* INET6 */ @@ -1627,6 +1734,7 @@ struct ifaddr *ifa; struct ifreq *ifr; struct ifaliasreq *ifra; + struct ifnet *cdev = NULL; int error = 0; ifa = (struct ifaddr *)addr; @@ -1683,12 +1791,12 @@ switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: - error = carp_del_addr(sc, satosin(&ifra->ifra_addr)); + sc->sc_naddrs--; break; #endif /* INET */ #ifdef INET6 case AF_INET6: - error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr)); + sc->sc_naddrs6--; break; #endif /* INET6 */ default: @@ -1719,6 +1827,11 @@ 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: @@ -1739,9 +1852,9 @@ error = EINVAL; break; } - if (sc->sc_ifp) { + if (sc->sc_carpdev) { struct carp_if *cif; - cif = (struct carp_if *)sc->sc_ifp->if_carp; + 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) @@ -1754,6 +1867,8 @@ sc->sc_ac.ac_enaddr[3] = 0; sc->sc_ac.ac_enaddr[4] = 1; sc->sc_ac.ac_enaddr[5] = sc->sc_vhid; + bcopy(&sc->sc_ac.ac_enaddr, + LLADDR(ifp->if_sadl), ifp->if_addrlen); error--; } if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) { @@ -1780,6 +1895,9 @@ 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; @@ -1790,6 +1908,14 @@ 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; } @@ -1841,7 +1967,7 @@ sc = carp_ifp->if_softc; /* Set the source MAC address to Virtual Router MAC Address */ - switch (ifp->if_type) { + switch (sc->sc_carpdev->if_type) { #if NETHER > 0 case IFT_ETHER: { struct ether_header *eh; @@ -1886,7 +2012,7 @@ #endif default: printf("%s: carp is not supported for this interface type\n", - ifp->if_xname); + sc->sc_carpdev->if_xname); return (EOPNOTSUPP); } @@ -1921,8 +2047,8 @@ struct carp_softc *sc; TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { - if (sc->sc_ifp->if_link_state == LINK_STATE_DOWN || - !(sc->sc_ifp->if_flags & IFF_UP)) { + if (sc->sc_carpdev->if_link_state == LINK_STATE_DOWN || + !(sc->sc_carpdev->if_flags & IFF_UP)) { sc->sc_flags_backup = sc->sc_ac.ac_if.if_flags; sc->sc_ac.ac_if.if_flags &= ~(IFF_UP|IFF_RUNNING); timeout_del(&sc->sc_ad_tmo); @@ -1944,5 +2070,131 @@ carp_suppress_preempt--; sc->sc_suppress = 0; } + } +} + + +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, (struct arpcom *)&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, (struct arpcom *)&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); + + error = ether_delmulti(ifr, (struct arpcom *)&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. */ + for (mc = LIST_FIRST(&sc->carp_mc_listhead); mc != NULL; + mc = LIST_NEXT(mc, mc_entries)) { + if (mc->mc_enm == enm) { + LIST_REMOVE(mc, mc_entries); + FREE(mc, M_DEVBUF); + break; + } + } + KASSERT(mc != NULL); + } else + (void)ether_addmulti(ifr, (struct arpcom *)&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; + } ifreq; + struct ifreq *ifr = &ifreq.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); } } Index: sys/netinet/ip_carp.h ================================================== ================= RCS file: /cvs/src/sys/netinet/ip_carp.h,v retrieving revision 1.8 diff -u -r1.8 ip_carp.h --- sys/netinet/ip_carp.h 29 Jul 2004 22:12:15 -0000 1.8 +++ sys/netinet/ip_carp.h 6 Dec 2004 20:41:12 -0000 @@ -109,6 +109,13 @@ u_int64_t carps_preempt; /* if enabled, preemptions */ }; +#define CARPDEVNAMSIZ 16 +#ifdef IFNAMSIZ +#if CARPDEVNAMSIZ != IFNAMSIZ +#error +#endif +#endif + /* * Configuration structure for SIOCSVH SIOCGVH */ @@ -116,6 +123,8 @@ 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; @@ -143,15 +152,15 @@ #ifdef _KERNEL void carp_ifdetach (struct ifnet *); -void carp_input (struct mbuf *, ...); +void carp_proto_input (struct mbuf *, ...); void carp_carpdev_state(void *); -int carp6_input (struct mbuf **, int *, int); -int carp_output (struct ifnet *, struct mbuf *, struct sockaddr *, +int carp6_proto_input(struct mbuf **, int *, int); +int carp_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); -int carp_iamatch (void *, struct in_ifaddr *, struct in_addr *, +int carp_iamatch(void *, struct in_ifaddr *, struct in_addr *, u_int8_t **); struct ifaddr *carp_iamatch6(void *, struct in6_addr *); void *carp_macmatch6(void *, struct mbuf *, struct in6_addr *); -struct ifnet *carp_forus (void *, void *); -int carp_sysctl (int *, u_int, void *, size_t *, void *, size_t); +int carp_input(struct ether_header *, struct mbuf *); +int carp_sysctl(int *, u_int, void *, size_t *, void *, size_t); #endif Index: sys/netinet/in_proto.c ================================================== ================= RCS file: /cvs/src/sys/netinet/in_proto.c,v retrieving revision 1.41 diff -u -r1.41 in_proto.c --- sys/netinet/in_proto.c 17 Sep 2004 11:32:53 -0000 1.41 +++ sys/netinet/in_proto.c 20 Nov 2004 17:59:21 -0000 @@ -292,7 +292,7 @@ #endif /* NGRE > 0 */ #if NCARP > 0 { SOCK_RAW, &inetdomain, IPPROTO_CARP, PR_ATOMIC|PR_ADDR, - carp_input, rip_output, 0, rip_ctloutput, + carp_proto_input, rip_output, 0, rip_ctloutput, rip_usrreq, 0, 0, 0, 0, carp_sysctl }, Index: sys/netinet6/in6_proto.c ================================================== ================= RCS file: /cvs/src/sys/netinet6/in6_proto.c,v retrieving revision 1.45 diff -u -r1.45 in6_proto.c --- sys/netinet6/in6_proto.c 18 Oct 2004 03:59:33 -0000 1.45 +++ sys/netinet6/in6_proto.c 20 Nov 2004 17:59:21 -0000 @@ -220,7 +220,7 @@ }, #if NCARP > 0 { SOCK_RAW, &inet6domain, IPPROTO_CARP, PR_ATOMIC|PR_ADDR, - carp6_input, rip6_output, 0, rip6_ctloutput, + carp6_proto_input, rip6_output, 0, rip6_ctloutput, rip6_usrreq, 0, 0, 0, 0, carp_sysctl }, Index: sys/netinet6/in6_ifattach.c ================================================== ================= RCS file: /cvs/src/sys/netinet6/in6_ifattach.c,v retrieving revision 1.36 diff -u -r1.36 in6_ifattach.c --- sys/netinet6/in6_ifattach.c 7 May 2004 14:42:27 -0000 1.36 +++ sys/netinet6/in6_ifattach.c 20 Nov 2004 17:59:21 -0000 @@ -577,6 +577,7 @@ case IFT_ENC: case IFT_PFLOG: case IFT_PFSYNC: + case IFT_CARP: return; case IFT_PROPVIRTUAL: if (strncmp("bridge", ifp->if_xname, sizeof("bridge")) == 0 && Index: sbin/ifconfig/ifconfig.c ================================================== ================= RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v retrieving revision 1.121 diff -u -r1.121 ifconfig.c --- sbin/ifconfig/ifconfig.c 1 Dec 2004 15:57:44 -0000 1.121 +++ sbin/ifconfig/ifconfig.c 6 Dec 2004 20:07:53 -0000 @@ -203,6 +203,8 @@ 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); void setpfsync_syncif(const char *, int); void setpfsync_maxupd(const char *, int); void unsetpfsync_syncif(const char *, int); @@ -304,6 +306,8 @@ { "pass", NEXTARG, 0, setcarp_passwd }, { "vhid", NEXTARG, 0, setcarp_vhid }, { "state", NEXTARG, 0, setcarp_state }, + { "carpdev", NEXTARG, 0, setcarpdev }, + { "-carpdev", 1, 0, unsetcarpdev }, { "syncif", NEXTARG, 0, setpfsync_syncif }, { "-syncif", 1, 0, unsetpfsync_syncif }, { "syncpeer", NEXTARG, 0, setpfsync_syncpeer }, @@ -2875,9 +2879,10 @@ else state = carp_states[carpr.carpr_state]; - printf("\tcarp: %s vhid %d advbase %d advskew %d\n", - state, carpr.carpr_vhid, carpr.carpr_advbase, - carpr.carpr_advskew); + 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); } } @@ -2997,6 +3002,40 @@ } /* 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(1, "SIOCGVH"); + + strlcpy(carpr.carpr_carpdev, val, sizeof(carpr.carpr_carpdev)); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "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(1, "SIOCGVH"); + + bzero((char *)&carpr.carpr_carpdev, sizeof(carpr.carpr_carpdev)); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); +} + void setpfsync_syncif(const char *val, int d) { Index: sys/net/if.c ================================================== ================= RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.95 diff -u -r1.95 if.c --- sys/net/if.c 4 Dec 2004 16:16:45 -0000 1.95 +++ sys/net/if.c 5 Dec 2004 03:31:36 -0000 @@ -79,6 +79,7 @@ #include <net/if.h> #include <net/if_dl.h> +#include <net/if_types.h> #include <net/route.h> #ifdef INET @@ -485,7 +486,7 @@ #if NCARP > 0 /* Remove the interface from any carp group it is a part of. */ - if (ifp->if_carp) + if (ifp->if_carp && ifp->if_type != IFT_CARP) carp_ifdetach(ifp); #endif Index: sys/net/if.h ================================================== ================= RCS file: /cvs/src/sys/net/if.h,v retrieving revision 1.60 diff -u -r1.60 if.h --- sys/net/if.h 3 Dec 2004 17:31:03 -0000 1.60 +++ sys/net/if.h 6 Dec 2004 21:20:53 -0000 @@ -180,7 +180,12 @@ int if_pcount; /* number of promiscuous listeners */ caddr_t if_bpf; /* packet filter structure */ caddr_t if_bridge; /* bridge structure */ - caddr_t if_carp; /* carp structure */ + 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 u_short if_index; /* numeric abbreviation for this if */ short if_timer; /* time 'til if_watchdog called */ short if_flags; /* up/down, broadcast, etc. */ |