Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 9 Dec 2007 18:35:25 +0100
From:      Max Laier <max@love2party.net>
To:        freebsd-pf@freebsd.org
Subject:   Re: carpdev ...
Message-ID:  <200712091835.33608.max@love2party.net>
In-Reply-To: <200712051432.29703.max@love2party.net>
References:  <200710272311.09059.max@love2party.net> <8e10486b0712041257p6a54c50by4c340bba9c4a39b3@mail.gmail.com> <200712051432.29703.max@love2party.net>

next in thread | previous in thread | raw e-mail | index | archive | help
--nextPart1795943.NKanXysAve
Content-Type: multipart/mixed;
  boundary="Boundary-01=_gdCXHstQkL2YQhf"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--Boundary-01=_gdCXHstQkL2YQhf
Content-Type: text/plain;
  charset="iso-8859-6"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

All,

the arp error print turned out to be a really stupid error - as suspected. =
=20
The attached should now be all quite and works for me in every possible=20
IPv4 scenario I could think up (mixed environment with OpenBSD and=20
unpatched FreeBSD tested, too).

All is still IPv4 only!!!  But I'll see to remedy this last problem next=20
week.

I'd be very, very eager to hear test reports now!  This is still use with=20
care etc. but you can easily patch your BACKUP for testing.  The patch is=20
relative to HEAD and should apply to RELENG_7 ... I'll see what RELENG_6=20
does in a few days ... unless somebody beats me to it.

Please report in case of failure *and* success!  Thanks.

This work is sponsored by pil.dk

=2D-=20
/"\  Best regards,                      | mlaier@freebsd.org
\ /  Max Laier                          | ICQ #67774661
 X   http://pf4freebsd.love2party.net/  | mlaier@EFnet
/ \  ASCII Ribbon Campaign              | Against HTML Mail and News

--Boundary-01=_gdCXHstQkL2YQhf
Content-Type: text/plain;
  charset="iso-8859-6";
  name="carpdev.BETA2.diff"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="carpdev.BETA2.diff"

diff --git a/sbin/ifconfig/ifcarp.c b/sbin/ifconfig/ifcarp.c
index 9c961b7..82dbd50 100644
=2D-- a/sbin/ifconfig/ifcarp.c
+++ b/sbin/ifconfig/ifcarp.c
@@ -52,13 +52,7 @@
=20
 static const char *carp_states[] =3D { CARP_STATES };
=20
=2Dvoid carp_status(int s);
=2Dvoid setcarp_advbase(const char *,int, int, const struct afswtch *rafp);
=2Dvoid setcarp_advskew(const char *, int, int, const struct afswtch *rafp);
=2Dvoid setcarp_passwd(const char *, int, int, const struct afswtch *rafp);
=2Dvoid setcarp_vhid(const char *, int, int, const struct afswtch *rafp);
=2D
=2Dvoid
+static void
 carp_status(int s)
 {
 	const char *state;
@@ -76,17 +70,17 @@ carp_status(int s)
 		else
 			state =3D carp_states[carpr.carpr_state];
=20
=2D		printf("\tcarp: %s vhid %d advbase %d advskew %d\n",
=2D		    state, carpr.carpr_vhid, carpr.carpr_advbase,
=2D		    carpr.carpr_advskew);
+		printf("\tcarp: %s carpdev %s vhid %d advbase %d advskew %d\n",
+		    state, carpr.carpr_carpdev, carpr.carpr_vhid,
+		    carpr.carpr_advbase, carpr.carpr_advskew);
 	}
=20
 	return;
=20
 }
=20
=2Dvoid
=2Dsetcarp_passwd(const char *val, int d, int s, const struct afswtch *afp)
+static
+DECL_CMD_FUNC(setcarp_passwd, val, d)
 {
 	struct carpreq carpr;
=20
@@ -105,8 +99,8 @@ setcarp_passwd(const char *val, int d, int s, const stru=
ct afswtch *afp)
 	return;
 }
=20
=2Dvoid
=2Dsetcarp_vhid(const char *val, int d, int s, const struct afswtch *afp)
+static
+DECL_CMD_FUNC(setcarp_vhid, val, d)
 {
 	int vhid;
 	struct carpreq carpr;
@@ -130,8 +124,8 @@ setcarp_vhid(const char *val, int d, int s, const struc=
t afswtch *afp)
 	return;
 }
=20
=2Dvoid
=2Dsetcarp_advskew(const char *val, int d, int s, const struct afswtch *afp)
+static
+DECL_CMD_FUNC(setcarp_advskew, val, d)
 {
 	int advskew;
 	struct carpreq carpr;
@@ -152,8 +146,8 @@ setcarp_advskew(const char *val, int d, int s, const st=
ruct afswtch *afp)
 	return;
 }
=20
=2Dvoid
=2Dsetcarp_advbase(const char *val, int d, int s, const struct afswtch *afp)
+static
+DECL_CMD_FUNC(setcarp_advbase, val, d)
 {
 	int advbase;
 	struct carpreq carpr;
@@ -174,11 +168,51 @@ setcarp_advbase(const char *val, int d, int s, const =
struct afswtch *afp)
 	return;
 }
=20
+static
+DECL_CMD_FUNC(setcarp_carpdev, val, d)
+{
+	struct carpreq carpr;
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data =3D (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) =3D=3D -1)
+		err(1, "SIOCGVH");
+
+	strlcpy(carpr.carpr_carpdev, val, sizeof(carpr.carpr_carpdev));
+
+	if (ioctl(s, SIOCSVH, (caddr_t)&ifr) =3D=3D -1)
+		err(1, "SIOCSVH");
+
+	return;
+}
+
+static
+DECL_CMD_FUNC(setcarp_unsetcarpdev, val, d)
+{
+	struct carpreq carpr;
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data =3D (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) =3D=3D -1)
+		err(1, "SIOCGVH");
+
+	memset(carpr.carpr_carpdev, 0, sizeof(carpr.carpr_carpdev));
+
+	if (ioctl(s, SIOCSVH, (caddr_t)&ifr) =3D=3D -1)
+		err(1, "SIOCSVH");
+
+	return;
+}
+
 static struct cmd carp_cmds[] =3D {
 	DEF_CMD_ARG("advbase",	setcarp_advbase),
 	DEF_CMD_ARG("advskew",	setcarp_advskew),
 	DEF_CMD_ARG("pass",	setcarp_passwd),
 	DEF_CMD_ARG("vhid",	setcarp_vhid),
+	DEF_CMD_ARG("carpdev",	setcarp_carpdev),
+	DEF_CMD_OPTARG("-carpdev",	setcarp_unsetcarpdev),
 };
 static struct afswtch af_carp =3D {
 	.af_name	=3D "af_carp",
diff --git a/sys/amd64/conf/CARP b/sys/amd64/conf/CARP
new file mode 100644
index 0000000..710a970
=2D-- /dev/null
+++ b/sys/amd64/conf/CARP
@@ -0,0 +1,4 @@
+include 	GENERIC
+ident 		CARP
+
+device		carp
diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h
index 7d45ce3..e7a3450 100644
=2D-- a/sys/net/ethernet.h
+++ b/sys/net/ethernet.h
@@ -380,6 +380,7 @@ extern	void ether_demux(struct ifnet *, struct mbuf *);
 extern	void ether_ifattach(struct ifnet *, const u_int8_t *);
 extern	void ether_ifdetach(struct ifnet *);
 extern	int  ether_ioctl(struct ifnet *, u_long, caddr_t);
+extern	void ether_input(struct ifnet *, struct mbuf *);
 extern	int  ether_output(struct ifnet *,
 		   struct mbuf *, struct sockaddr *, struct rtentry *);
 extern	int  ether_output_frame(struct ifnet *, struct mbuf *);
diff --git a/sys/net/if.c b/sys/net/if.c
index 9db8935..9178a3d 100644
=2D-- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -1309,8 +1309,7 @@ if_unroute(struct ifnet *ifp, int flag, int fam)
 			pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
 	if_qflush(&ifp->if_snd);
 #ifdef DEV_CARP
=2D	if (ifp->if_carp)
=2D		carp_carpdev_state(ifp->if_carp);
+	carp_carpdev_state(ifp);
 #endif
 	rt_ifmsg(ifp);
 }
@@ -1333,8 +1332,7 @@ if_route(struct ifnet *ifp, int flag, int fam)
 		if (fam =3D=3D PF_UNSPEC || (fam =3D=3D ifa->ifa_addr->sa_family))
 			pfctlinput(PRC_IFUP, ifa->ifa_addr);
 #ifdef DEV_CARP
=2D	if (ifp->if_carp)
=2D		carp_carpdev_state(ifp->if_carp);
+	carp_carpdev_state(ifp);
 #endif
 	rt_ifmsg(ifp);
 #ifdef INET6
@@ -1386,8 +1384,7 @@ do_link_state_change(void *arg, int pending)
 	    IFP2AC(ifp)->ac_netgraph !=3D NULL)
 		(*ng_ether_link_state_p)(ifp, link_state);
 #ifdef DEV_CARP
