[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

CARP without address on the physical interface



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	29 Nov 2004 18:37:10 -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 = carp_sendif(ifp);
+		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	20 Nov 2004 17:59:21 -0000
@@ -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 &&
+		    carp_sendif(rt->rt_ifp) == &ac->ac_if) &&
+		    !(ac->ac_if.if_type == IFT_CARP &&
+		    carp_sendif(&ac->ac_if) == 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	1 Dec 2004 20:22:50 -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,6 +92,15 @@
 
 #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;
 	int if_flags;			/* current flags to treat UP/DOWN */
@@ -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,90 @@
 	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_ifp != NULL && sc->sc_ifp->if_carp != NULL) {
+				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))
+					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 +388,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 +408,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 +476,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;
@@ -456,23 +536,29 @@
 	}
 	m->m_data -= *offp;
 
-	carp_input_c(m, ch, AF_INET6);
+	carp_proto_input_c(m, ch, AF_INET6);
 	return (IPPROTO_DONE);
 }
 #endif /* INET6 */
 
+struct ifnet *
+carp_sendif(struct ifnet *ifp)
+{
+	return (((struct carp_softc *)ifp)->sc_ifp);
+}
+
 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_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 +570,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 +709,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,10 +740,6 @@
 {
 	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);
@@ -680,31 +749,13 @@
 		cif = (struct carp_if *)sc->sc_ifp->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;
 			FREE(cif, M_IFADDR);
 		}
 	}
 
-#if NBPFILTER > 0
-	bpfdetach(ifp);
-#endif
+	ether_ifdetach(ifp);
 	if_detach(ifp);
 	free(sc, M_DEVBUF);
 
@@ -714,25 +765,28 @@
 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_ifp != NULL) {
+		cif = (struct carp_if *)sc->sc_ifp->if_carp;
+		TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
+		if (!--cif->vhif_nvrs) {
+			ifpromisc(sc->sc_ifp, 0);
+			sc->sc_ifp->if_carp = NULL;
+			FREE(cif, M_IFADDR);
 		}
+	}
+	sc->sc_ifp = NULL;
+	sc->sc_ac.ac_if.if_carp = NULL;
 }
 
 /* Detach an interface from the carp.  */
@@ -808,9 +862,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_ifp == 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 +898,7 @@
 
 
 #ifdef INET
-	if (sc->sc_ia) {
+	if (sc->sc_naddrs) {
 		struct ip *ip;
 
 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
@@ -863,7 +924,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_ifp);
+                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 +978,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 +999,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_ifp);
+                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;
@@ -1178,23 +1255,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 +1352,16 @@
 {
 	struct timeval tv;
 
+	if (sc->sc_ifp == 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 +1414,121 @@
 	}
 }
 
+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_ifp)
+		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_ifp != 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_ac.ac_if.if_carp = ifp->if_carp;
+		sc->sc_ifp = 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_ifp;
 	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 +1541,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 +1556,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;
-
-		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 (ia_if) {
+		ia = ia_if;
+		if (ifp) {
+			if (ifp != ia->ia_ifp)
+				return (EADDRNOTAVAIL);
+		} else {
+			ifp = ia->ia_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_ifp == 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_ifp != 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_ifp;
 	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 +1650,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_ifp) {
+			if (sc->sc_ifp != 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_ifp == 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_ifp != 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;
-
-		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);
+	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.
+	 */
 
-			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);
-		}
-	}
+	/* 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_ifp;
+	if (imm)
+		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm,
+		    i6mm_chain);
+	if (imm2)
+		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm2,
+		    i6mm_chain);
 
-	return (error);
+	return (0);
 }
 
 #endif /* INET6 */
@@ -1627,6 +1741,7 @@
 	struct ifaddr *ifa;
 	struct ifreq *ifr;
 	struct ifaliasreq *ifra;
+	struct ifnet *cdev = NULL;
 	int error = 0;
 
 	ifa = (struct ifaddr *)addr;
@@ -1683,12 +1798,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 +1834,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:
@@ -1754,6 +1874,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 +1902,9 @@
 
 	case SIOCGVH:
 		bzero(&carpr, sizeof(carpr));
+		if (sc->sc_ifp != NULL)
+			strlcpy(carpr.carpr_carpdev, sc->sc_ifp->if_xname,
+			    IFNAMSIZ);
 		carpr.carpr_state = sc->sc_state;
 		carpr.carpr_vhid = sc->sc_vhid;
 		carpr.carpr_advbase = sc->sc_advbase;
@@ -1790,6 +1915,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 +1974,7 @@
 	sc = carp_ifp->if_softc;
 
 	/* Set the source MAC address to Virtual Router MAC Address */
-	switch (ifp->if_type) {
+	switch (sc->sc_ifp->if_type) {
 #if NETHER > 0
 	case IFT_ETHER: {
 			struct ether_header *eh;
@@ -1886,7 +2019,7 @@
 #endif
 	default:
 		printf("%s: carp is not supported for this interface type\n",
-		    ifp->if_xname);
+		    sc->sc_ifp->if_xname);
 		return (EOPNOTSUPP);
 	}
 
@@ -1944,5 +2077,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_ifp;
+	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_ifp;
+	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_ifp;		/* 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	20 Nov 2004 17:59:21 -0000
@@ -116,6 +116,7 @@
 	int		carpr_state;
 #define	CARP_STATES	"INIT", "BACKUP", "MASTER"
 #define	CARP_MAXSTATE	2
+	char		carpr_carpdev[IFNAMSIZ];
 	int		carpr_vhid;
 	int		carpr_advskew;
 	int		carpr_advbase;
@@ -143,15 +144,16 @@
 
 #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);
+struct ifnet	*carp_sendif(struct ifnet *);
+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	4 Dec 2004 07:28:03 -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)
 {



Visit your host, monkey.org