=2D	if (ifp->if_carp)
=2D		carp_carpdev_state(ifp->if_carp);
+	carp_carpdev_state(ifp);
 #endif
 	if (ifp->if_bridge) {
 		KASSERT(bstp_linkstate_p !=3D NULL,("if_bridge bstp not loaded!"));
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 7023e9c..170bcc7 100644
=2D-- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -153,6 +153,9 @@ ether_output(struct ifnet *ifp, struct mbuf *m,
 	u_char esrc[ETHER_ADDR_LEN], edst[ETHER_ADDR_LEN];
 	struct ether_header *eh;
 	struct pf_mtag *t;
+#ifdef DEV_CARP
+	struct ifnet *ifp0 =3D ifp;
+#endif
 	int loop_copy =3D 1;
 	int hlen;	/* link layer header length */
=20
@@ -162,6 +165,19 @@ ether_output(struct ifnet *ifp, struct mbuf *m,
 		senderr(error);
 #endif
=20
+#ifdef DEV_CARP
+	if (ifp->if_type =3D=3D IFT_CARP) {
+		struct ifaddr *ifa;
+
+		if (dst !=3D NULL && ifp->if_link_state =3D=3D LINK_STATE_UP &&
+		    (ifa =3D ifa_ifwithaddr(dst)) !=3D NULL &&
+		    ifa->ifa_ifp =3D=3D ifp)
+			return (looutput(ifp, m, dst, rt0));
+
+		ifp =3D ifp->if_carpdev;
+	}
+#endif
+
 	if (ifp->if_flags & IFF_MONITOR)
 		senderr(ENETDOWN);
 	if (!((ifp->if_flags & IFF_UP) &&
@@ -172,7 +188,11 @@ ether_output(struct ifnet *ifp, struct mbuf *m,
 	switch (dst->sa_family) {
 #ifdef INET
 	case AF_INET:
+#ifdef DEV_CARP
+		error =3D arpresolve(ifp0, rt0, m, dst, edst);
+#else
 		error =3D arpresolve(ifp, rt0, m, dst, edst);
+#endif
 		if (error)
 			return (error =3D=3D EWOULDBLOCK ? 0 : error);
 		type =3D htons(ETHERTYPE_IP);
@@ -293,6 +313,14 @@ ether_output(struct ifnet *ifp, struct mbuf *m,
 		(void)memcpy(eh->ether_shost, IF_LLADDR(ifp),
 			sizeof(eh->ether_shost));
=20
+#ifdef DEV_CARP
+	if (ifp0 !=3D ifp && ifp0->if_type =3D=3D IFT_CARP) {
+		/* XXX: LINK1 */
+		(void)memcpy(eh->ether_shost, IF_LLADDR(ifp0),
+		    sizeof(eh->ether_shost));
+	}
+#endif
+
 	/*
 	 * If a simplex interface, and the packet is being sent to our
 	 * Ethernet address or a broadcast address, loopback a copy.
@@ -351,12 +379,6 @@ ether_output(struct ifnet *ifp, struct mbuf *m,
 		return (error);
 	}
=20
=2D#ifdef DEV_CARP
=2D	if (ifp->if_carp &&
=2D	    (error =3D carp_output(ifp, m, dst, NULL)))
=2D		goto bad;
=2D#endif
=2D
 	/* Handle ng_ether(4) processing, if any */
 	if (IFP2AC(ifp)->ac_netgraph !=3D NULL) {
 		KASSERT(ng_ether_output_p !=3D NULL,
@@ -506,7 +528,7 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
  * Process a received Ethernet packet; the packet is in the
  * mbuf chain m with the ethernet header at the front.
  */
=2Dstatic void
+void
 ether_input(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ether_header *eh;
@@ -658,19 +680,15 @@ ether_input(struct ifnet *ifp, struct mbuf *m)
 	}
=20
 #ifdef DEV_CARP
=2D	/*
=2D	 * Clear M_PROMISC on frame so that carp(4) will see it when the
=2D	 * mbuf flows up to Layer 3.
=2D	 * FreeBSD's implementation of carp(4) uses the inprotosw
=2D	 * to dispatch IPPROTO_CARP. carp(4) also allocates its own
=2D	 * Ethernet addresses of the form 00:00:5e:00:01:xx, which
=2D	 * is outside the scope of the M_PROMISC test below.
=2D	 * TODO: Maintain a hash table of ethernet addresses other than
=2D	 * ether_dhost which may be active on this ifp.
=2D	 */
=2D	if (ifp->if_carp && carp_forus(ifp->if_carp, eh->ether_dhost)) {
=2D		m->m_flags &=3D ~M_PROMISC;
=2D	} else
+	if (ifp->if_carp) {
+		if (ifp->if_type !=3D IFT_CARP && (carp_input(m) =3D=3D 0))
+			return;
+		else if (ifp->if_type =3D=3D IFT_CARP &&
+		    /* XXX: LINK2 */
+		    m->m_flags & (M_BCAST | M_MCAST) &&
+		    !bcmp(IFP2AC(ifp), eh->ether_dhost, ETHER_ADDR_LEN))
+			m->m_flags &=3D ~(M_BCAST | M_MCAST);
+	}
 #endif
 	{
 		/*
diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c
index bd15bdf..1706f58 100644
=2D-- a/sys/net/if_loop.c
+++ b/sys/net/if_loop.c
@@ -98,8 +98,6 @@ struct lo_softc {
=20
 int		loioctl(struct ifnet *, u_long, caddr_t);
 static void	lortrequest(int, struct rtentry *, struct rt_addrinfo *);
=2Dint		looutput(struct ifnet *ifp, struct mbuf *m,
=2D		    struct sockaddr *dst, struct rtentry *rt);
 static int	lo_clone_create(struct if_clone *, int, caddr_t);
 static void	lo_clone_destroy(struct ifnet *);
=20
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 44a297e..b0da599 100644
=2D-- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -131,7 +131,12 @@ struct ifnet {
 		 */
 	struct	knlist if_klist;	/* events attached to this if */
 	int	if_pcount;		/* number of promiscuous listeners */
=2D	struct	carp_if *if_carp;	/* carp interface structure */
+	union {
+		struct carp_if *carp_s;
+		struct ifnet *carp_d;
+	} if_carp_ptr;
+#define	if_carp		if_carp_ptr.carp_s
+#define	if_carpdev	if_carp_ptr.carp_d
 	struct	bpf_if *if_bpf;		/* packet filter structure */
 	u_short	if_index;		/* numeric abbreviation for this if  */
 	short	if_timer;		/* time 'til if_watchdog called */
@@ -692,6 +697,8 @@ struct	ifaddr *ifa_ifwithroute(int, struct sockaddr *, =
struct sockaddr *);
 struct	ifaddr *ifaof_ifpforaddr(struct sockaddr *, struct ifnet *);
=20
 int	if_simloop(struct ifnet *ifp, struct mbuf *m, int af, int hlen);
+int	looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+	    struct rtentry *rt);
=20
 typedef	void *if_com_alloc_t(u_char type, struct ifnet *ifp);
 typedef	void if_com_free_t(void *com, u_char type);
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index 13f2c06..b4f667d 100644
=2D-- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -110,7 +110,6 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CT=
LFLAG_RW,
 	   &arp_proxyall, 0, "Enable proxy ARP for all suitable requests");
=20
 static void	arp_init(void);
=2Dstatic void	arp_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
 static void	arprequest(struct ifnet *,
 			struct in_addr *, struct in_addr *, u_char *);
 static void	arpintr(struct mbuf *);
@@ -144,7 +143,7 @@ arptimer(void *arg)
 /*
  * Parallel to llc_rtrequest.
  */
=2Dstatic void
+void
 arp_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info)
 {
 	struct sockaddr *gate;
@@ -575,9 +574,6 @@ in_arpinput(struct mbuf *m)
 	int op, rif_len;
 	int req_len;
 	int bridged =3D 0;
=2D#ifdef DEV_CARP
=2D	int carp_match =3D 0;
=2D#endif
=20
 	if (ifp->if_bridge)
 		bridged =3D 1;
@@ -608,12 +604,11 @@ in_arpinput(struct mbuf *m)
 		    itaddr.s_addr =3D=3D ia->ia_addr.sin_addr.s_addr)
 			goto match;
 #ifdef DEV_CARP
=2D		if (ifp->if_carp !=3D NULL &&
+		if (ifp->if_type !=3D IFT_CARP && ifp->if_carp !=3D NULL &&
+		    ia->ia_ifp->if_type =3D=3D IFT_CARP &&
 		    carp_iamatch(ifp->if_carp, ia, &isaddr, &enaddr) &&
=2D		    itaddr.s_addr =3D=3D ia->ia_addr.sin_addr.s_addr) {
=2D			carp_match =3D 1;
+		    itaddr.s_addr =3D=3D ia->ia_addr.sin_addr.s_addr)
 			goto match;
=2D		}
 #endif
 	}
 	LIST_FOREACH(ia, INADDR_HASH(isaddr.s_addr), ia_hash)
@@ -676,7 +671,9 @@ match:
 	/* The following is not an error when doing bridging. */
 	if (!bridged && rt->rt_ifp !=3D ifp
 #ifdef DEV_CARP
=2D	    && (ifp->if_type !=3D IFT_CARP || !carp_match)
+	    && !(rt->rt_ifp->if_type =3D=3D IFT_CARP &&
+	    rt->rt_ifp->if_carpdev =3D=3D ifp) &&
+	    !(ifp->if_type =3D=3D IFT_CARP && ifp->if_carpdev =3D=3D rt->rt_ifp)
 #endif
 							) {
 		if (log_arp_wrong_iface)
diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h
index 9bc6b7b..8c36e02 100644
=2D-- a/sys/netinet/if_ether.h
+++ b/sys/netinet/if_ether.h
@@ -113,6 +113,7 @@ int	arpresolve(struct ifnet *ifp, struct rtentry *rt,
 		struct mbuf *m, struct sockaddr *dst, u_char *desten);
 void	arp_ifinit(struct ifnet *, struct ifaddr *);
 void	arp_ifinit2(struct ifnet *, struct ifaddr *, u_char *);
+void	arp_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
 #endif
=20
 #endif
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index 5239805..f642bb9 100644
=2D-- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -318,7 +318,7 @@ struct protosw inetsw[] =3D {
 	.pr_domain =3D		&inetdomain,
 	.pr_protocol =3D		IPPROTO_CARP,
 	.pr_flags =3D		PR_ATOMIC|PR_ADDR,
=2D	.pr_input =3D		carp_input,
+	.pr_input =3D		carp_proto_input,
 	.pr_output =3D		(pr_output_t*)rip_output,
 	.pr_ctloutput =3D		rip_ctloutput,
 	.pr_usrreqs =3D		&rip_usrreqs
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
index c08d39f..aea3518 100644
=2D-- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -92,11 +92,9 @@ SYSCTL_DECL(_net_inet_carp);
=20
 struct carp_softc {
 	struct ifnet	 	*sc_ifp;	/* Interface clue */
=2D	struct ifnet		*sc_carpdev;	/* Pointer to parent interface */
=2D	struct in_ifaddr 	*sc_ia;		/* primary iface address */
+#define	sc_carpdev sc_ifp->if_carpdev
 	struct ip_moptions 	 sc_imo;
 #ifdef INET6
=2D	struct in6_ifaddr 	*sc_ia6;	/* primary iface address v6 */
 	struct ip6_moptions 	 sc_im6o;
 #endif /* INET6 */
 	TAILQ_ENTRY(carp_softc)	 sc_list;
@@ -159,7 +157,7 @@ struct carp_if {
 	struct mtx	 vhif_mtx;
 };
=20
=2D/* Get carp_if from softc. Valid after carp_set_addr{,6}. */
+/* Get carp_if from softc. Valid after carp_set_{addr[6],ifp}. */
 #define	SC2CIF(sc)		((struct carp_if *)(sc)->sc_carpdev->if_carp)
=20
 /* lock per carp_if queue */
@@ -190,7 +188,7 @@ static void	carp_hmac_generate(struct carp_softc *, u_i=
nt32_t *,
 static int	carp_hmac_verify(struct carp_softc *, u_int32_t *,
 		    unsigned char *);
 static void	carp_setroute(struct carp_softc *, int);
=2Dstatic void	carp_input_c(struct mbuf *, struct carp_header *, sa_family_=
t);
+static void	carp_proto_input_c(struct mbuf *, struct carp_header *, sa_fam=
ily_t);
 static int 	carp_clone_create(struct if_clone *, int, caddr_t);
 static void 	carp_clone_destroy(struct ifnet *);
 static void	carpdetach(struct carp_softc *, int);
@@ -203,7 +201,7 @@ static void	carp_send_arp(struct carp_softc *);
 static void	carp_master_down(void *);
 static void	carp_master_down_locked(struct carp_softc *);
 static int	carp_ioctl(struct ifnet *, u_long, caddr_t);
=2Dstatic int	carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr =
*,
+static int	carp_output(struct ifnet *, struct mbuf *, struct sockaddr *,
 		    struct rtentry *);
 static void	carp_start(struct ifnet *);
 static void	carp_setrun(struct carp_softc *, sa_family_t);
@@ -212,13 +210,16 @@ static int	carp_addrcount(struct carp_if *, struct in=
_ifaddr *, int);
 enum	{ CARP_COUNT_MASTER, CARP_COUNT_RUNNING };
=20
 static void	carp_multicast_cleanup(struct carp_softc *);
+static int	carp_set_ifp(struct carp_softc *, struct ifnet *);
 static int	carp_set_addr(struct carp_softc *, struct sockaddr_in *);
+static int	carp_join_multicast(struct carp_softc *);
 static int	carp_del_addr(struct carp_softc *, struct sockaddr_in *);
 static void	carp_carpdev_state_locked(struct carp_if *);
 static void	carp_sc_state_locked(struct carp_softc *);
 #ifdef INET6
 static void	carp_send_na(struct carp_softc *);
 static int	carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *);
+static int	carp_join_multicast6(struct carp_softc *);
 static int	carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *);
 static void	carp_multicast6_cleanup(struct carp_softc *);
 #endif
@@ -247,9 +248,9 @@ carp_hmac_prepare(struct carp_softc *sc)
 #endif
=20
 	if (sc->sc_carpdev)
=2D		CARP_SCLOCK(sc);
+		CARP_SCLOCK_ASSERT(sc);
=20
=2D	/* XXX: possible race here */
+	/* XXX: possible race here - really? */
=20
 	/* compute ipad from key */
 	bzero(sc->sc_pad, sizeof(sc->sc_pad));
@@ -285,8 +286,6 @@ carp_hmac_prepare(struct carp_softc *sc)
 	for (i =3D 0; i < sizeof(sc->sc_pad); i++)
 		sc->sc_pad[i] ^=3D 0x36 ^ 0x5c;
=20
=2D	if (sc->sc_carpdev)
=2D		CARP_SCUNLOCK(sc);
 }
=20
 static void
@@ -334,13 +333,106 @@ carp_setroute(struct carp_softc *sc, int cmd)
 	TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
 		if (ifa->ifa_addr->sa_family =3D=3D AF_INET &&
 		    sc->sc_carpdev !=3D NULL) {
=2D			int count =3D carp_addrcount(
=2D			    (struct carp_if *)sc->sc_carpdev->if_carp,
=2D			    ifatoia(ifa), CARP_COUNT_MASTER);
+			int count =3D 0, error;
+			struct sockaddr sa;
+			struct rtentry *rt;
+			struct radix_node_head *rnh;
+			struct radix_node *rn;
+			struct rt_addrinfo info;
+			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 !=3D NULL &&
+			    sc->sc_carpdev->if_carp !=3D NULL) {
+				count =3D carp_addrcount(
+				    (struct carp_if *)sc->sc_carpdev->if_carp,
+				    ifatoia(ifa), CARP_COUNT_MASTER);
+				if ((cmd =3D=3D RTM_ADD && count !=3D 1) ||
+				    (cmd =3D=3D RTM_DELETE && count !=3D 0))
+					continue;
+			}
=20
=2D			if ((cmd =3D=3D RTM_ADD && count =3D=3D 1) ||
=2D			    (cmd =3D=3D RTM_DELETE && count =3D=3D 0))
=2D				rtinit(ifa, cmd, RTF_UP | RTF_HOST);
+			/* Remove the existing host route, if any */
+			bzero(&info, sizeof(info));
+			info.rti_info[RTAX_DST] =3D ifa->ifa_addr;
+			info.rti_flags =3D RTF_HOST;
+			error =3D rtrequest1(RTM_DELETE, &info, NULL);
+			rt_missmsg(RTM_DELETE, &info, info.rti_flags, error);
+
+			/* Check for our address on another interface */
+			/* XXX cries for proper API */
+			rnh =3D rt_tables[ifa->ifa_addr->sa_family];
+			RADIX_NODE_HEAD_LOCK(rnh);
+			rn =3D rnh->rnh_matchaddr(ifa->ifa_addr, rnh);
+			rt =3D (struct rtentry *)rn;
+			hr_otherif =3D (rt && rt->rt_ifp !=3D sc->sc_ifp &&
+			    rt->rt_flags & (RTF_CLONING|RTF_WASCLONED));
+
+			/* Check for a network route on our interface */
+			bcopy(ifa->ifa_addr, &sa, sizeof(sa));
+			satosin(&sa)->sin_addr.s_addr =3D satosin(ifa->ifa_netmask
+			    )->sin_addr.s_addr & satosin(&sa)->sin_addr.s_addr;
+			rn =3D rnh->rnh_lookup(&sa, ifa->ifa_netmask, rnh);
+			rt =3D (struct rtentry *)rn;
+			nr_ourif =3D (rt && rt->rt_ifp =3D=3D sc->sc_ifp);
+			RADIX_NODE_HEAD_UNLOCK(rnh);
+
+			switch (cmd) {
+			case RTM_ADD:
+				if (hr_otherif) {
+					ifa->ifa_rtrequest =3D NULL;
+					ifa->ifa_flags &=3D ~RTF_CLONING;
+					bzero(&info, sizeof(info));
+					info.rti_info[RTAX_DST] =3D
+					    ifa->ifa_addr;
+					info.rti_info[RTAX_GATEWAY] =3D
+					    ifa->ifa_addr;
+					info.rti_flags =3D RTF_UP | RTF_HOST;
+					error =3D rtrequest1(RTM_ADD, &info,
+					    NULL);
+					rt_missmsg(RTM_ADD, &info,
+					    info.rti_flags, error);
+				}
+				if (!hr_otherif || nr_ourif || !rt) {
+					if (nr_ourif && !(rt->rt_flags &
+					    RTF_CLONING)) {
+						bzero(&info, sizeof(info));
+						info.rti_info[RTAX_DST] =3D &sa;
+						info.rti_info[RTAX_NETMASK] =3D
+						    ifa->ifa_netmask;
+						error =3D rtrequest1(RTM_DELETE,
+						    &info, NULL);
+						rt_missmsg(RTM_DELETE, &info,
+						    info.rti_flags, error);
+					}
+
+					ifa->ifa_rtrequest =3D arp_rtrequest;
+					ifa->ifa_flags |=3D RTF_CLONING;
+
+					bzero(&info, sizeof(info));
+					info.rti_info[RTAX_DST] =3D &sa;
+					info.rti_info[RTAX_GATEWAY] =3D
+					    ifa->ifa_addr;
+					info.rti_info[RTAX_NETMASK] =3D
+					    ifa->ifa_netmask;
+					error =3D rtrequest1(RTM_ADD, &info,
+					    NULL);
+					if (error =3D=3D 0)
+						ifa->ifa_flags |=3D IFA_ROUTE;
+					rt_missmsg(RTM_ADD, &info,
+					    info.rti_flags, error);
+				}
+				break;
+			case RTM_DELETE:
+				break;
+			default:
+				break;
+			}
+			break;
 		}
 #ifdef INET6
 		if (ifa->ifa_addr->sa_family =3D=3D AF_INET6) {
@@ -360,6 +452,7 @@ carp_clone_create(struct if_clone *ifc, int unit, caddr=
_t params)
=20
 	struct carp_softc *sc;
 	struct ifnet *ifp;
+	static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
=20
 	MALLOC(sc, struct carp_softc *, sizeof(*sc), M_CARP, M_WAITOK|M_ZERO);
 	ifp =3D SC2IFP(sc) =3D if_alloc(IFT_ETHER);
@@ -391,16 +484,13 @@ carp_clone_create(struct if_clone *ifc, int unit, cad=
dr_t params)
 =09
 	ifp->if_softc =3D sc;
 	if_initname(ifp, CARP_IFNAME, unit);
=2D	ifp->if_mtu =3D ETHERMTU;
=2D	ifp->if_flags =3D IFF_LOOPBACK;
+	ether_ifattach(ifp, eaddr);
+	ifp->if_flags =3D IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl =3D carp_ioctl;
=2D	ifp->if_output =3D carp_looutput;
+	ifp->if_output =3D carp_output;
 	ifp->if_start =3D carp_start;
 	ifp->if_type =3D IFT_CARP;
 	ifp->if_snd.ifq_maxlen =3D ifqmaxlen;
=2D	ifp->if_hdrlen =3D 0;
=2D	if_attach(ifp);
=2D	bpfattach(SC2IFP(sc), DLT_NULL, sizeof(u_int32_t));
 	mtx_lock(&carp_mtx);
 	LIST_INSERT_HEAD(&carpif_list, sc, sc_next);
 	mtx_unlock(&carp_mtx);
@@ -503,7 +593,7 @@ carp_ifdetach(void *arg __unused, struct ifnet *ifp)
  * but it seems more efficient this way or not possible otherwise.
  */
 void
=2Dcarp_input(struct mbuf *m, int hlen)
+carp_proto_input(struct mbuf *m, int hlen)
 {
 	struct ip *ip =3D mtod(m, struct ip *);
 	struct carp_header *ch;
@@ -517,9 +607,9 @@ carp_input(struct mbuf *m, int hlen)
 	}
=20
 	/* check if received on a valid carp interface */
=2D	if (m->m_pkthdr.rcvif->if_carp =3D=3D NULL) {
+	if (m->m_pkthdr.rcvif->if_type !=3D IFT_CARP) {
 		carpstats.carps_badif++;
=2D		CARP_LOG("carp_input: packet received on non-carp "
+		CARP_LOG("carp_proto_input: packet received on non-carp "
 		    "interface: %s\n",
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
@@ -529,7 +619,7 @@ carp_input(struct mbuf *m, int hlen)
 	/* verify that the IP TTL is 255.  */
 	if (ip->ip_ttl !=3D CARP_DFLTTL) {
 		carpstats.carps_badttl++;
=2D		CARP_LOG("carp_input: received ttl %d !=3D 255i on %s\n",
+		CARP_LOG("carp_proto_input: received ttl %d !=3D 255i on %s\n",
 		    ip->ip_ttl,
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
@@ -540,7 +630,7 @@ carp_input(struct mbuf *m, int hlen)
=20
 	if (m->m_pkthdr.len < iplen + sizeof(*ch)) {
 		carpstats.carps_badlen++;
=2D		CARP_LOG("carp_input: received len %zd < "
+		CARP_LOG("carp_proto_input: received len %zd < "
 		    "sizeof(struct carp_header)\n",
 		    m->m_len - sizeof(struct ip));
 		m_freem(m);
@@ -550,7 +640,7 @@ carp_input(struct mbuf *m, int hlen)
 	if (iplen + sizeof(*ch) < m->m_len) {
 		if ((m =3D m_pullup(m, iplen + sizeof(*ch))) =3D=3D NULL) {
 			carpstats.carps_hdrops++;
=2D			CARP_LOG("carp_input: pullup failed\n");
+			CARP_LOG("carp_proto_input: pullup failed\n");
 			return;
 		}
 		ip =3D mtod(m, struct ip *);
@@ -564,7 +654,7 @@ carp_input(struct mbuf *m, int hlen)
 	len =3D iplen + sizeof(*ch);
 	if (len > m->m_pkthdr.len) {
 		carpstats.carps_badlen++;
=2D		CARP_LOG("carp_input: packet too short %d on %s\n",
+		CARP_LOG("carp_proto_input: packet too short %d on %s\n",
 		    m->m_pkthdr.len,
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
@@ -582,19 +672,19 @@ carp_input(struct mbuf *m, int hlen)
 	m->m_data +=3D iplen;
 	if (carp_cksum(m, len - iplen)) {
 		carpstats.carps_badsum++;
=2D		CARP_LOG("carp_input: checksum failed on %s\n",
+		CARP_LOG("carp_proto_input: checksum failed on %s\n",
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
 		return;
 	}
 	m->m_data -=3D iplen;
=20
=2D	carp_input_c(m, ch, AF_INET);
+	carp_proto_input_c(m, ch, AF_INET);
 }
=20
 #ifdef INET6
 int
=2Dcarp6_input(struct mbuf **mp, int *offp, int proto)
+carp6_proto_input(struct mbuf **mp, int *offp, int proto)
 {
 	struct mbuf *m =3D *mp;
 	struct ip6_hdr *ip6 =3D mtod(m, struct ip6_hdr *);
@@ -609,9 +699,9 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
 	}
=20
 	/* check if received on a valid carp interface */
=2D	if (m->m_pkthdr.rcvif->if_carp =3D=3D NULL) {
+	if (m->m_pkthdr.rcvif->if_type !=3D IFT_CARP) {
 		carpstats.carps_badif++;
=2D		CARP_LOG("carp6_input: packet received on non-carp "
+		CARP_LOG("carp6_proto_input: packet received on non-carp "
 		    "interface: %s\n",
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
@@ -621,7 +711,7 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
 	/* verify that the IP TTL is 255 */
 	if (ip6->ip6_hlim !=3D CARP_DFLTTL) {
 		carpstats.carps_badttl++;
=2D		CARP_LOG("carp6_input: received ttl %d !=3D 255 on %s\n",
+		CARP_LOG("carp6_proto_input: received ttl %d !=3D 255 on %s\n",
 		    ip6->ip6_hlim,
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
@@ -633,7 +723,7 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
 	IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch));
 	if (ch =3D=3D NULL) {
 		carpstats.carps_badlen++;
=2D		CARP_LOG("carp6_input: packet size %u too small\n", len);
+		CARP_LOG("carp6_proto_input: packet size %u too small\n", len);
 		return (IPPROTO_DONE);
 	}
=20
@@ -642,22 +732,22 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
 	m->m_data +=3D *offp;
 	if (carp_cksum(m, sizeof(*ch))) {
 		carpstats.carps_badsum++;
=2D		CARP_LOG("carp6_input: checksum failed, on %s\n",
+		CARP_LOG("carp6_proto_input: checksum failed, on %s\n",
 		    m->m_pkthdr.rcvif->if_xname);
 		m_freem(m);
 		return (IPPROTO_DONE);
 	}
 	m->m_data -=3D *offp;
=20
=2D	carp_input_c(m, ch, AF_INET6);
+	carp_proto_input_c(m, ch, AF_INET6);
 	return (IPPROTO_DONE);
 }
 #endif /* INET6 */
=20
 static void
=2Dcarp_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)
 {
=2D	struct ifnet *ifp =3D m->m_pkthdr.rcvif;
+	struct ifnet *ifp =3D m->m_pkthdr.rcvif->if_carpdev;
 	struct carp_softc *sc;
 	u_int64_t tmp_counter;
 	struct timeval sc_tv, ch_tv;
@@ -793,9 +883,6 @@ carp_input_c(struct mbuf *m, struct carp_header *ch, sa=
_family_t af)
 static int
 carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header =
*ch)
 {
=2D	struct m_tag *mtag;
=2D	struct ifnet *ifp =3D SC2IFP(sc);
=2D
 	if (sc->sc_init_counter) {
 		/* this could also be seconds since unix epoch */
 		sc->sc_counter =3D arc4random();
@@ -809,16 +896,6 @@ carp_prepare_ad(struct mbuf *m, struct carp_softc *sc,=
 struct carp_header *ch)
=20
 	carp_hmac_generate(sc, ch->carp_counter, ch->carp_md);
=20
=2D	/* Tag packet for carp_output */
=2D	mtag =3D m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT);
=2D	if (mtag =3D=3D NULL) {
=2D		m_freem(m);
=2D		SC2IFP(sc)->if_oerrors++;
=2D		return (ENOMEM);
=2D	}
=2D	bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *));
=2D	m_tag_prepend(m, mtag);
=2D
 	return (0);
 }
=20
@@ -859,6 +936,8 @@ carp_send_ad_locked(struct carp_softc *sc)
 	struct carp_header *ch_ptr;
 	struct mbuf *m;
 	int len, advbase, advskew;
+	struct ifaddr *ifa;
+	struct sockaddr sa;
=20
 	CARP_SCLOCK_ASSERT(sc);
=20
@@ -887,7 +966,7 @@ carp_send_ad_locked(struct carp_softc *sc)
 	ch.carp_cksum =3D 0;
=20
 #ifdef INET
=2D	if (sc->sc_ia) {
+	if (sc->sc_naddrs) {
 		struct ip *ip;
=20
 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
@@ -916,7 +995,15 @@ carp_send_ad_locked(struct carp_softc *sc)
 		ip->ip_ttl =3D CARP_DFLTTL;
 		ip->ip_p =3D IPPROTO_CARP;
 		ip->ip_sum =3D 0;
=2D		ip->ip_src.s_addr =3D sc->sc_ia->ia_addr.sin_addr.s_addr;
+
+		bzero(&sa, sizeof(sa));
+		sa.sa_family =3D AF_INET;
+		ifa =3D ifaof_ifpforaddr(&sa, SC2IFP(sc));
+		if (ifa =3D=3D NULL)
+			ip->ip_src.s_addr =3D 0;
+		else
+			ip->ip_src.s_addr =3D
+			    ifatoia(ifa)->ia_addr.sin_addr.s_addr;
 		ip->ip_dst.s_addr =3D htonl(INADDR_CARP_GROUP);
=20
 		ch_ptr =3D (struct carp_header *)(&ip[1]);
@@ -959,7 +1046,7 @@ carp_send_ad_locked(struct carp_softc *sc)
 	}
 #endif /* INET */
 #ifdef INET6
=2D	if (sc->sc_ia6) {
+	if (sc->sc_naddrs6) {
 		struct ip6_hdr *ip6;
=20
 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
@@ -983,8 +1070,15 @@ carp_send_ad_locked(struct carp_softc *sc)
 		ip6->ip6_vfc |=3D IPV6_VERSION;
 		ip6->ip6_hlim =3D CARP_DFLTTL;
 		ip6->ip6_nxt =3D IPPROTO_CARP;
=2D		bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src,
=2D		    sizeof(struct in6_addr));
+
+		bzero(&sa, sizeof(sa));
+		sa.sa_family =3D AF_INET6;
+		ifa =3D ifaof_ifpforaddr(&sa, SC2IFP(sc));
+		if (ifa =3D=3D NULL)
+			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 */
=20
 		ip6->ip6_dst.s6_addr16[0] =3D htons(0xff02);
@@ -1058,7 +1152,7 @@ carp_send_arp(struct carp_softc *sc)
 			continue;
=20
 /*		arprequest(sc->sc_carpdev, &in, &in, IF_LLADDR(sc->sc_ifp)); */
=2D		arp_ifinit2(sc->sc_carpdev, ifa, IF_LLADDR(sc->sc_ifp));
+		arp_ifinit2(SC2IFP(sc), ifa, IF_LLADDR(sc->sc_ifp));
=20
 		DELAY(1000);	/* XXX */
 	}
@@ -1211,7 +1305,6 @@ carp_iamatch6(void *v, struct in6_addr *taddr)
 void *
 carp_macmatch6(void *v, struct mbuf *m, const struct in6_addr *taddr)
 {
=2D	struct m_tag *mtag;
 	struct carp_if *cif =3D v;
 	struct carp_softc *sc;
 	struct ifaddr *ifa;
@@ -1223,18 +1316,6 @@ carp_macmatch6(void *v, struct mbuf *m, const struct=
 in6_addr *taddr)
 			    &ifatoia6(ifa)->ia_addr.sin6_addr) &&
  			    (SC2IFP(sc)->if_flags & IFF_UP) &&
 			    (SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING)) {
=2D				struct ifnet *ifp =3D SC2IFP(sc);
=2D				mtag =3D m_tag_get(PACKET_TAG_CARP,
=2D				    sizeof(struct ifnet *), M_NOWAIT);
=2D				if (mtag =3D=3D NULL) {
=2D					/* better a bit than nothing */
=2D					CARP_UNLOCK(cif);
=2D					return (IF_LLADDR(sc->sc_ifp));
=2D				}
=2D				bcopy(&ifp, (caddr_t)(mtag + 1),
=2D				    sizeof(struct ifnet *));
=2D				m_tag_prepend(m, mtag);
=2D
 				CARP_UNLOCK(cif);
 				return (IF_LLADDR(sc->sc_ifp));
 			}
@@ -1423,15 +1504,116 @@ carp_multicast6_cleanup(struct carp_softc *sc)
 #endif
=20
 static int
+carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp)
+{
+	struct carp_if *cif =3D NULL, *ncif =3D NULL;
+	struct carp_softc *vr, *after =3D NULL;
+	int myself =3D 0, error =3D 0;
+
+	if (ifp =3D=3D sc->sc_carpdev)
+		return (0);
+
+	if (ifp !=3D NULL) {
+		if ((ifp->if_flags & IFF_MULTICAST) =3D=3D 0)
+			return (ENODEV);
+		if (ifp->if_type =3D=3D IFT_CARP)
+			return (EINVAL);
+
+		if (ifp->if_carp =3D=3D NULL) {
+			MALLOC(ncif, struct carp_if *, sizeof(*ncif), M_CARP,
+			    M_WAITOK|M_ZERO);
+			if (!ncif)
+				return (ENOBUFS);
+			if ((error =3D ifpromisc(ifp, 1))) {
+				FREE(ncif, M_CARP);
+				return (error);
+			}
+		} else {
+			cif =3D (struct carp_if *)ifp->if_carp;
+			CARP_LOCK(cif);
+			TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
+				if (vr !=3D sc && vr->sc_vhid =3D=3D sc->sc_vhid) {
+					CARP_UNLOCK(cif);
+					return (EINVAL);
+				}
+		}
+
+		/* detach from old interface */
+		if (sc->sc_carpdev !=3D NULL) {
+			CARP_SCLOCK(sc);
+			carpdetach(sc, 1);
+		}
+
+		if (sc->sc_naddrs !=3D 0 &&
+		    (error =3D carp_join_multicast(sc)) !=3D 0)
+		    	goto cleanup;
+#ifdef INET6
+		if (sc->sc_naddrs6 !=3D 0 &&
+		    (error =3D carp_join_multicast6(sc)) !=3D 0) {
+		    	carp_multicast_cleanup(sc);
+		    	goto cleanup;
+		}
+#endif
+
+		/* attach carp glue to physical interface */
+		if (ncif !=3D NULL) {
+			CARP_LOCK_INIT(ncif);
+			CARP_LOCK(ncif);
+			ncif->vhif_ifp =3D ifp;
+			TAILQ_INIT(&ncif->vhif_vrs);
+			TAILQ_INSERT_HEAD(&ncif->vhif_vrs, sc, sc_list);
+			ncif->vhif_nvrs++;
+			ifp->if_carp =3D ncif;
+			CARP_UNLOCK(ncif);
+		} else {
+			cif =3D (struct carp_if *)ifp->if_carp;
+			TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) {
+				if (vr =3D=3D sc)
+					myself =3D 1;
+				if (vr->sc_vhid < sc->sc_vhid)
+					after =3D vr;
+			}
+			if (!myself) {
+				if (after =3D=3D NULL) {
+					TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc,
+					    sc_list);
+				} else {
+					TAILQ_INSERT_AFTER(&cif->vhif_vrs,
+					    after, sc, sc_list);
+				}
+				cif->vhif_nvrs++;
+			}
+			CARP_UNLOCK(cif);
+		}
+
+		sc->sc_carpdev =3D ifp;
+		if (sc->sc_naddrs || sc->sc_naddrs6)
+			sc->sc_ifp->if_flags |=3D IFF_UP;
+		carp_carpdev_state(ifp);
+	} else {
+		CARP_SCLOCK(sc);
+		carpdetach(sc, 1);
+		SC2IFP(sc)->if_flags &=3D ~IFF_UP;
+		SC2IFP(sc)->if_drv_flags &=3D ~IFF_DRV_RUNNING;
+	}
+
+	return (0);
+cleanup:
+	if (ncif)
+		FREE(ncif, M_CARP);
+	else
+		CARP_UNLOCK(cif);
+
+	return (error);
+}
+
+static int
 carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin)
 {
=2D	struct ifnet *ifp;
=2D	struct carp_if *cif;
+	struct ifnet *ifp =3D sc->sc_carpdev;
 	struct in_ifaddr *ia, *ia_if;
=2D	struct ip_moptions *imo =3D &sc->sc_imo;
=2D	struct in_addr addr;
 	u_long iaddr =3D htonl(sin->sin_addr.s_addr);
=2D	int own, error;
+	int error;
=20
 	if (sin->sin_addr.s_addr =3D=3D 0) {
 		if (!(SC2IFP(sc)->if_flags & IFF_UP))
@@ -1443,7 +1625,7 @@ carp_set_addr(struct carp_softc *sc, struct sockaddr_=
in *sin)
 	}
=20
 	/* we have to do it by hands to check we won't match on us */
=2D	ia_if =3D NULL; own =3D 0;
+	ia_if =3D NULL;
 	TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
 		/* and, yeah, we need a multicast-capable iface too */
 		if (ia->ia_ifp !=3D SC2IFP(sc) &&
@@ -1451,106 +1633,65 @@ carp_set_addr(struct carp_softc *sc, struct sockad=
dr_in *sin)
 		    (iaddr & ia->ia_subnetmask) =3D=3D ia->ia_subnet) {
 			if (!ia_if)
 				ia_if =3D ia;
=2D			if (sin->sin_addr.s_addr =3D=3D
=2D			    ia->ia_addr.sin_addr.s_addr)
=2D				own++;
 		}
 	}
=20
=2D	if (!ia_if)
=2D		return (EADDRNOTAVAIL);
=2D
=2D	ia =3D ia_if;
=2D	ifp =3D ia->ia_ifp;
=2D
=2D	if (ifp =3D=3D NULL || (ifp->if_flags & IFF_MULTICAST) =3D=3D 0 ||
=2D	    (imo->imo_multicast_ifp && imo->imo_multicast_ifp !=3D ifp))
=2D		return (EADDRNOTAVAIL);
=2D
=2D	if (imo->imo_num_memberships =3D=3D 0) {
=2D		addr.s_addr =3D htonl(INADDR_CARP_GROUP);
=2D		if ((imo->imo_membership[0] =3D in_addmulti(&addr, ifp)) =3D=3D NULL)
=2D			return (ENOBUFS);
=2D		imo->imo_num_memberships++;
=2D		imo->imo_multicast_ifp =3D ifp;
=2D		imo->imo_multicast_ttl =3D CARP_DFLTTL;
=2D		imo->imo_multicast_loop =3D 0;
=2D	}
=2D
=2D	if (!ifp->if_carp) {
=2D
=2D		MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP,
=2D		    M_WAITOK|M_ZERO);
=2D		if (!cif) {
=2D			error =3D ENOBUFS;
=2D			goto cleanup;
=2D		}
=2D		if ((error =3D ifpromisc(ifp, 1))) {
=2D			FREE(cif, M_CARP);
=2D			goto cleanup;
+	if (ia_if) {
+		ia =3D ia_if;
+		if (ifp) {
+			if (ifp !=3D ia->ia_ifp)
+				return (EADDRNOTAVAIL);
+		} else {
+			ifp =3D ia->ia_ifp;
 		}
=2D	=09
=2D		CARP_LOCK_INIT(cif);
=2D		CARP_LOCK(cif);
=2D		cif->vhif_ifp =3D ifp;
=2D		TAILQ_INIT(&cif->vhif_vrs);
=2D		ifp->if_carp =3D cif;
=2D
=2D	} else {
=2D		struct carp_softc *vr;
=2D
=2D		cif =3D (struct carp_if *)ifp->if_carp;
=2D		CARP_LOCK(cif);
=2D		TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
=2D			if (vr !=3D sc && vr->sc_vhid =3D=3D sc->sc_vhid) {
=2D				CARP_UNLOCK(cif);
=2D				error =3D EINVAL;
=2D				goto cleanup;
=2D			}
 	}
=2D	sc->sc_ia =3D ia;
=2D	sc->sc_carpdev =3D ifp;
=20
=2D	{ /* XXX prevent endless loop if already in queue */
=2D	struct carp_softc *vr, *after =3D NULL;
=2D	int myself =3D 0;
=2D	cif =3D (struct carp_if *)ifp->if_carp;
+	if ((error =3D carp_set_ifp(sc, ifp)))
+		return (error);
=20
=2D	/* XXX: cif should not change, right? So we still hold the lock */
=2D	CARP_LOCK_ASSERT(cif);
=2D
=2D	TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) {
=2D		if (vr =3D=3D sc)
=2D			myself =3D 1;
=2D		if (vr->sc_vhid < sc->sc_vhid)
=2D			after =3D vr;
=2D	}
+	if (sc->sc_carpdev =3D=3D NULL)
+		return (EADDRNOTAVAIL);
=20
=2D	if (!myself) {
=2D		/* We're trying to keep things in order */
=2D		if (after =3D=3D NULL) {
=2D			TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list);
=2D		} else {
=2D			TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list);
=2D		}
=2D		cif->vhif_nvrs++;
=2D	}
+	CARP_SCLOCK(sc);
+	if (sc->sc_naddrs =3D=3D 0 && (error =3D carp_join_multicast(sc)) !=3D 0)=
 {
+		CARP_SCUNLOCK(sc);
+		return (error);
 	}
=20
 	sc->sc_naddrs++;
 	SC2IFP(sc)->if_flags |=3D IFF_UP;
=2D	if (own)
=2D		sc->sc_advskew =3D 0;
 	carp_sc_state_locked(sc);
 	carp_setrun(sc, 0);
=2D
=2D	CARP_UNLOCK(cif);
+	CARP_SCUNLOCK(sc);
=20
 	return (0);
=20
=2Dcleanup:
=2D	in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
=2D	return (error);
+/*
+ * XXX: cleanup multi?
+ * cleanup:
+ *	return (error);
+ */
+}
+
+static int
+carp_join_multicast(struct carp_softc *sc)
+{
+	struct ip_moptions *imo =3D &sc->sc_imo;
+	struct in_addr addr;
+
+	KASSERT(imo->imo_num_memberships =3D=3D 0,
+	    ("carp_join_multicast: leftover multicast memberships"));
+
+	addr.s_addr =3D htonl(INADDR_CARP_GROUP);
+	if ((imo->imo_membership[0] =3D
+	    in_addmulti(&addr, SC2IFP(sc))) =3D=3D NULL)
+		return (ENOBUFS);
+	imo->imo_num_memberships++;
+	imo->imo_multicast_ifp =3D SC2IFP(sc);
+	imo->imo_multicast_ttl =3D CARP_DFLTTL;
+	imo->imo_multicast_loop =3D 0;
+
+	return (0);
 }
=20
 static int
@@ -1587,12 +1728,8 @@ static int
 carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
 {
 	struct ifnet *ifp;
=2D	struct carp_if *cif;
 	struct in6_ifaddr *ia, *ia_if;
=2D	struct ip6_moptions *im6o =3D &sc->sc_im6o;
=2D	struct in6_multi_mship *imm;
=2D	struct in6_addr in6;
=2D	int own, error;
+	int own;
=20
 	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
 		if (!(SC2IFP(sc)->if_flags & IFF_UP))
@@ -1633,93 +1770,12 @@ carp_set_addr6(struct carp_softc *sc, struct sockad=
dr_in6 *sin6)
 	ifp =3D ia->ia_ifp;
=20
 	if (ifp =3D=3D NULL || (ifp->if_flags & IFF_MULTICAST) =3D=3D 0 ||
=2D	    (im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp !=3D ifp))
+	    (sc->sc_im6o.im6o_multicast_ifp &&
+	    sc->sc_im6o.im6o_multicast_ifp !=3D ifp))
 		return (EADDRNOTAVAIL);
=20
=2D	if (!sc->sc_naddrs6) {
=2D		im6o->im6o_multicast_ifp =3D ifp;
=2D
=2D		/* join CARP multicast address */
=2D		bzero(&in6, sizeof(in6));
=2D		in6.s6_addr16[0] =3D htons(0xff02);
=2D		in6.s6_addr8[15] =3D 0x12;
=2D		if (in6_setscope(&in6, ifp, NULL) !=3D 0)
=2D			goto cleanup;
=2D		if ((imm =3D in6_joingroup(ifp, &in6, &error, 0)) =3D=3D NULL)
=2D			goto cleanup;
=2D		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
=2D
=2D		/* join solicited multicast address */
=2D		bzero(&in6, sizeof(in6));
=2D		in6.s6_addr16[0] =3D htons(0xff02);
=2D		in6.s6_addr32[1] =3D 0;
=2D		in6.s6_addr32[2] =3D htonl(1);
=2D		in6.s6_addr32[3] =3D sin6->sin6_addr.s6_addr32[3];
=2D		in6.s6_addr8[12] =3D 0xff;
=2D		if (in6_setscope(&in6, ifp, NULL) !=3D 0)
=2D			goto cleanup;
=2D		if ((imm =3D in6_joingroup(ifp, &in6, &error, 0)) =3D=3D NULL)
=2D			goto cleanup;
=2D		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
=2D	}
=2D
=2D	if (!ifp->if_carp) {
=2D		MALLOC(cif, struct carp_if *, sizeof(*cif), M_CARP,
=2D		    M_WAITOK|M_ZERO);
=2D		if (!cif) {
=2D			error =3D ENOBUFS;
=2D			goto cleanup;
=2D		}
=2D		if ((error =3D ifpromisc(ifp, 1))) {
=2D			FREE(cif, M_CARP);
=2D			goto cleanup;
=2D		}
=2D
=2D		CARP_LOCK_INIT(cif);
=2D		CARP_LOCK(cif);
=2D		cif->vhif_ifp =3D ifp;
=2D		TAILQ_INIT(&cif->vhif_vrs);
=2D		ifp->if_carp =3D cif;
=2D
=2D	} else {
=2D		struct carp_softc *vr;
=2D
=2D		cif =3D (struct carp_if *)ifp->if_carp;
=2D		CARP_LOCK(cif);
=2D		TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
=2D			if (vr !=3D sc && vr->sc_vhid =3D=3D sc->sc_vhid) {
=2D				CARP_UNLOCK(cif);
=2D				error =3D EINVAL;
=2D				goto cleanup;
=2D			}
=2D	}
=2D	sc->sc_ia6 =3D ia;
 	sc->sc_carpdev =3D ifp;
=20
=2D	{ /* XXX prevent endless loop if already in queue */
=2D	struct carp_softc *vr, *after =3D NULL;
=2D	int myself =3D 0;
=2D	cif =3D (struct carp_if *)ifp->if_carp;
=2D	CARP_LOCK_ASSERT(cif);
=2D
=2D	TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) {
=2D		if (vr =3D=3D sc)
=2D			myself =3D 1;
=2D		if (vr->sc_vhid < sc->sc_vhid)
=2D			after =3D vr;
=2D	}
=2D
=2D	if (!myself) {
=2D		/* We're trying to keep things in order */
=2D		if (after =3D=3D NULL) {
=2D			TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list);
=2D		} else {
=2D			TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list);
=2D		}
=2D		cif->vhif_nvrs++;
=2D	}
=2D	}
=2D
 	sc->sc_naddrs6++;
 	SC2IFP(sc)->if_flags |=3D IFF_UP;
 	if (own)
@@ -1727,20 +1783,61 @@ carp_set_addr6(struct carp_softc *sc, struct sockad=
dr_in6 *sin6)
 	carp_sc_state_locked(sc);
 	carp_setrun(sc, 0);
=20
=2D	CARP_UNLOCK(cif);
=2D
 	return (0);
=20
=2Dcleanup:
=2D	/* clean up multicast memberships */
=2D	if (!sc->sc_naddrs6) {
=2D		while (!LIST_EMPTY(&im6o->im6o_memberships)) {
=2D			imm =3D LIST_FIRST(&im6o->im6o_memberships);
=2D			LIST_REMOVE(imm, i6mm_chain);
=2D			in6_leavegroup(imm);
=2D		}
+/* XXX:
+ * cleanup:
+ *	 * clean up multicast memberships *
+ *	if (!sc->sc_naddrs6) {
+ *		while (!LIST_EMPTY(&im6o->im6o_memberships)) {
+ *			imm =3D LIST_FIRST(&im6o->im6o_memberships);
+ *			LIST_REMOVE(imm, i6mm_chain);
+ *			in6_leavegroup(imm);
+ *		}
+ *	}
+ *	return (error);
+ */
+}
+
+static int
+carp_join_multicast6(struct carp_softc *sc)
+{
+	struct ip6_moptions *im6o =3D &sc->sc_im6o;
+	struct in6_multi_mship *imm, *imm2;
+	struct in6_addr in6;
+	int error =3D 0;
+
+	/* join CARP multicast address */
+	bzero(&in6, sizeof(in6));
+	in6.s6_addr16[0] =3D htons(0xff02);
+	in6.s6_addr8[15] =3D 0x12;
+	if ((error =3D in6_setscope(&in6, sc->sc_carpdev, NULL)) !=3D 0)
+		return (error);
+	if ((imm =3D in6_joingroup(sc->sc_carpdev, &in6, &error, 0)) =3D=3D NULL)
+		return (error);
+
+	/* join solicited multicast address */
+	bzero(&in6, sizeof(in6));
+	in6.s6_addr16[0] =3D htons(0xff02);
+	in6.s6_addr32[1] =3D 0;
+	in6.s6_addr32[2] =3D htonl(1);
+	in6.s6_addr32[3] =3D 0; /* XXX: sin6->sin6_addr.s6_addr32[3]; */
+	in6.s6_addr8[12] =3D 0xff;
+	if ((error =3D in6_setscope(&in6, sc->sc_carpdev, NULL)) !=3D 0) {
+		in6_leavegroup(imm);
+		return (error);
 	}
=2D	return (error);
+	if ((imm2 =3D in6_joingroup(sc->sc_carpdev, &in6, &error, 0)) =3D=3D NULL=
) {
+		in6_leavegroup(imm);
+		return (error);
+	}
+
+	im6o->im6o_multicast_ifp =3D sc->sc_carpdev;
+
+	LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+	LIST_INSERT_HEAD(&im6o->im6o_memberships, imm2, i6mm_chain);
+
+	return (0);
 }
=20
 static int
@@ -1786,7 +1883,8 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t add=
r)
 	struct ifaddr *ifa;
 	struct ifreq *ifr;
 	struct ifaliasreq *ifra;
=2D	int locked =3D 0, error =3D 0;
+	struct ifnet *cdev =3D NULL;
+	int locked =3D 0, error =3D 0, changed =3D 0;
=20
 	ifa =3D (struct ifaddr *)addr;
 	ifra =3D (struct ifaliasreq *)addr;
@@ -1794,12 +1892,12 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t a=
ddr)
=20
 	switch (cmd) {
 	case SIOCSIFADDR:
+	case SIOCAIFADDR:
+		changed++;
 		switch (ifa->ifa_addr->sa_family) {
 #ifdef INET
 		case AF_INET:
 			SC2IFP(sc)->if_flags |=3D IFF_UP;
=2D			bcopy(ifa->ifa_addr, ifa->ifa_dstaddr,
=2D			    sizeof(struct sockaddr));
 			error =3D carp_set_addr(sc, satosin(ifa->ifa_addr));
 			break;
 #endif /* INET */
@@ -1815,29 +1913,8 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t ad=
dr)
 		}
 		break;
=20
=2D	case SIOCAIFADDR:
=2D		switch (ifa->ifa_addr->sa_family) {
=2D#ifdef INET
=2D		case AF_INET:
=2D			SC2IFP(sc)->if_flags |=3D IFF_UP;
=2D			bcopy(ifa->ifa_addr, ifa->ifa_dstaddr,
=2D			    sizeof(struct sockaddr));
=2D			error =3D carp_set_addr(sc, satosin(&ifra->ifra_addr));
=2D			break;
=2D#endif /* INET */
=2D#ifdef INET6
=2D		case AF_INET6:
=2D			SC2IFP(sc)->if_flags |=3D IFF_UP;
=2D			error =3D carp_set_addr6(sc, satosin6(&ifra->ifra_addr));
=2D			break;
=2D#endif /* INET6 */
=2D		default:
=2D			error =3D EAFNOSUPPORT;
=2D			break;
=2D		}
=2D		break;
=2D
 	case SIOCDIFADDR:
+		changed++;
 		switch (ifa->ifa_addr->sa_family) {
 #ifdef INET
 		case AF_INET:
@@ -1881,6 +1958,14 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t ad=
dr)
 		if ((error =3D copyin(ifr->ifr_data, &carpr, sizeof carpr)))
 			break;
 		error =3D 1;
+		changed++;
+		if (carpr.carpr_carpdev[0] !=3D '\0' &&
+		    (cdev =3D ifunit(carpr.carpr_carpdev)) =3D=3D NULL) {
+		    	error =3D EINVAL;
+		    	break;
+		}
+		if ((error =3D carp_set_ifp(sc, cdev)))
+			break;
 		if (sc->sc_carpdev) {
 			locked =3D 1;
 			CARP_SCLOCK(sc);
@@ -1959,64 +2044,37 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t a=
ddr)
 		if (error =3D=3D 0)
 			bcopy(sc->sc_key, carpr.carpr_key,
 			    sizeof(carpr.carpr_key));
+		if (sc->sc_carpdev !=3D NULL)
+			strlcpy(carpr.carpr_carpdev, sc->sc_carpdev->if_xname,
+			    CARPDEVNAMSIZ);
 		error =3D copyout(&carpr, ifr->ifr_data, sizeof(carpr));
 		break;
=20
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		/* TODO: tell carpdev */
+		break;
+
 	default:
 		error =3D EINVAL;
 	}
=20
+	if (changed) {
+		if (!locked && sc->sc_carpdev) {
+			/* XXX: This really shouldn't happen */
+			CARP_SCLOCK(sc);
+			locked =3D 1;
+		}
+		carp_hmac_prepare(sc);
+	}
+
 	if (locked)
 		CARP_SCUNLOCK(sc);
=20
=2D	carp_hmac_prepare(sc);
=2D
 	return (error);
 }
=20
 /*
=2D * XXX: this is looutput. We should eventually use it from there.
=2D */
=2Dstatic int
=2Dcarp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
=2D    struct rtentry *rt)
=2D{
=2D	u_int32_t af;
=2D
=2D	M_ASSERTPKTHDR(m); /* check if we have the packet header */
=2D
=2D	if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
=2D		m_freem(m);
=2D		return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
=2D			rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
=2D	}
=2D
=2D	ifp->if_opackets++;
=2D	ifp->if_obytes +=3D m->m_pkthdr.len;
=2D
=2D	/* BPF writes need to be handled specially. */
=2D	if (dst->sa_family =3D=3D AF_UNSPEC) {
=2D		bcopy(dst->sa_data, &af, sizeof(af));
=2D		dst->sa_family =3D af;
=2D	}
=2D
=2D#if 1	/* XXX */
=2D	switch (dst->sa_family) {
=2D	case AF_INET:
=2D	case AF_INET6:
=2D	case AF_IPX:
=2D	case AF_APPLETALK:
=2D		break;
=2D	default:
=2D		printf("carp_looutput: af=3D%d unexpected\n", dst->sa_family);
=2D		m_freem(m);
=2D		return (EAFNOSUPPORT);
=2D	}
=2D#endif
=2D	return(if_simloop(ifp, m, dst->sa_family, 0));
=2D}
=2D
=2D/*
  * Start output on carp interface. This function should never be called.
  */
 static void
@@ -2027,81 +2085,84 @@ carp_start(struct ifnet *ifp)
 #endif
 }
=20
=2Dint
+static int
 carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
     struct rtentry *rt)
 {
=2D	struct m_tag *mtag;
=2D	struct carp_softc *sc;
=2D	struct ifnet *carp_ifp;
+	struct carp_softc *sc =3D ifp->if_softc;
=20
=2D	if (!sa)
=2D		return (0);
+	if (sc->sc_carpdev !=3D NULL && sc->sc_state =3D=3D MASTER)
+		return (sc->sc_carpdev->if_output(ifp, m, sa, rt));
+	else {
+		m_freem(m);
+		return (ENETUNREACH);
+	}
+}
=20
=2D	switch (sa->sa_family) {
=2D#ifdef INET
=2D	case AF_INET:
=2D		break;
=2D#endif /* INET */
=2D#ifdef INET6
=2D	case AF_INET6:
=2D		break;
=2D#endif /* INET6 */
=2D	default:
=2D		return (0);
+struct ifnet *
+carp_ourether(void *v, struct ether_header *eh, u_char iftype, int src)
+{
+	struct carp_if *cif =3D (struct carp_if *)v;
+	struct carp_softc *vh;
+	u_int8_t *ena;
+
+	if (src)
+		ena =3D (u_int8_t *)&eh->ether_shost;
+	else
+		ena =3D (u_int8_t *)&eh->ether_dhost;
+
+	TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
+		if ((vh->sc_ifp->if_flags & (IFF_UP)) !=3D (IFF_UP))
+			continue;
+		if ((vh->sc_state =3D=3D MASTER /* || vh->sc_ifp->if_flags & IFF_LINK0 *=
/)
+		    && !bcmp(ena, IF_LLADDR(vh->sc_ifp), ETHER_ADDR_LEN))
+			return (vh->sc_ifp);
 	}
+	return (NULL);
+}
=20
=2D	mtag =3D m_tag_find(m, PACKET_TAG_CARP, NULL);
=2D	if (mtag =3D=3D NULL)
=2D		return (0);
+int
+carp_input(struct mbuf *m)
+{
+	struct ether_header *eh;
+	struct carp_if *cif =3D (struct carp_if *)m->m_pkthdr.rcvif->if_carp;
+	struct ifnet *ifp;
=20
=2D	bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *));
=2D	sc =3D carp_ifp->if_softc;
=2D
=2D	/* Set the source MAC address to Virtual Router MAC Address */
=2D	switch (ifp->if_type) {
=2D	case IFT_ETHER:
=2D	case IFT_L2VLAN: {
=2D			struct ether_header *eh;
=2D
=2D			eh =3D mtod(m, struct ether_header *);
=2D			eh->ether_shost[0] =3D 0;
=2D			eh->ether_shost[1] =3D 0;
=2D			eh->ether_shost[2] =3D 0x5e;
=2D			eh->ether_shost[3] =3D 0;
=2D			eh->ether_shost[4] =3D 1;
=2D			eh->ether_shost[5] =3D sc->sc_vhid;
=2D		}
=2D		break;
=2D	case IFT_FDDI: {
=2D			struct fddi_header *fh;
=2D
=2D			fh =3D mtod(m, struct fddi_header *);
=2D			fh->fddi_shost[0] =3D 0;
=2D			fh->fddi_shost[1] =3D 0;
=2D			fh->fddi_shost[2] =3D 0x5e;
=2D			fh->fddi_shost[3] =3D 0;
=2D			fh->fddi_shost[4] =3D 1;
=2D			fh->fddi_shost[5] =3D sc->sc_vhid;
=2D		}
=2D		break;
=2D	case IFT_ISO88025: {
=2D 			struct iso88025_header *th;
=2D 			th =3D mtod(m, struct iso88025_header *);
=2D			th->iso88025_shost[0] =3D 3;
=2D			th->iso88025_shost[1] =3D 0;
=2D			th->iso88025_shost[2] =3D 0x40 >> (sc->sc_vhid - 1);
=2D			th->iso88025_shost[3] =3D 0x40000 >> (sc->sc_vhid - 1);
=2D			th->iso88025_shost[4] =3D 0;
=2D			th->iso88025_shost[5] =3D 0;
+	eh =3D mtod(m, struct ether_header *);
+
+	if ((ifp =3D carp_ourether(cif, eh, m->m_pkthdr.rcvif->if_type, 0)))
+		;
+	else 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 =3D m_dup(m, M_DONTWAIT);
+			if (m0 =3D=3D NULL)
+				continue;
+			m0->m_pkthdr.rcvif =3D vh->sc_ifp;
+			ether_input(vh->sc_ifp, m0);
 		}
=2D		break;
=2D	default:
=2D		printf("%s: carp is not supported for this interface type\n",
=2D		    ifp->if_xname);
=2D		return (EOPNOTSUPP);
+		return (1);
 	}
=20
+	if (ifp =3D=3D NULL)
+		return (1);
+
+	m->m_pkthdr.rcvif =3D ifp;
+
+#if 0 /* XXX: BPF */
+	if (ifp->if_bpf)
+		bpf_mtap_hdr(ifp->if_bpf, (char *)&eh, ETHER_HDR_LEN, m,
+		    BPF_DIRECTION_IN);
+#endif
+	ifp->if_ipackets++;
+	ether_input(ifp, m);
+
 	return (0);
 }
=20
@@ -2131,9 +2192,14 @@ carp_set_state(struct carp_softc *sc, int state)
 }
=20
 void
=2Dcarp_carpdev_state(void *v)
+carp_carpdev_state(struct ifnet *ifp)
 {
=2D	struct carp_if *cif =3D v;
+	struct carp_if *cif;
+
+	if (ifp->if_type =3D=3D IFT_CARP || ifp->if_carp =3D=3D NULL)
+		return;
+
+	cif =3D ifp->if_carp;
=20
 	CARP_LOCK(cif);
 	carp_carpdev_state_locked(cif);
diff --git a/sys/netinet/ip_carp.h b/sys/netinet/ip_carp.h
index 1688b01..3525ab9 100644
=2D-- a/sys/netinet/ip_carp.h
+++ b/sys/netinet/ip_carp.h
@@ -117,6 +117,13 @@ struct carpstats {
 	uint64_t	carps_preempt;		/* if enabled, preemptions */
 };
=20
+#define	CARPDEVNAMSIZ	16
+#ifdef IFNAMSIZ
+#if CARPDEVNAMSIZ !=3D IFNAMSIZ
+#error
+#endif
+#endif
+
 /*
  * Configuration structure for SIOCSVH SIOCGVH
  */
@@ -128,6 +135,7 @@ struct carpreq {
 	int		carpr_advskew;
 	int		carpr_advbase;
 	unsigned char	carpr_key[CARP_KEY_LEN];
+	char		carpr_carpdev[CARPDEVNAMSIZ];
 };
 #define	SIOCSVH	_IOWR('i', 245, struct ifreq)
 #define	SIOCGVH	_IOWR('i', 246, struct ifreq)
@@ -152,15 +160,15 @@ struct carpreq {
 }
=20
 #ifdef _KERNEL
=2Dvoid		 carp_carpdev_state(void *);
=2Dvoid		 carp_input (struct mbuf *, int);
=2Dint		 carp6_input (struct mbuf **, int *, int);
=2Dint		 carp_output (struct ifnet *, struct mbuf *, struct sockaddr *,
=2D		     struct rtentry *);
=2Dint		 carp_iamatch (void *, struct in_ifaddr *, struct in_addr *,
+void		 carp_carpdev_state(struct ifnet *);
+void		 carp_proto_input(struct mbuf *, int);
+int		 carp6_proto_input(struct mbuf **, int *, int);
+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 *, const struct in6_addr *);
=2Dstruct	ifnet	*carp_forus (void *, void *);
+struct	ifnet	*carp_forus(void *, void *);
+struct ifnet	*carp_ourether(void *, struct ether_header *, u_char, int);
+int		 carp_input(struct mbuf *);
 #endif
 #endif /* _IP_CARP_H */
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index 2230741..fbd022d 100644
=2D-- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -319,7 +319,7 @@ struct ip6protosw inet6sw[] =3D {
 	.pr_domain =3D		&inet6domain,
 	.pr_protocol =3D		IPPROTO_CARP,
 	.pr_flags =3D		PR_ATOMIC|PR_ADDR,
=2D	.pr_input =3D		carp6_input,
+	.pr_input =3D		carp6_proto_input,
 	.pr_output =3D		rip6_output,
 	.pr_ctloutput =3D		rip6_ctloutput,
 	.pr_usrreqs =3D		&rip6_usrreqs

--Boundary-01=_gdCXHstQkL2YQhf--

--nextPart1795943.NKanXysAve
Content-Type: application/pgp-signature; name=signature.asc 
Content-Description: This is a digitally signed message part.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4 (FreeBSD)

iD8DBQBHXCdlXyyEoT62BG0RAqOOAJ0a/PRe61xOY9a+1Ns8cJP9CO2B8wCfZn9k
c9XkbPcouqlTXaOkc6zFHJM=
=5f0T
-----END PGP SIGNATURE-----

--nextPart1795943.NKanXysAve--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712091835.33608.max>